78 lines
4.9 KiB
Markdown
78 lines
4.9 KiB
Markdown
# Security checklist — Privacy: Period Tracker
|
|
|
|
```
|
|
Status: Current
|
|
Owner: _null
|
|
Last reviewed: 2026-08-18
|
|
Governs: the checks run before a Play release, and what each one proves
|
|
Review trigger: A new SDK; a new class of input; a change to what is logged,
|
|
exported or backed up; any finding that got past this list
|
|
```
|
|
|
|
## Why this is separate from SECURITY.md
|
|
|
|
[`SECURITY.md`](SECURITY.md) is the threat model: what is protected and from
|
|
whom. It is read carefully once and revisited rarely.
|
|
|
|
This is the list somebody actually works through before pressing publish.
|
|
Keeping them apart means the model can stay stable while the checks change, and
|
|
it means the release list stays short enough to finish rather than skim.
|
|
|
|
## Before a release
|
|
|
|
- [ ] `bash scripts/secrets.sh --tracked` is clean — proves no credential shape is in the tracked tree
|
|
- [ ] `git log -p` searched for keystore, `.jks`, service-account JSON and `local.properties` — proves nothing secret is in the **history**, which a directory listing cannot tell you
|
|
- [ ] The release APK/AAB is signed by the upload key from outside the repository — proves signing config never needed a tracked secret
|
|
- [ ] `./gradlew test` passes with the prediction acceptance tests included — proves the forecast still behaves at the boundaries §51 names
|
|
- [ ] Play **Data Safety** declaration re-read against what the app actually sends — proves the declaration is a description rather than an aspiration
|
|
- [ ] Play **Health apps** declaration completed as applicable — proves the category rules were checked, not assumed
|
|
- [ ] Store listing carries no medical or contraceptive claim — proves the copy did not drift past what the app can support
|
|
- [ ] Current Play target-API requirement confirmed **at submission time**, not from this document — proves the deadline was checked rather than remembered
|
|
|
|
## Standing checks
|
|
|
|
- [ ] No secret in the repository, in a log line, or in an error message
|
|
- [ ] Every input that reaches a query or a filesystem path is validated at the boundary
|
|
- [ ] Dependencies reviewed, and any accepted advisory recorded with a reason
|
|
- [ ] Every third-party SDK in the build is in [`SECURITY.md`](SECURITY.md)'s third-parties table — proves a dependency did not arrive without a decision
|
|
|
|
### Health data never reaches advertising
|
|
|
|
The one group that is not generic. Every item proves part of
|
|
[`../planning/PRODUCT_PLAN.md` §34](../planning/PRODUCT_PLAN.md).
|
|
|
|
- [ ] the ads module declares no Gradle dependency on core/database or `domain/*`, and the boundary guard was **proved to fail** this release — proves the check is evidence rather than decoration
|
|
- [ ] No health-derived value appears in an ad request extra, user property, or callback log — proves targeting cannot happen by accident
|
|
- [ ] Ad requests are non-personalized/contextual, with region-appropriate consent — proves the default is the private one
|
|
- [ ] The ads provider is not initialised for entitled (ad-free) users where practical — proves the purchase removes the SDK, not just the view
|
|
- [ ] The built AAB inspected for the ad SDK's declared collection — proves the scan reached the artifact users receive, not only the source
|
|
|
|
### What the device and the lock screen expose
|
|
|
|
- [ ] Cycle history is in app-private storage only — proves nothing landed in shared or external storage
|
|
- [ ] Lock-screen text in **Discreet** and **Maximum privacy** modes contains no menstrual detail, checked on a real lock screen at every mode — proves the notification privacy feature actually works, which is the breach most likely to happen
|
|
- [ ] The health database's inclusion in platform auto-backup is a **recorded decision**, not a default — proves the local-first promise is not undone by the OS
|
|
- [ ] Release build has verbose logging off and no raw cycle date in any crash payload — proves §45's logging rule survived the build type
|
|
- [ ] Delete My Data removes the rows and is irreversible after confirmation — proves the promise in Settings is true
|
|
- [ ] Export produces only the user's own data, to a location they chose — proves export is not an accidental leak path
|
|
|
|
### The compliance bar, which is not the launch bar
|
|
|
|
- [ ] The privacy policy is published, reachable from the app, and matches what the app does
|
|
- [ ] The privacy promise appears in onboarding, in Settings → Privacy & Security, and on the public privacy page — proves the promise is where §4 says it must be
|
|
|
|
*(Not applicable here, and deleted rather than carried as permanently skipped:
|
|
authorisation and IDOR checks — there are no accounts and no server; browser
|
|
bundle and cookie checks — there is no browser; rate limiting and spend
|
|
ceilings — there is no endpoint of ours to exhaust.)*
|
|
|
|
## What got past this list
|
|
|
|
Add an entry whenever a real finding was not caught here, and then add the check
|
|
that would have caught it. A checklist that never grows is one nobody is honest
|
|
with.
|
|
|
|
| When | What was missed | The check now added |
|
|
| --- | --- | --- |
|
|
| — | — | — |
|