[Feature] Security hardening: nine items below the blocking layer #516
Labels
No labels
accepted
bug
declined
docs
duplicate
enhancement
good first issue
in-progress
needs-info
privacy
security
triage
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
LibreKAT/Ocideck#516
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Nine security items from the release review that are hardening rather than open holes. The blocking ones are fixed; these are the layer underneath.
audienceBoundaryonly checks three hard-coded files (tool/check_conventions.dart), while SECURITY_DESIGN §8 claims a general compile-time guarantee. A fourth export surface would not be seen. Make the gate generic.sanitize_svg.dartis a deny-list.<style>contents,<set attributeName="on…">and semicolon lists invalues=slip through. Harmless today (onlyflutter_svgreads it), but a deny-list is the wrong shape for this. Make it an allow-list.file_service_open.dart:86), sidecars (file_service.dart:669,682), the nested CVE zip (cve_bulk_ingest.dart:223).connect-src 'self' https:inweb/index.htmlpermits a no-cors POST to any host.contains('youtube.com')rather than on host (media_previews_video.dart).DocumentSignatureis an unkeyed hash over a name — consider real signing (Ed25519/PKI) alongside it.password_strength.dartreasons as if the KDF were stronger. The claim is literally true and the KDF is weak.network_sink_guard_testdoes not coverProcess.startorWebViewController.Each is small on its own. Weigh them individually rather than as one batch: some are worth a gate, some are worth a line of code, and at least one (the KDF) is a compatibility decision.
Three of the nine are done, in the order the maintainer's rule now requires — security before bugs before features.
Done
ImageSidecarStorewere unbounded. The write path was the sharper one: without a cap it carried on with an empty map and wrote that over the file, wiping every other caption in the folder. 1 MiB cap, mutation-checked.contains. Not a gap in knowledge — the reasoning was written down and applied to one of the two branches. Neither had a test.connect-src(#546). No directive changed; the reasoning did. The comment credited CORS with protecting this, which is true for reads and false for ano-corsPOST — that request is sent, only the response is opaque. The actual control isscript-src 'self'without the dynamic-code escapes. A security comment that credits the wrong mechanism is worse than none: the next reader believes it is covered.Left, with what I learned about two of them
audienceBoundarygeneric. Measured before attempting: 208 rawDeck/List<Slide>parameters across 67 files outsideprivacy/. So the obvious inversion — baseline every Deck-taking function — produces noise, not signal. A workable rule has to key on output: a function that takes a raw deck and reaches an output primitive. That is a design, not a quick fix.assurance/ASVS-5.0.0-afwijkingen.md, with the reasoning that the number is fixed by the WinZip-AES specification and not raisable from OciDeck. If it is resolved it should be resolved there, by leaving WinZip AES — not re-decided in a hardening pass.sanitize_svgdeny-list → allow-list; the remaining unbounded reads (chart data, the nested CVE zip);DocumentSignatureas an unkeyed hash; CMS/chain verification of the RFC 3161 token;network_sink_guard_testcoverage forProcess.startandWebViewController.Relabelled
bug— several of these are defects rather than improvements, and under the new ordering that matters for when they get picked up.Eight of the nine are done; the ninth is split off.
Done this round (#560, #561, #562)
network_sink_guard_testforProcess.startandWebViewController. The reasoning was already written down atdisk_traces.dart("a second subprocess here must raise the alarm") — but semgrep is not inmake check, so that alarm existed nowhere.sanitize_svgis an allow-list. The objection recorded in that file was real, and it dissolved once the list stopped being guesswork:flutter_svgis the only reader this SVG ever gets, and its parser names its own vocabulary. The list is never stricter than the reader. It matters that this was checked —x1/y1/x2/y2were missing from the first draft, which turned every<line>into a point.audienceBoundarygeneric. The measurement in the previous comment held: 208 raw Deck parameters is noise. Keying on output gives thirteen. The gate now finds them itself and refuses to pass until each is classified — audience surface, or deliberately source-faithful. It does not decide which, because a.ocideckpackage must carry the source verbatim and a PDF must not, and no static rule derives that difference. That surfaced twelve output channels nobody had ever classified. None of them a leak; twelve places where the next builder had to guess.DocumentSignature. Decided: no Ed25519/PKI alongside it. The reason is sovereignty, not cryptography — a checkable signature needs a trust anchor, and that anchor lands in the critical path. Recorded in SECURITY_DESIGN §12 with the conditions under which it would be revisited.PBKDF2-SHA1 stands as recorded in the previous comment: it is a deviation in
assurance/ASVS-5.0.0-afwijkingen.md, fixed by the WinZip-AES specification, and if it is resolved it is resolved there by leaving that format — not re-decided in a hardening pass.Split off: the RFC 3161 nonce echo (#563). The CMS and chain verification that the same item asked about stays unbuilt, with the reasoning that is already recorded and still holds.
One consequence of this round is filed separately: a skipped sidecar is currently invisible to the user (#564).
All nine are now resolved: seven built, one decided against (
DocumentSignature), one split off (#563). Closing.