@dailephd/my-frontend-observer 0.9.1 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/CHANGELOG.md +490 -471
  2. package/LICENSE +21 -21
  3. package/README.md +375 -357
  4. package/dist/application/projectCheckService.d.ts +6 -0
  5. package/dist/application/projectCheckService.js +8 -1
  6. package/dist/application/projectCheckService.js.map +1 -1
  7. package/dist/application/projectWorkflowService.d.ts +7 -2
  8. package/dist/application/projectWorkflowService.js +10 -3
  9. package/dist/application/projectWorkflowService.js.map +1 -1
  10. package/dist/application/visualChangeAgentHandoffService.d.ts +28 -0
  11. package/dist/application/visualChangeAgentHandoffService.js +111 -0
  12. package/dist/application/visualChangeAgentHandoffService.js.map +1 -0
  13. package/dist/application/visualChangeProjectWorkflowService.d.ts +95 -0
  14. package/dist/application/visualChangeProjectWorkflowService.js +376 -0
  15. package/dist/application/visualChangeProjectWorkflowService.js.map +1 -0
  16. package/dist/application/visualChangeReviewService.d.ts +50 -0
  17. package/dist/application/visualChangeReviewService.js +69 -0
  18. package/dist/application/visualChangeReviewService.js.map +1 -0
  19. package/dist/application/visualChangeWorkflowPersistenceService.d.ts +26 -0
  20. package/dist/application/visualChangeWorkflowPersistenceService.js +15 -0
  21. package/dist/application/visualChangeWorkflowPersistenceService.js.map +1 -0
  22. package/dist/artifacts/visualChangeWorkflowArtifactReader.d.ts +9 -0
  23. package/dist/artifacts/visualChangeWorkflowArtifactReader.js +47 -0
  24. package/dist/artifacts/visualChangeWorkflowArtifactReader.js.map +1 -0
  25. package/dist/artifacts/visualChangeWorkflowArtifactWriter.d.ts +20 -0
  26. package/dist/artifacts/visualChangeWorkflowArtifactWriter.js +41 -0
  27. package/dist/artifacts/visualChangeWorkflowArtifactWriter.js.map +1 -0
  28. package/dist/cli.js +9 -7
  29. package/dist/cli.js.map +1 -1
  30. package/dist/domain/visualChangeAgentHandoff.d.ts +82 -0
  31. package/dist/domain/visualChangeAgentHandoff.js +80 -0
  32. package/dist/domain/visualChangeAgentHandoff.js.map +1 -0
  33. package/dist/domain/visualChangeAgentHandoffSerialization.d.ts +2 -0
  34. package/dist/domain/visualChangeAgentHandoffSerialization.js +11 -0
  35. package/dist/domain/visualChangeAgentHandoffSerialization.js.map +1 -0
  36. package/dist/domain/visualChangeCycle.d.ts +8 -0
  37. package/dist/domain/visualChangeCycle.js +7 -0
  38. package/dist/domain/visualChangeCycle.js.map +1 -0
  39. package/dist/domain/visualChangeWorkflow.d.ts +125 -0
  40. package/dist/domain/visualChangeWorkflow.js +109 -0
  41. package/dist/domain/visualChangeWorkflow.js.map +1 -0
  42. package/dist/domain/visualChangeWorkflowIdentity.d.ts +5 -0
  43. package/dist/domain/visualChangeWorkflowIdentity.js +24 -0
  44. package/dist/domain/visualChangeWorkflowIdentity.js.map +1 -0
  45. package/dist/index.d.ts +21 -1
  46. package/dist/index.js +12 -1
  47. package/dist/index.js.map +1 -1
  48. package/dist/projectWorkflow/projectPaths.d.ts +3 -0
  49. package/dist/projectWorkflow/projectPaths.js +7 -0
  50. package/dist/projectWorkflow/projectPaths.js.map +1 -1
  51. package/dist/viewer/assets/index-DglJ6f28.css +1 -0
  52. package/dist/viewer/assets/index-DsODREY5.js +9 -0
  53. package/dist/viewer/index.html +15 -15
  54. package/dist/viewer/sw.js +1 -1
  55. package/dist/viewerServer/evidence/classify.d.ts +3 -1
  56. package/dist/viewerServer/evidence/classify.js +10 -0
  57. package/dist/viewerServer/evidence/classify.js.map +1 -1
  58. package/dist/viewerServer/evidence/handles.js +1 -0
  59. package/dist/viewerServer/evidence/handles.js.map +1 -1
  60. package/dist/viewerServer/evidence/projection.d.ts +5 -0
  61. package/dist/viewerServer/evidence/projection.js +19 -0
  62. package/dist/viewerServer/evidence/projection.js.map +1 -1
  63. package/dist/viewerServer/evidence/visualChangeWorkflowView.d.ts +31 -0
  64. package/dist/viewerServer/evidence/visualChangeWorkflowView.js +36 -0
  65. package/dist/viewerServer/evidence/visualChangeWorkflowView.js.map +1 -0
  66. package/dist/viewerServer/httpServer.js +323 -1
  67. package/dist/viewerServer/httpServer.js.map +1 -1
  68. package/dist/viewerServer/referenceApproval.d.ts +22 -0
  69. package/dist/viewerServer/referenceApproval.js +42 -0
  70. package/dist/viewerServer/referenceApproval.js.map +1 -0
  71. package/dist/viewerServer/referenceVisualChangeAuthoring.d.ts +28 -0
  72. package/dist/viewerServer/referenceVisualChangeAuthoring.js +134 -0
  73. package/dist/viewerServer/referenceVisualChangeAuthoring.js.map +1 -0
  74. package/dist/viewerServer/runtimeVisualChangeAuthoring.d.ts +33 -0
  75. package/dist/viewerServer/runtimeVisualChangeAuthoring.js +81 -0
  76. package/dist/viewerServer/runtimeVisualChangeAuthoring.js.map +1 -0
  77. package/dist/viewerServer/visualChangeAuthoring.d.ts +46 -0
  78. package/dist/viewerServer/visualChangeAuthoring.js +63 -0
  79. package/dist/viewerServer/visualChangeAuthoring.js.map +1 -0
  80. package/dist/viewerServer/visualChangeHandoff.d.ts +23 -0
  81. package/dist/viewerServer/visualChangeHandoff.js +31 -0
  82. package/dist/viewerServer/visualChangeHandoff.js.map +1 -0
  83. package/dist/viewerServer/visualChangeReview.d.ts +30 -0
  84. package/dist/viewerServer/visualChangeReview.js +46 -0
  85. package/dist/viewerServer/visualChangeReview.js.map +1 -0
  86. package/docs/ARCHITECTURE.md +1394 -1373
  87. package/docs/CI_CD.md +349 -327
  88. package/docs/COMMANDS.md +1035 -1012
  89. package/docs/CONTRACTS.md +1971 -1926
  90. package/docs/CURRENT_STATE.md +1277 -1238
  91. package/docs/DEVELOPMENT.md +240 -237
  92. package/docs/DOCUMENTATION_PRESERVATION_POLICY.md +50 -50
  93. package/docs/PROJECT_DESCRIPTION.md +2248 -2224
  94. package/docs/PROJECT_MILESTONES.md +2681 -2558
  95. package/docs/PROJECT_OVERVIEW.md +200 -191
  96. package/docs/QUICKSTART.md +100 -96
  97. package/docs/RELEASE.md +37 -33
  98. package/docs/ROADMAP.md +1105 -1033
  99. package/docs/SECURITY.md +297 -275
  100. package/docs/WORKFLOWS.md +806 -770
  101. package/docs/plans/v0.10-implementation-plan.md +1509 -0
  102. package/docs/plans/v0.8-implementation-plan.md +655 -655
  103. package/docs/plans/v0.8.1-cli-usability-patch-plan.md +505 -505
  104. package/docs/plans/v0.9-implementation-plan.md +1529 -1529
  105. package/docs/plans/v0.9.1-implementation-plan.md +468 -468
  106. package/docs/reports/v0.10-batch1-visual-change-workflow-foundation.md +102 -0
  107. package/docs/reports/v0.10-batch2-project-composition-check-recording.md +103 -0
  108. package/docs/reports/v0.10-batch3-viewer-visual-change-workspace.md +93 -0
  109. package/docs/reports/v0.10-batch4-actual-frontend-entry.md +59 -0
  110. package/docs/reports/v0.10-batch5-reference-driven-entry.md +238 -0
  111. package/docs/reports/v0.10-batch6-coding-agent-handoff.md +85 -0
  112. package/docs/reports/v0.10-batch7-correction-review-acceptance.md +145 -0
  113. package/docs/reports/v0.10-batch8-integrated-acceptance.md +109 -0
  114. package/docs/reports/v0.10-implementation-completeness-documentation-reconciliation.md +344 -0
  115. package/docs/reports/v0.10-pre-release-readiness.md +120 -0
  116. package/docs/reports/v0.10-release-preparation.md +70 -0
  117. package/docs/reports/v0.10.1-project-check-baseline-context-implementation.md +86 -0
  118. package/docs/reports/v0.7-bounded-fidelity-context-prompt7.md +243 -243
  119. package/docs/reports/v0.7-implementation-completeness-documentation-reconciliation.md +497 -497
  120. package/docs/reports/v0.7-pre-release-readiness.md +337 -337
  121. package/docs/reports/v0.7-reference-binding-prompt5.md +223 -223
  122. package/docs/reports/v0.7-reference-compatibility-prompt4.md +234 -234
  123. package/docs/reports/v0.7-reference-correction-workflow-prompt8.md +222 -222
  124. package/docs/reports/v0.7-reference-fidelity-prompt6.md +216 -216
  125. package/docs/reports/v0.7-reference-foundation-prompt1.md +151 -151
  126. package/docs/reports/v0.7-reference-regions-prompt2.md +195 -195
  127. package/docs/reports/v0.7-reference-requirements-prompt3.md +217 -217
  128. package/docs/reports/v0.7-release-prep.md +423 -423
  129. package/docs/reports/v0.8-binding-fidelity-interaction-batch6.md +279 -279
  130. package/docs/reports/v0.8-bounded-context-correlation-batch7.md +233 -233
  131. package/docs/reports/v0.8-comparison-contract-inspection-batch4.md +279 -279
  132. package/docs/reports/v0.8-evidence-index-readers-batch2.md +247 -247
  133. package/docs/reports/v0.8-implementation-completeness-documentation-reconciliation.md +741 -741
  134. package/docs/reports/v0.8-integrated-viewer-acceptance-batch8.md +128 -128
  135. package/docs/reports/v0.8-observation-svg-inspection-batch3.md +223 -223
  136. package/docs/reports/v0.8-prerelease-readiness-cross-platform-security-code-rot.md +687 -687
  137. package/docs/reports/v0.8-reference-candidate-inspection-batch5.md +232 -232
  138. package/docs/reports/v0.8-viewer-runtime-pwa-batch1.md +278 -278
  139. package/docs/reports/v0.8.1-implementation-completeness-documentation-reconciliation.md +114 -114
  140. package/docs/reports/v0.8.1-prerelease-readiness-cross-platform-security-code-rot.md +170 -170
  141. package/docs/reports/v0.9-architecture-retrieval.md +14 -37
  142. package/docs/reports/v0.9-final-pre-release-readiness.md +209 -209
  143. package/docs/reports/v0.9-final-readiness-corrections.md +530 -530
  144. package/docs/reports/v0.9-pre-release-readiness.md +169 -169
  145. package/docs/reports/v0.9.1-batch1-pwa-hard-gate-isolation.md +359 -359
  146. package/docs/reports/v0.9.1-batch2-hard-gate-validation-integration.md +262 -262
  147. package/docs/reports/v0.9.1-pre-release-readiness.md +206 -206
  148. package/package.json +59 -59
  149. package/dist/viewer/assets/index-BN41MI7m.css +0 -1
  150. package/dist/viewer/assets/index-CkKXnlrI.js +0 -9
