fix: add input sanitization across date plan forms and repository

This commit is contained in:
null 2026-06-17 19:23:53 -05:00
parent b049024ba9
commit 0095151bd9
3 changed files with 41 additions and 12 deletions

View File

@ -77,18 +77,16 @@ class DatePlanRepositoryImpl @Inject constructor(
}
override suspend fun savePlan(plan: DatePlan) {
// Store in Room for local-first access
val entity = plan.toEntity()
if (plan.id.isEmpty()) {
// Create new plan - let Firestore generate the ID
val newId = planDataSource.createPlan(plan.coupleId, plan)
val sanitized = plan.sanitized()
val entity = sanitized.toEntity()
if (sanitized.id.isEmpty()) {
val newId = planDataSource.createPlan(sanitized.coupleId, sanitized)
val updatedEntity = entity.copy(id = newId)
planDao.insert(updatedEntity)
} else {
// Update existing plan
planDao.insert(entity)
planDataSource.updatePlan(plan.coupleId, plan)
planDataSource.updatePlan(sanitized.coupleId, sanitized)
}
}
@ -135,8 +133,29 @@ class DatePlanRepositoryImpl @Inject constructor(
)
}
// ─── Sanitization ────────────────────────────────────────────────────────
private fun DatePlan.sanitized(): DatePlan = copy(
scheduledTime = scheduledTime.take(MAX_TIME_LENGTH),
duration = duration.take(MAX_DURATION_LENGTH),
activity = activity.take(MAX_TEXT_LENGTH),
food = food.take(MAX_TEXT_LENGTH),
conversationPrompts = conversationPrompts
.take(MAX_PROMPTS)
.map { it.take(MAX_PROMPT_LENGTH) },
optionalChallenge = optionalChallenge?.take(MAX_TEXT_LENGTH)
)
// ─── Mappers ─────────────────────────────────────────────────────────────
private companion object {
const val MAX_TIME_LENGTH = 20
const val MAX_DURATION_LENGTH = 50
const val MAX_TEXT_LENGTH = 500
const val MAX_PROMPT_LENGTH = 1_000
const val MAX_PROMPTS = 20
}
private fun DatePlanPreferenceEntity.toDomain(): DatePlanPreference = DatePlanPreference(
id = id,
coupleId = coupleId,

View File

@ -45,8 +45,8 @@ class BucketListViewModel @Inject constructor(
val newItem = BucketListItem(
coupleId = coupleId,
title = title,
description = description,
title = title.take(MAX_TITLE_LENGTH),
description = description.take(MAX_DESCRIPTION_LENGTH),
category = category,
addedBy = "currentUser",
addedAt = System.currentTimeMillis()
@ -100,6 +100,11 @@ class BucketListViewModel @Inject constructor(
fun clear() {
_uiState.update { BucketListUiState() }
}
private companion object {
const val MAX_TITLE_LENGTH = 100
const val MAX_DESCRIPTION_LENGTH = 500
}
}
data class BucketListUiState(

View File

@ -47,9 +47,9 @@ class DateBuilderViewModel @Inject constructor(
val preference = DatePlanPreference(
dateIdeaId = state.dateIdeaId,
preferredDate = state.scheduledDate,
preferredTime = state.scheduledTime,
preferredTime = state.scheduledTime.take(MAX_TIME_LENGTH),
budget = state.budget,
duration = state.duration
duration = state.duration.take(MAX_DURATION_LENGTH)
)
viewModelScope.launch {
@ -60,6 +60,11 @@ class DateBuilderViewModel @Inject constructor(
fun clear() {
_uiState.update { DateBuilderUiState() }
}
private companion object {
const val MAX_TIME_LENGTH = 20
const val MAX_DURATION_LENGTH = 50
}
}
data class DateBuilderUiState(