Privacy-Period-Tracker/core/export/build.gradle.kts

23 lines
740 B
Plaintext
Raw Normal View History

feat: export my data, as one plaintext file the user places "Your cycle belongs to you" was a promise the app could not keep: there was no way to get the data out. ## The format, because it outlives the batch One pretty-printed JSON file. The issue asks for "human-readable" and means it — this is what somebody's archive will be in for years, so it is a contract with eight rules written down beside it, not an implementation detail. Pinned byte-for-byte against a committed golden file, which doubles as the documented example so SECURITY.md links at it rather than keeping a second copy that would drift. A reformat, a reordered key or a changed date rendering all fail in a test rather than in an archive. Dates are ISO calendar dates with no timezone and no conversion, ever. Converters.kt stores a LocalDate as its epoch day precisely so it "cannot carry a timezone by accident", and a zone-aware formatter here would shift every date for users east or west of whoever wrote it — a cycle tracker off by one day is wrong in the way that matters. There is a test that renders the same fixture in UTC, +14 and -12 and requires identical bytes, because that bug would never fire where it was written. ## Plaintext, and that is the decision rather than the default An earlier note said this would be encrypted. It should not be, and SECURITY.md now says why: the export is the copy that makes a lost Keystore key survivable instead of final — the exact condition recorded for ever revisiting database encryption. Putting it behind a passphrase reproduces the failure that decision was taken to avoid: a forgotten secret and an archive nobody, including this app, can open. §45's "prefer encrypted backup/export formats" is scoped to backup, which this is not. ## Only the user's own data, as a compile error :core:export is pure JVM and depends on :domain:cycle alone. Prediction, PredictionAccuracy, FertilityEstimate and CycleRecord live in :domain:prediction and are simply not on its classpath, and kotlin("jvm") keeps android.os.Build off it too — so a forecast or a device fact cannot be added by accident. The key set is asserted with assertEquals rather than contains, so a new field is a failing test rather than a silent addition. Row ids are out because they are monotonic and would disclose how many records the user DELETED. The Play entitlement is out because a purchase one file-edit away from being granted is a purchase that will be. ## No second copy, ever The Storage Access Framework writes straight into the document the user picked. The alternative — write to cacheDir, share by FileProvider, delete after — creates the temporary file the issue warns about and races the receiving app still reading it. A test walks cacheDir after a successful export and requires it empty; it fails the moment anybody reintroduces that pattern. The destination is parked until the session is unlocked. Returning from the picker can re-lock, and writing while locked would hand the whole history to whoever took the phone during the save dialog. ## Two new guards, both proved to fail checkNoSharedStorageWrites: §45's shared-storage ban was enforced by nobody having typed it. checkPermissions structurally cannot see it — it matches <uses-permission>, and a <provider> declaring FileProvider merges green. checkNoHealthLogging gains a completeness check. A module missing from modulesSeeingHealthData was silently exempt with a green build, which app/proguard-rules.pro has described as a hazard since before :core:security and :core:export existed. Every module must now be in that list or in an explicit modulesWithNoHealthData with its reason; being in neither is a violation rather than an exemption. 261 JVM tests, none skipped. Five guards green. closes #35 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 22:13:07 -05:00
plugins {
alias(libs.plugins.kotlin.jvm)
}
// Pure JVM, and that is the point rather than a convenience.
//
// The issue's hardest requirement is "only the user's own data — no derived
// analytics, no diagnostic payload, nothing about the device". Depending on
// `:domain:cycle` alone makes that a COMPILE ERROR instead of a review comment:
// `Prediction`, `PredictionAccuracy`, `FertilityEstimate` and `CycleRecord` live
// in `:domain:prediction` and are simply not on this classpath, and being
// kotlin("jvm") puts `android.os.Build` off it too, so a device fact cannot be
// added by accident.
kotlin {
jvmToolchain(21)
}
dependencies {
implementation(project(":domain:cycle"))
testImplementation(libs.junit)
}