package/docs/SECURITY.md CHANGED
@@ -1,275 +1,297 @@
1
- # Security
2
-
3
- Project acceptance paths are portable, project-relative, and realpath-contained before use, including symlink/junction escape rejection. `current` alias replacement changes only catalog metadata and never deletes or overwrites canonical evidence. `check` adds no generic file-serving route, inferred binding, source mutation, persisted check artifact, or image bytes to JSON; configured references must be explicitly approved. JSON artifact paths are project-relative.
4
-
5
- ## Current controls
6
-
7
- `my-frontend-observer` launches a real, sandboxed Chromium browser
8
- (`src/browser/chromiumAdapter.ts`) and enforces a conservative, local-first,
9
- credential-free, non-destructive browser/network boundary
10
- (`src/safety/policy.ts`) as actual product behavior, covered by real-Chromium
11
- tests:
12
-
13
- - allowed schemes are `http`/`https` only;
14
- - allowed hosts are loopback only (`localhost`, `127.0.0.1`, `::1`, and any
15
- `127.x.x.x` form) - no DNS resolution, no arbitrary "local dev host";
16
- - credential-bearing URLs (`user:pass@host`) are rejected;
17
- - the initial target, every navigation redirect, and every subresource
18
- request are independently classified against the same loopback policy and
19
- blocked before being contacted if unsafe;
20
- - popups and downloads are never followed/saved (reported as non-fatal
21
- diagnostics);
22
- - navigation and readiness are bounded by explicit, request-configured
23
- timeouts - no unbounded wait;
24
- - the Chromium browser/context/page are reliably closed on every exit path
25
- (success, safety rejection, navigation/readiness failure, or an
26
- unexpected internal error);
27
- - the observed target's own content/source is never modified by observation.
28
-
29
- ## Comparison (`compare`, shipped as part of the published `0.4.0` package)
30
-
31
- `my-frontend-observer compare` introduces no new network or browser
32
- surface: it never
33
- launches Chromium, never navigates, and never re-observes a target - it only
34
- reads two local, already-persisted observation-artifact `manifest.json`
35
- files (`src/artifacts/artifactReader.ts`) through the same structural
36
- validator the observation writer uses, computes a pure in-memory
37
- comparison, and writes one local comparison `manifest.json`
38
- (`src/artifacts/comparisonArtifactWriter.ts`). Manifest content is parsed
39
- as JSON only and is never executed (no `eval`, no dynamic code loading from
40
- a manifest).
41
-
42
- ## Frontend contracts (`approve-baseline`/`save-change-contract`/`evaluate-contract`, shipped as part of the published `0.5.0` package)
43
-
44
- These three commands introduce no new browser or network surface:
45
- `src/application/frontendContractPersistenceService.ts` and
46
- `src/application/frontendContractEvaluationService.ts` import nothing from
47
- `src/browser/` and never launch Chromium. `approve-baseline` and
48
- `save-change-contract` validate and persist a local JSON contract file
49
- (parsed as JSON only, never executed); `evaluate-contract` reads
50
- already-persisted local observation/comparison/contract artifacts and runs
51
- the pure `evaluateFrontendContract` function. None of the three navigates,
52
- re-observes a target, or contacts a network resource.
53
-
54
- ## Bounded agent context and correlation (v0.6, released as `0.6.0`)
55
-
56
- `src/domain/boundedAgentContextProjection.ts` and
57
- `src/domain/boundedAgentContextCorrelation.ts` introduce no new browser or
58
- network surface: neither imports anything from `src/browser/`, neither
59
- launches Chromium or navigates, and neither performs filesystem or network
60
- I/O of its own. Both are pure, in-memory functions over already-captured
61
- observation/comparison/contract evidence plus caller-supplied candidate
62
- static-evidence records - the correlation module never reads a file path or
63
- retrieves anything itself; the caller (outside this repository) is
64
- responsible for however it obtained the candidate evidence it passes in.
65
- Neither module embeds source code snippets, redacts anything, or invokes
66
- another ecosystem tool (`@dailephd/my-dev-kit` is not a dependency of either
67
- module). Bounded runtime projections may include existing screenshot *path
68
- references* (never embedded bytes), consistent with every other artifact
69
- family's existing reference-not-embed discipline.
70
-
71
- ## External visual-reference security and privacy boundary (v0.7, released as `0.7.0`)
72
-
73
- External visual-reference support (`import-reference`/`approve-reference`/
74
- `evaluate-reference-fidelity`, plus the programmatic correction-workflow
75
- coordinator) is released as part of the published `0.7.0` package. Imported reference images remain
76
- local-first evidence: `import-reference` reads a local file path only,
77
- never a URL, and no code path in this repository uploads a reference image,
78
- a candidate screenshot, source code, or any derived evidence to an external
79
- service. Reference/candidate compatibility and fidelity evaluation are pure,
80
- in-process computations over already-loaded artifacts - neither launches a
81
- network request.
82
-
83
- Bounded local-file safety for reference images is a frozen, source-verified
84
- policy, not an open decision: `EXTERNAL_REFERENCE_SUPPORTED_IMAGE_FORMATS`
85
- (`png`/`jpeg`/`webp`, detected from header/magic bytes only, never from a
86
- caller-declared file extension), `EXTERNAL_REFERENCE_MAX_IMAGE_BYTES`
87
- (20,000,000 bytes), and `[EXTERNAL_REFERENCE_MIN_DIMENSION_PX,
88
- EXTERNAL_REFERENCE_MAX_DIMENSION_PX]` (`[1, 8192]` pixels per side, parsed
89
- from the same bounded header bytes, never a full pixel decode) are all
90
- enforced before any artifact is persisted; an unsupported/undetectable
91
- format, an over-limit file, or invalid/out-of-bound dimensions is rejected
92
- outright (`unsupported-image-format`/`invalid-image-dimensions`/
93
- `image-too-large`), never silently clamped or accepted.
94
-
95
- Reference images are treated as untrusted data, not executable content: the
96
- format/dimension detector never evaluates embedded code, never dynamically
97
- loads a script, and never treats image metadata as an instruction source -
98
- only bounded header-byte inspection is performed, no general-purpose image
99
- or video codec is invoked. Explicit, caller-supplied state
100
- (`--state-file`/`--applicability-file`, `theme`/`applicationState`/
101
- `authenticatedState`) is a closed, bounded label vocabulary with no field
102
- capable of holding a credential, token, cookie, session id, or authorization
103
- header - `authenticatedState` accepts only the literal values
104
- `'authenticated'`/`'unauthenticated'`.
105
-
106
- Reference artifacts and the bounded correction handoff preserve path privacy
107
- and boundedness: every reference/observation/binding/fidelity/context
108
- identity function is a pure hash of semantic content only - an operational
109
- file path (the image path, a `--regions-file`/`--requirements-file`/
110
- `--applicability-file`/`--state-file`/`--bindings-file` path, or an artifact
111
- root directory) is never included in any logical identity, and importing
112
- the same semantic reference content from two different filesystem locations
113
- produces the same `referenceRequestId`. The bounded coding-agent handoff
114
- (`ReferenceCorrectionHandoff`) never embeds raw reference image bytes, a
115
- full `ObservationArtifact`, or a source excerpt - only stable identifiers,
116
- bounded fidelity mismatch records, and evidence path *references*.
117
-
118
- Import never silently promotes an image to an approved reference: only the
119
- explicit `approve-reference` command (or `approveExternalReference`
120
- programmatically) transitions a reference out of the `'imported'` lifecycle
121
- state, and the v0.7 correction workflow's `prepareReferenceCorrection`/
122
- `reviewReferenceCorrectionAttempt` both fail closed if the supplied
123
- reference is not already approved. Neither function - nor anything either
124
- calls - ever invokes `approveExternalReference` or
125
- `approveAndPersistBaseline` itself; a `'pass'` review result is reported as
126
- `approvalEligible: true`, a plain flag, never an automatic approval action.
127
-
128
- Reference/candidate comparison and fidelity evaluation do not broaden the
129
- existing browser/network boundary: candidate rendering continues through the
130
- existing loopback-only Chromium observation path unchanged, and reference
131
- evaluation itself never launches a browser at all (it consumes only
132
- already-captured `ObservationArtifact` evidence). The v0.7 correction
133
- workflow never edits target source: `src/domain/referenceCorrectionWorkflow.ts`
134
- and everything it imports contain no filesystem-write call, no
135
- `child_process` invocation, and no patch-application mechanism - the actual
136
- source edit between review attempts is always the responsibility of an
137
- external implementation actor (a human or a coding agent), never this
138
- package's own product code. No remote AI/model-provider dependency was
139
- introduced anywhere in v0.7.
140
-
141
- ## Interactive local viewer (v0.8, released as `0.8.0`)
142
-
143
- `my-frontend-observer view` (`src/viewerServer/`, `viewer/`) is implemented,
144
- tested, and formally security-reviewed. The properties below were verified
145
- locally/manually during implementation (Batches 1-8) and then re-verified
146
- through the formal pre-release security audit - see
147
- `docs/reports/v0.8-prerelease-readiness-cross-platform-security-code-rot.md`,
148
- which also found and fixed one real finding: a media filename that is
149
- itself a symlink/junction pointing outside the evidence root could
150
- previously have had its linked-to file's content served. `checkExists()`
151
- (`src/viewerServer/evidence/mediaResolver.ts`) now uses `lstat()` and
152
- rejects any non-regular-file entry, closing that escape.
153
-
154
- - **Loopback-only, fixed local origin**: the Node viewer server binds only
155
- to `127.0.0.1` (never `0.0.0.0`), never a configurable remote host.
156
- `--port` selects the TCP port only (default `4319`); an explicit alternate
157
- port fails startup if already in use rather than silently falling back.
158
- - **Explicit evidence root, path containment, traversal rejection**: `--root`
159
- is the only filesystem root the server ever reads from. Every artifact/
160
- media route resolves a caller-supplied handle against that root through
161
- the existing canonical discovery/classification path and rejects any
162
- handle that would resolve outside it - the server never exposes `--root`
163
- as a generic static directory or arbitrary filesystem path, and never
164
- interprets an `EvidenceReference` as a filesystem path to the browser.
165
- - **No arbitrary filesystem browsing, no static-candidate path
166
- interpretation**: the viewer offers no directory-listing or free-path
167
- endpoint; every route addresses one specific, already-discovered handle.
168
- - **Read-only API (v0.8)**: in v0.8 every `/api/*` route rejected
169
- non-`GET`/`HEAD` methods with `405` at a single top-of-handler check
170
- (`src/viewerServer/httpServer.ts`). v0.9 keeps that check for every
171
- inspection route and adds exactly three project-aware authoring `POST`
172
- routes, described in "v0.9 local annotation write boundary" below.
173
- - **No target-source or evidence mutation**: the viewer never edits target
174
- source and never modifies an existing Observer evidence artifact. In v0.8
175
- the viewer server had no filesystem-write path at all. In v0.9 a
176
- project-aware session may create new immutable annotation, change-contract,
177
- and imported external-reference artifacts through the canonical writers,
178
- only on an explicit authoring request. A standalone `view --root` session
179
- still never writes.
180
- - **Binding/context files are explicit local session input, not persisted
181
- evidence**: `--bindings-file`/`--context-file` are read once at startup,
182
- validated through the existing canonical validators, held only in server
183
- memory, and never written into any Observer artifact or exposed as a
184
- filesystem path to the browser. The viewer never runs
185
- `@dailephd/my-dev-kit` and never rebuilds a bounded context or its
186
- correlation from these files - it only displays what it was given.
187
- - **PWA shell cache scope**: the service worker precaches only the built
188
- application shell (HTML/JS/CSS/icons/manifest); `navigateFallbackDenylist`
189
- excludes every `/api/` route from the precache, verified both statically
190
- (built `sw.js`) and live (real Chromium: zero Cache Storage entries under
191
- any `/api/` pathname after normal use - see
192
- `docs/reports/v0.8-integrated-viewer-acceptance-batch8.md`).
193
- - **Server-down stale-evidence protection**: proven in real Chromium - after
194
- the server is stopped and the page reloaded, the app shell may still
195
- render from the precache, but the evidence-dependent surface shows an
196
- explicit unavailable state, never previously-fetched evidence presented
197
- as current.
198
-
199
- ## v0.9 local annotation write boundary (released in 0.9.0)
200
-
201
- v0.9 adds a narrow local write surface to the viewer. It is released in
202
- `0.9.0`. It is a same-machine capability boundary for
203
- one local viewer session. It is not remote account authentication and does not
204
- protect against other software already running as the same user.
205
-
206
- - **Loopback only**: the viewer still binds only to `127.0.0.1`.
207
- - **Project-aware authoring only**: authoring is enabled only when `view` runs
208
- without `--root` inside an initialized project. The standalone arbitrary-root
209
- viewer (`view --root <root>`) stays read-only. Its
210
- `GET /api/authoring/session` reports `enabled: false`, and every authoring
211
- `POST` returns `403`.
212
- - **Session capability**: each project-aware server creates a random 32-byte
213
- token (64 hex characters) in memory. It is handed out by
214
- `GET /api/authoring/session` only on the exact expected loopback `Host`, as a
215
- defense against DNS rebinding. The viewer keeps it only in React memory. It
216
- is never persisted, never written into evidence, and never cached.
217
- - **Request checks, in order**: exact `Host`, exact same-origin `Origin`, the
218
- `x-frontend-observer-authoring-token` header compared in constant time,
219
- `content-type: application/json`, identity `content-encoding` only, a
220
- 256 KiB (`262144` byte) body limit, valid JSON, and a closed request shape
221
- with unknown fields rejected.
222
- - **Exactly three `POST` routes**: `POST /api/annotations`,
223
- `POST /api/annotations/:handle/promote-contract`, and
224
- `POST /api/annotations/:handle/materialize-reference`. `PUT`, `PATCH`, and
225
- `DELETE` stay unsupported everywhere. Any other `POST` returns `405`.
226
- - **No permissive CORS**: no `Access-Control-Allow-*` headers are sent, so a
227
- page from any other origin cannot read the capability or send a JSON
228
- authoring request. A real-Chromium test proves this for all three routes.
229
- - **Server-side resolution only**: the browser sends evidence handles and item
230
- ids, never output paths. The server resolves handles through canonical
231
- discovery with path containment and writes only under the project's managed
232
- evidence root (`annotations`, `contracts`, and `references`) through the
233
- canonical writers.
234
- - **Serialized writes**: all three routes share one write queue per session.
235
- Annotation revisions use stale-parent conflict detection instead of
236
- last-write-wins.
237
- - **No automatic approval**: promotion never approves a baseline, and
238
- materialization never approves a reference or changes project reference
239
- acceptance. Contract activation for `check` happens only on explicit
240
- request.
241
- - **No caching of authority**: authoring and evidence API responses use
242
- `cache-control: no-store`, and the service worker never caches `/api/`
243
- routes, including the new `POST` routes.
244
- - **Safe media**: annotation overlay media is served only after the stored
245
- SVG is re-rendered from the canonical artifact and verified. It uses a
246
- script-blocking `content-security-policy` with `sandbox` and `nosniff`.
247
- Existing media containment and symlink checks apply unchanged.
248
- - **Temporary directories**: evidence discovery skips writer temporary
249
- `.tmp-*` directories, so a partially written artifact is never presented as
250
- evidence.
251
-
252
- ## Not yet addressed
253
-
254
- Certificate-failure-specific handling, permission-prompt-specific handling
255
- (Chromium's default deny-all applies; no permission is ever explicitly
256
- granted), and any non-loopback/remote browsing mode remain unimplemented and
257
- out of scope. `@dailephd/my-frontend-observer@0.9.0` is published to npm, and a
258
- pre-release readiness CI workflow (Windows/Linux/macOS packed-candidate
259
- validation, now covering the v0.8 viewer alongside every earlier version's
260
- packed behavior) exists (see `docs/CI_CD.md`). The v0.7 external-reference/
261
- correction-workflow security properties above are released as part of
262
- `0.7.0`, following a completed cross-platform pre-release security
263
- validation stage (see `docs/reports/v0.7-pre-release-readiness.md`). The
264
- v0.8 interactive viewer's security boundary described above is released as
265
- part of `0.8.0`, following a completed formal pre-release cross-platform
266
- security validation stage (see
267
- `docs/reports/v0.8-prerelease-readiness-cross-platform-security-code-rot.md`).
268
- Symlink/junction filesystem-escape handling for the viewer's raw-evidence
269
- routes is now exercised by a dedicated regression test
270
- (`tests/unit/viewerEvidenceServer.test.ts`), which caught and led to the fix
271
- described above. The v0.9 annotation write boundary described above is
272
- implemented and covered by local security tests, but its formal
273
- cross-platform pre-release security validation has not run yet. None of this
274
- expands the security scope above: remote browsing, certificate handling, and
275
- permission-prompt handling remain separate, unimplemented concerns.
1
+ # Security
2
+
3
+ Project acceptance paths are portable, project-relative, and realpath-contained before use, including symlink/junction escape rejection. `current` alias replacement changes only catalog metadata and never deletes or overwrites canonical evidence. `check` adds no generic file-serving route, inferred binding, source mutation, persisted check artifact, or image bytes to JSON; configured references must be explicitly approved. JSON artifact paths are project-relative.
4
+
5
+ ## Current controls
6
+
7
+ `my-frontend-observer` launches a real, sandboxed Chromium browser
8
+ (`src/browser/chromiumAdapter.ts`) and enforces a conservative, local-first,
9
+ credential-free, non-destructive browser/network boundary
10
+ (`src/safety/policy.ts`) as actual product behavior, covered by real-Chromium
11
+ tests:
12
+
13
+ - allowed schemes are `http`/`https` only;
14
+ - allowed hosts are loopback only (`localhost`, `127.0.0.1`, `::1`, and any
15
+ `127.x.x.x` form) - no DNS resolution, no arbitrary "local dev host";
16
+ - credential-bearing URLs (`user:pass@host`) are rejected;
17
+ - the initial target, every navigation redirect, and every subresource
18
+ request are independently classified against the same loopback policy and
19
+ blocked before being contacted if unsafe;
20
+ - popups and downloads are never followed/saved (reported as non-fatal
21
+ diagnostics);
22
+ - navigation and readiness are bounded by explicit, request-configured
23
+ timeouts - no unbounded wait;
24
+ - the Chromium browser/context/page are reliably closed on every exit path
25
+ (success, safety rejection, navigation/readiness failure, or an
26
+ unexpected internal error);
27
+ - the observed target's own content/source is never modified by observation.
28
+
29
+ ## Comparison (`compare`, shipped as part of the published `0.4.0` package)
30
+
31
+ `my-frontend-observer compare` introduces no new network or browser
32
+ surface: it never
33
+ launches Chromium, never navigates, and never re-observes a target - it only
34
+ reads two local, already-persisted observation-artifact `manifest.json`
35
+ files (`src/artifacts/artifactReader.ts`) through the same structural
36
+ validator the observation writer uses, computes a pure in-memory
37
+ comparison, and writes one local comparison `manifest.json`
38
+ (`src/artifacts/comparisonArtifactWriter.ts`). Manifest content is parsed
39
+ as JSON only and is never executed (no `eval`, no dynamic code loading from
40
+ a manifest).
41
+
42
+ ## Frontend contracts (`approve-baseline`/`save-change-contract`/`evaluate-contract`, shipped as part of the published `0.5.0` package)
43
+
44
+ These three commands introduce no new browser or network surface:
45
+ `src/application/frontendContractPersistenceService.ts` and
46
+ `src/application/frontendContractEvaluationService.ts` import nothing from
47
+ `src/browser/` and never launch Chromium. `approve-baseline` and
48
+ `save-change-contract` validate and persist a local JSON contract file
49
+ (parsed as JSON only, never executed); `evaluate-contract` reads
50
+ already-persisted local observation/comparison/contract artifacts and runs
51
+ the pure `evaluateFrontendContract` function. None of the three navigates,
52
+ re-observes a target, or contacts a network resource.
53
+
54
+ ## Bounded agent context and correlation (v0.6, released as `0.6.0`)
55
+
56
+ `src/domain/boundedAgentContextProjection.ts` and
57
+ `src/domain/boundedAgentContextCorrelation.ts` introduce no new browser or
58
+ network surface: neither imports anything from `src/browser/`, neither
59
+ launches Chromium or navigates, and neither performs filesystem or network
60
+ I/O of its own. Both are pure, in-memory functions over already-captured
61
+ observation/comparison/contract evidence plus caller-supplied candidate
62
+ static-evidence records - the correlation module never reads a file path or
63
+ retrieves anything itself; the caller (outside this repository) is
64
+ responsible for however it obtained the candidate evidence it passes in.
65
+ Neither module embeds source code snippets, redacts anything, or invokes
66
+ another ecosystem tool (`@dailephd/my-dev-kit` is not a dependency of either
67
+ module). Bounded runtime projections may include existing screenshot *path
68
+ references* (never embedded bytes), consistent with every other artifact
69
+ family's existing reference-not-embed discipline.
70
+
71
+ ## External visual-reference security and privacy boundary (v0.7, released as `0.7.0`)
72
+
73
+ External visual-reference support (`import-reference`/`approve-reference`/
74
+ `evaluate-reference-fidelity`, plus the programmatic correction-workflow
75
+ coordinator) is released as part of the published `0.7.0` package. Imported reference images remain
76
+ local-first evidence: `import-reference` reads a local file path only,
77
+ never a URL, and no code path in this repository uploads a reference image,
78
+ a candidate screenshot, source code, or any derived evidence to an external
79
+ service. Reference/candidate compatibility and fidelity evaluation are pure,
80
+ in-process computations over already-loaded artifacts - neither launches a
81
+ network request.
82
+
83
+ Bounded local-file safety for reference images is a frozen, source-verified
84
+ policy, not an open decision: `EXTERNAL_REFERENCE_SUPPORTED_IMAGE_FORMATS`
85
+ (`png`/`jpeg`/`webp`, detected from header/magic bytes only, never from a
86
+ caller-declared file extension), `EXTERNAL_REFERENCE_MAX_IMAGE_BYTES`
87
+ (20,000,000 bytes), and `[EXTERNAL_REFERENCE_MIN_DIMENSION_PX,
88
+ EXTERNAL_REFERENCE_MAX_DIMENSION_PX]` (`[1, 8192]` pixels per side, parsed
89
+ from the same bounded header bytes, never a full pixel decode) are all
90
+ enforced before any artifact is persisted; an unsupported/undetectable
91
+ format, an over-limit file, or invalid/out-of-bound dimensions is rejected
92
+ outright (`unsupported-image-format`/`invalid-image-dimensions`/
93
+ `image-too-large`), never silently clamped or accepted.
94
+
95
+ Reference images are treated as untrusted data, not executable content: the
96
+ format/dimension detector never evaluates embedded code, never dynamically
97
+ loads a script, and never treats image metadata as an instruction source -
98
+ only bounded header-byte inspection is performed, no general-purpose image
99
+ or video codec is invoked. Explicit, caller-supplied state
100
+ (`--state-file`/`--applicability-file`, `theme`/`applicationState`/
101
+ `authenticatedState`) is a closed, bounded label vocabulary with no field
102
+ capable of holding a credential, token, cookie, session id, or authorization
103
+ header - `authenticatedState` accepts only the literal values
104
+ `'authenticated'`/`'unauthenticated'`.
105
+
106
+ Reference artifacts and the bounded correction handoff preserve path privacy
107
+ and boundedness: every reference/observation/binding/fidelity/context
108
+ identity function is a pure hash of semantic content only - an operational
109
+ file path (the image path, a `--regions-file`/`--requirements-file`/
110
+ `--applicability-file`/`--state-file`/`--bindings-file` path, or an artifact
111
+ root directory) is never included in any logical identity, and importing
112
+ the same semantic reference content from two different filesystem locations
113
+ produces the same `referenceRequestId`. The bounded coding-agent handoff
114
+ (`ReferenceCorrectionHandoff`) never embeds raw reference image bytes, a
115
+ full `ObservationArtifact`, or a source excerpt - only stable identifiers,
116
+ bounded fidelity mismatch records, and evidence path *references*.
117
+
118
+ Import never silently promotes an image to an approved reference: only the
119
+ explicit `approve-reference` command (or `approveExternalReference`
120
+ programmatically) transitions a reference out of the `'imported'` lifecycle
121
+ state, and the v0.7 correction workflow's `prepareReferenceCorrection`/
122
+ `reviewReferenceCorrectionAttempt` both fail closed if the supplied
123
+ reference is not already approved. Neither function - nor anything either
124
+ calls - ever invokes `approveExternalReference` or
125
+ `approveAndPersistBaseline` itself; a `'pass'` review result is reported as
126
+ `approvalEligible: true`, a plain flag, never an automatic approval action.
127
+
128
+ Reference/candidate comparison and fidelity evaluation do not broaden the
129
+ existing browser/network boundary: candidate rendering continues through the
130
+ existing loopback-only Chromium observation path unchanged, and reference
131
+ evaluation itself never launches a browser at all (it consumes only
132
+ already-captured `ObservationArtifact` evidence). The v0.7 correction
133
+ workflow never edits target source: `src/domain/referenceCorrectionWorkflow.ts`
134
+ and everything it imports contain no filesystem-write call, no
135
+ `child_process` invocation, and no patch-application mechanism - the actual
136
+ source edit between review attempts is always the responsibility of an
137
+ external implementation actor (a human or a coding agent), never this
138
+ package's own product code. No remote AI/model-provider dependency was
139
+ introduced anywhere in v0.7.
140
+
141
+ ## Interactive local viewer (v0.8, released as `0.8.0`)
142
+
143
+ `my-frontend-observer view` (`src/viewerServer/`, `viewer/`) is implemented,
144
+ tested, and formally security-reviewed. The properties below were verified
145
+ locally/manually during implementation (Batches 1-8) and then re-verified
146
+ through the formal pre-release security audit - see
147
+ `docs/reports/v0.8-prerelease-readiness-cross-platform-security-code-rot.md`,
148
+ which also found and fixed one real finding: a media filename that is
149
+ itself a symlink/junction pointing outside the evidence root could
150
+ previously have had its linked-to file's content served. `checkExists()`
151
+ (`src/viewerServer/evidence/mediaResolver.ts`) now uses `lstat()` and
152
+ rejects any non-regular-file entry, closing that escape.
153
+
154
+ - **Loopback-only, fixed local origin**: the Node viewer server binds only
155
+ to `127.0.0.1` (never `0.0.0.0`), never a configurable remote host.
156
+ `--port` selects the TCP port only (default `4319`); an explicit alternate
157
+ port fails startup if already in use rather than silently falling back.
158
+ - **Explicit evidence root, path containment, traversal rejection**: `--root`
159
+ is the only filesystem root the server ever reads from. Every artifact/
160
+ media route resolves a caller-supplied handle against that root through
161
+ the existing canonical discovery/classification path and rejects any
162
+ handle that would resolve outside it - the server never exposes `--root`
163
+ as a generic static directory or arbitrary filesystem path, and never
164
+ interprets an `EvidenceReference` as a filesystem path to the browser.
165
+ - **No arbitrary filesystem browsing, no static-candidate path
166
+ interpretation**: the viewer offers no directory-listing or free-path
167
+ endpoint; every route addresses one specific, already-discovered handle.
168
+ - **Read-only API (v0.8)**: in v0.8 every `/api/*` route rejected
169
+ non-`GET`/`HEAD` methods with `405` at a single top-of-handler check
170
+ (`src/viewerServer/httpServer.ts`). v0.9 keeps that check for every
171
+ inspection route and adds exactly three project-aware authoring `POST`
172
+ routes, described in "v0.9 local annotation write boundary" below.
173
+ - **No target-source or evidence mutation**: the viewer never edits target
174
+ source and never modifies an existing Observer evidence artifact. In v0.8
175
+ the viewer server had no filesystem-write path at all. In v0.9 a
176
+ project-aware session may create new immutable annotation, change-contract,
177
+ and imported external-reference artifacts through the canonical writers,
178
+ only on an explicit authoring request. A standalone `view --root` session
179
+ still never writes.
180
+ - **Binding/context files are explicit local session input, not persisted
181
+ evidence**: `--bindings-file`/`--context-file` are read once at startup,
182
+ validated through the existing canonical validators, held only in server
183
+ memory, and never written into any Observer artifact or exposed as a
184
+ filesystem path to the browser. The viewer never runs
185
+ `@dailephd/my-dev-kit` and never rebuilds a bounded context or its
186
+ correlation from these files - it only displays what it was given.
187
+ - **PWA shell cache scope**: the service worker precaches only the built
188
+ application shell (HTML/JS/CSS/icons/manifest); `navigateFallbackDenylist`
189
+ excludes every `/api/` route from the precache, verified both statically
190
+ (built `sw.js`) and live (real Chromium: zero Cache Storage entries under
191
+ any `/api/` pathname after normal use - see
192
+ `docs/reports/v0.8-integrated-viewer-acceptance-batch8.md`).
193
+ - **Server-down stale-evidence protection**: proven in real Chromium - after
194
+ the server is stopped and the page reloaded, the app shell may still
195
+ render from the precache, but the evidence-dependent surface shows an
196
+ explicit unavailable state, never previously-fetched evidence presented
197
+ as current.
198
+
199
+ ## v0.9 local annotation write boundary (released in 0.9.0)
200
+
201
+ v0.9 adds a narrow local write surface to the viewer. It is released in
202
+ `0.9.0`. It is a same-machine capability boundary for
203
+ one local viewer session. It is not remote account authentication and does not
204
+ protect against other software already running as the same user.
205
+
206
+ - **Loopback only**: the viewer still binds only to `127.0.0.1`.
207
+ - **Project-aware authoring only**: authoring is enabled only when `view` runs
208
+ without `--root` inside an initialized project. The standalone arbitrary-root
209
+ viewer (`view --root <root>`) stays read-only. Its
210
+ `GET /api/authoring/session` reports `enabled: false`, and every authoring
211
+ `POST` returns `403`.
212
+ - **Session capability**: each project-aware server creates a random 32-byte
213
+ token (64 hex characters) in memory. It is handed out by
214
+ `GET /api/authoring/session` only on the exact expected loopback `Host`, as a
215
+ defense against DNS rebinding. The viewer keeps it only in React memory. It
216
+ is never persisted, never written into evidence, and never cached.
217
+ - **Request checks, in order**: exact `Host`, exact same-origin `Origin`, the
218
+ `x-frontend-observer-authoring-token` header compared in constant time,
219
+ `content-type: application/json`, identity `content-encoding` only, a
220
+ 256 KiB (`262144` byte) body limit, valid JSON, and a closed request shape
221
+ with unknown fields rejected.
222
+ - **Exactly three v0.9 `POST` routes**: v0.9 introduced
223
+ `POST /api/annotations`,
224
+ `POST /api/annotations/:handle/promote-contract`, and
225
+ `POST /api/annotations/:handle/materialize-reference`. `PUT`, `PATCH`, and
226
+ `DELETE` stay unsupported everywhere. The implemented and released v0.10
227
+ routes extend this same gate as described below.
228
+ - **No permissive CORS**: no `Access-Control-Allow-*` headers are sent, so a
229
+ page from any other origin cannot read the capability or send a JSON
230
+ authoring request. A real-Chromium test proves this for all three routes.
231
+ - **Server-side resolution only**: the browser sends evidence handles and item
232
+ ids, never output paths. The server resolves handles through canonical
233
+ discovery with path containment and writes only under the project's managed
234
+ evidence root (`annotations`, `contracts`, and `references`) through the
235
+ canonical writers.
236
+ - **Serialized writes**: all three routes share one write queue per session.
237
+ Annotation revisions use stale-parent conflict detection instead of
238
+ last-write-wins.
239
+ - **No automatic approval**: promotion never approves a baseline, and
240
+ materialization never approves a reference or changes project reference
241
+ acceptance. Contract activation for `check` happens only on explicit
242
+ request.
243
+ - **No caching of authority**: authoring and evidence API responses use
244
+ `cache-control: no-store`, and the service worker never caches `/api/`
245
+ routes, including the new `POST` routes.
246
+ - **Safe media**: annotation overlay media is served only after the stored
247
+ SVG is re-rendered from the canonical artifact and verified. It uses a
248
+ script-blocking `content-security-policy` with `sandbox` and `nosniff`.
249
+ Existing media containment and symlink checks apply unchanged.
250
+ - **Temporary directories**: evidence discovery skips writer temporary
251
+ `.tmp-*` directories, so a partially written artifact is never presented as
252
+ evidence.
253
+
254
+ ## v0.10 Visual Change authoring boundary
255
+
256
+ v0.10 extends only the project-aware guarded `POST` surface. It adds explicit
257
+ reference approval and actual/reference workflow creation, plus workflow
258
+ activation, canonical check, restore, handoff preparation, human review, and
259
+ recording of already-existing governance results. Every route reuses the same
260
+ exact Host, Origin, memory-only capability token, JSON content type,
261
+ compression rejection, body limit, closed request-shape, serialized-write,
262
+ path-containment, and `no-store` controls. Standalone `view --root` remains
263
+ read-only, and the service worker continues to cache no `/api/` response.
264
+
265
+ The additional surface does not grant source-edit or external-process
266
+ authority. Observer never edits target source, executes a coding agent, invokes
267
+ my-dev-kit, or contacts/controls an orchestrator. Optional orchestrator fields
268
+ are bounded traceability metadata only. Handoffs are generated in memory and
269
+ are not a persisted evidence family. Review acceptance requires the latest
270
+ canonical Observer check to be PASS; it cannot approve a baseline/reference or
271
+ restore project acceptance. Governance recording can reference only a
272
+ separately persisted canonical approval and does not perform that approval.
273
+
274
+ ## Not yet addressed
275
+
276
+ Certificate-failure-specific handling, permission-prompt-specific handling
277
+ (Chromium's default deny-all applies; no permission is ever explicitly
278
+ granted), and any non-loopback/remote browsing mode remain unimplemented and
279
+ out of scope. `@dailephd/my-frontend-observer@0.10.1` is the current release. A
280
+ pre-release readiness CI workflow (Windows/Linux/macOS packed-candidate
281
+ validation, now covering the v0.8 viewer alongside every earlier version's
282
+ packed behavior) exists (see `docs/CI_CD.md`). The v0.7 external-reference/
283
+ correction-workflow security properties above are released as part of
284
+ `0.7.0`, following a completed cross-platform pre-release security
285
+ validation stage (see `docs/reports/v0.7-pre-release-readiness.md`). The
286
+ v0.8 interactive viewer's security boundary described above is released as
287
+ part of `0.8.0`, following a completed formal pre-release cross-platform
288
+ security validation stage (see
289
+ `docs/reports/v0.8-prerelease-readiness-cross-platform-security-code-rot.md`).
290
+ Symlink/junction filesystem-escape handling for the viewer's raw-evidence
291
+ routes is now exercised by a dedicated regression test
292
+ (`tests/unit/viewerEvidenceServer.test.ts`), which caught and led to the fix
293
+ described above. The v0.9 annotation write boundary described above is
294
+ implemented and covered by local security tests; formal cross-platform
295
+ pre-release validation was completed before release. None of this
296
+ expands the security scope above: remote browsing, certificate handling, and
297
+ permission-prompt handling remain separate, unimplemented concerns.