299 lines
13 KiB
Markdown
299 lines
13 KiB
Markdown
# Task 6 — Frontend identity and administrator UX report
|
|
|
|
## RED
|
|
|
|
- Added API tests for the `/me` principal call and `mine`/`all` session-list scopes.
|
|
- Added component tests for regular-user scope, admin scope switching, owner labels,
|
|
administrator banner, foreign-owner delete confirmation, and foreign-owner archive
|
|
confirmation.
|
|
- Initial focused run: 7 expected failures (missing `getMe`, missing scope query,
|
|
missing owner label/admin controls, and missing foreign-action confirmation).
|
|
- The archive-confirmation regression was also run separately before its implementation
|
|
and failed because `window.confirm` was not called.
|
|
|
|
## GREEN
|
|
|
|
- `npx vitest run src/api/sessions.test.ts src/shell/NavSessions.test.tsx src/shell/AppShell.session-mgmt.test.tsx`
|
|
— passed (47 tests before the archive follow-up; the focused archive regression then passed).
|
|
- `npm test` — passed: 44 files / 305 tests.
|
|
- `npx tsc -b` — passed.
|
|
- `npm run build` — passed.
|
|
- `git diff --check` — passed.
|
|
- `npm run e2e` reached Playwright but could not run: the environment has no Chromium
|
|
executable at Playwright's configured cache path. No application test failure was reported.
|
|
|
|
## Files changed
|
|
|
|
- `frontend/src/api/types.ts`: typed principal and session scope contracts.
|
|
- `frontend/src/api/sessions.ts`: typed `/me` API call; scoped listing defaults to `mine`.
|
|
- `frontend/src/shell/AppShell.tsx`: identity query, admin-only session scope selector and
|
|
banner, owner-aware destructive action confirmations.
|
|
- `frontend/src/shell/NavSessions.tsx`: owner labels in the all-sessions view.
|
|
- `frontend/src/api/sessions.test.ts`, `frontend/src/shell/NavSessions.test.tsx`, and
|
|
`frontend/src/shell/AppShell.session-mgmt.test.tsx`: contract and UX coverage.
|
|
|
|
## Self-review
|
|
|
|
- Regular users remain fail-closed on `mine`; no administrator control renders without
|
|
`principal.isAdmin`.
|
|
- The all-sessions view includes owner labels (including `Unknown` for legacy records).
|
|
- Delete confirmation preserves the pre-existing select-all behavior and adds confirmation
|
|
for foreign/unknown owners. Foreign archive now also requires an explicit browser
|
|
confirmation; existing Stop & save already has its confirmation dialog.
|
|
- A read-only review found no critical, important, or minor issues. The archive guard was
|
|
added after that review in response to the requirement to cover every destructive rail
|
|
action, and has its own RED/GREEN regression plus the final full verification above.
|
|
|
|
## Concerns
|
|
|
|
- E2E remains environment-blocked until the Playwright Chromium browser is installed.
|
|
- Existing Vitest runs emit pre-existing MSW unmatched-request and dialog-ref warnings; all
|
|
assertions pass and this task does not modify those shared test/UI primitives.
|
|
|
|
## Review remediation
|
|
|
|
- A post-commit review correctly identified that matching `displayName` must never establish
|
|
ownership. The predicate now skips confirmation only when `session.author` exactly equals
|
|
`principal.subject`; all display-name matches and missing authors are conservative
|
|
cross-owner actions.
|
|
- Added RED/GREEN regressions where two principals share display name `Alice` but have distinct
|
|
subjects: both delete (with another session present, so select-all cannot mask the guard) and
|
|
archive require confirmation.
|
|
- Added `aria-pressed` to the My sessions / All sessions controls and asserts their selected state
|
|
before and after switching.
|
|
- Remediation verification: focused regressions passed; full frontend Vitest (44 files / 305
|
|
tests), `npx tsc -b`, `npm run build`, and `git diff --check` all passed.
|
|
|
|
|
|
---
|
|
|
|
# DWH authentication Task 6 — Nginx and CI gate report
|
|
|
|
## Scope
|
|
|
|
Added only the two DWH-auth Nginx gates and the `dwh-auth-linux` deployment workflow job:
|
|
|
|
- `scripts/test-dwh-auth-nginx-contract.sh`
|
|
- `scripts/test-dwh-auth-nginx-integration.sh`
|
|
- `.github/workflows/deployment.yml`
|
|
|
|
This report deliberately remains unstaged. The pre-existing frontend Task 6 report above is
|
|
preserved rather than overwritten.
|
|
|
|
## TDD RED
|
|
|
|
The structural gate was written before any Task 5 template change. Those templates already met
|
|
the approved contract, so the behavioral RED was obtained by copying them into one exact temporary
|
|
root and removing only the effective `/dwh/` `auth_request` directive. The new checker failed as
|
|
required, with no credential material in output:
|
|
|
|
```text
|
|
case=source_contract status=FAIL
|
|
```
|
|
|
|
The runtime gate was also first invoked before its file existed:
|
|
|
|
```text
|
|
bash: scripts/test-dwh-auth-nginx-integration.sh: No such file or directory
|
|
```
|
|
|
|
The CI-job RED check found no `dwh-auth-linux` job in `deployment.yml`. No production template was
|
|
modified: the tests prove the existing Task 5 template contract instead of weakening it.
|
|
|
|
## GREEN
|
|
|
|
Shell syntax and workflow YAML were checked with:
|
|
|
|
```text
|
|
bash -n scripts/test-dwh-auth-nginx-contract.sh scripts/test-dwh-auth-nginx-integration.sh
|
|
python3 -c import-yaml-and-safe-load
|
|
```
|
|
|
|
The structural gate passed its source contract plus these 13 real copied-and-mutated Nginx fixtures:
|
|
|
|
```text
|
|
missing_auth_request
|
|
missing_proxy_method
|
|
missing_proxy_body
|
|
missing_proxy_header_isolation
|
|
missing_content_length_clear
|
|
missing_verifier_key_forward
|
|
missing_upstream_key_clear
|
|
missing_failure_mapping
|
|
public_verifier
|
|
tcp_authenticator
|
|
postgrest_bypass
|
|
failure_mapped_to_success
|
|
full_secret_rate_key
|
|
```
|
|
|
|
Each test mutates an effective, not comment-only, directive and requires the checker to reject it.
|
|
The source test and all 13 fixture tests emitted `case=... status=PASS`, followed by
|
|
`case=summary status=PASS`.
|
|
|
|
The isolated Nginx 1.24 smoke passed these sanitized cases:
|
|
|
|
```text
|
|
nginx_1_24
|
|
build_dwh_auth
|
|
registry_setup
|
|
verifier_start
|
|
synthetic_upstreams
|
|
composite_nginx_config
|
|
nginx_start
|
|
auth_socket_unix_only
|
|
verifier_not_public
|
|
valid_v1
|
|
valid_legacy
|
|
invalid_key
|
|
revoked_key
|
|
expired_key
|
|
duplicate_v1
|
|
duplicate_legacy
|
|
stopped_verifier
|
|
header_and_path_isolation
|
|
summary
|
|
```
|
|
|
|
It builds with the pinned official Go 1.26.5 image when the host Go binary is absent, creates only
|
|
synthetic v1, legacy, revoked, and expired credentials in a `0700` `/tmp` root, runs both Nginx and
|
|
the verifier on explicit temporary Unix sockets, and uses a loopback-only marker backend. Its output
|
|
is strictly `case` and `status`; keys, values, and digests remain only in the exact temporary root
|
|
and are removed by the trap.
|
|
|
|
`nginx -t` passed against the complete generated configuration. The marker proves that successful
|
|
`/dwh/?keep=exact&second=two` reaches the upstream unchanged, while neither the client API key nor
|
|
client or verifier `X-DWH-Key-ID` reaches it. A Unix forwarding probe proves that the verifier sees
|
|
only `X-API-Key`, with Cookie, Authorization, and spoofed audit ID absent. Duplicate v1 and ordinary
|
|
legacy headers return 401 through Nginx; a stopped verifier returns 503.
|
|
|
|
The final local equivalent of the four CI commands passed:
|
|
|
|
```text
|
|
Docker Go 1.26.5: go test -race ./... -count=1 and go vet ./...
|
|
bash scripts/test-dwh-auth-build-contract.sh
|
|
bash scripts/test-dwh-auth-nginx-contract.sh
|
|
bash scripts/test-dwh-auth-nginx-integration.sh
|
|
```
|
|
|
|
The Go race suite passed for command, credential, record, registry, securefile, and service;
|
|
`go vet` was silent; the build contract passed; both Nginx gates reached their summaries.
|
|
|
|
## CI contract
|
|
|
|
The new job uses `actions/checkout` with `persist-credentials: false`, pins Go 1.26.5 with cache
|
|
keyed on `tools/dwh-auth/go.mod`, installs `nginx-light`, and runs exactly the four required commands.
|
|
Existing jobs were not altered.
|
|
|
|
## Self-review
|
|
|
|
- The template tests parse normalized effective directives, so commented-out declarations cannot
|
|
satisfy the gate.
|
|
- The authentication socket is configured as `http://unix:...:/verify`, is observed by `ss -xl`,
|
|
and Nginx itself listens only on a temporary Unix socket; neither test starts a public listener.
|
|
- All spawned processes are registered by PID; cleanup signals only those PIDs and deletes only the
|
|
exact `mktemp` root after a guarded path check.
|
|
- The verifier, marker, registry, Nginx prefix, PID, logs, config, and sockets all reside beneath
|
|
that root. No `/etc`, systemd, active Nginx config, stack, legacy route, or real registry/key is
|
|
read or changed.
|
|
- Task 5 templates were not modified because the structural and runtime tests passed unchanged.
|
|
|
|
## Concern
|
|
|
|
The sandbox `apply_patch` helper repeatedly failed with `bwrap: loopback: Failed RTM_NEWADDR:
|
|
Operation not permitted`. A narrowly scoped fallback editor was used only for the workflow and the
|
|
Nginx-version assertion. Its first workflow insertion interpreted the action-reference at signs;
|
|
the two malformed values were immediately corrected and all final YAML, exact-string, syntax, and
|
|
four-command checks were rerun. No remaining product concern is known; the integration gate requires
|
|
Nginx 1.24 and Python 3, both supplied by the specified Ubuntu CI runner.
|
|
|
|
|
|
---
|
|
|
|
# DWH authentication Task 6 — review remediation wave
|
|
|
|
## Review findings and RED evidence
|
|
|
|
The three review findings were reproduced against the Task 6 commit before their corresponding
|
|
hardening was accepted.
|
|
|
|
1. The contract checker originally selected only the first matching `/dwh/` location. A real copied
|
|
fixture appended this competing location without authentication:
|
|
|
|
```nginx
|
|
location ~ ^/dwh/ {
|
|
proxy_pass http://127.0.0.1:3001;
|
|
}
|
|
```
|
|
|
|
The first run reached the new check and failed as required:
|
|
|
|
```text
|
|
case=negative_postgrest_regex_bypass status=FAIL
|
|
```
|
|
|
|
2. The previous process stop sent TERM and immediately used an unbounded `wait`. A synthetic Python
|
|
child ignored TERM; the RED run used one exact short-lived watchdog only to prevent a test hang and
|
|
produced:
|
|
|
|
```text
|
|
case=cleanup_term_ignored_bounded status=FAIL
|
|
```
|
|
|
|
3. The TCP detector has a positive-control regression. A scratch copy of the integration script
|
|
replaced its `ss -ltnpH` detector with `return 1`; its known loopback listener was then not
|
|
detected and the run failed with:
|
|
|
|
```text
|
|
case=tcp_listener_detector_positive status=FAIL
|
|
```
|
|
|
|
All RED fixtures and the scratch script used an exact temporary path and were removed. No template,
|
|
service, workflow, key, or active Nginx configuration was changed.
|
|
|
|
## GREEN changes
|
|
|
|
- `location_declarations` consumes normalized, comment-stripped effective lines and `check_templates`
|
|
requires exactly one each of the only approved locations: verifier, unavailable named location, and
|
|
`/dwh/`. It therefore rejects both any extra intercepting location and a duplicate. The real regex
|
|
bypass and a new real duplicate `/dwh/` bypass fixture both pass by being rejected.
|
|
- `tcp_listener_for_pid` uses `ss -ltnpH` and a PID-bound match. The integration gate starts a
|
|
loopback-only synthetic listener, proves the detector sees that exact PID, stops and deregisters it,
|
|
then proves the verifier PID has no TCP listener while its Unix socket remains present.
|
|
- `stop_registered_pid` now sends TERM, polls for exit or zombie for a bounded deadline, sends KILL
|
|
if required, polls a second bounded deadline, and only reaps a direct child after terminal state is
|
|
proved. Explicit stops deregister their PID. The cleanup loop invokes that bounded operation only
|
|
for recorded PIDs and removes only its guarded temporary root.
|
|
- The synthetic child that ignores TERM is killed by the bounded path, must no longer answer to
|
|
`kill -0`, must not remain registered, and must finish within three seconds. Final gate output is
|
|
restricted to `case` and `status` lines.
|
|
|
|
## GREEN verification
|
|
|
|
```text
|
|
bash -n scripts/test-dwh-auth-nginx-contract.sh scripts/test-dwh-auth-nginx-integration.sh
|
|
Docker Go 1.26.5: go test -race ./... -count=1 and go vet ./...
|
|
bash scripts/test-dwh-auth-build-contract.sh
|
|
bash scripts/test-dwh-auth-nginx-contract.sh
|
|
gate contract: source plus 15 negative fixtures PASS, then summary PASS
|
|
bash scripts/test-dwh-auth-nginx-integration.sh
|
|
gate integration: 20 named cases PASS, then summary PASS
|
|
git diff --check
|
|
```
|
|
|
|
The integration cases include `cleanup_term_ignored_bounded`,
|
|
`tcp_listener_detector_positive`, `auth_socket_unix_only`, all existing credential decisions,
|
|
composite Nginx syntax, and stopped-verifier 503 behavior. Go race tests passed for command,
|
|
credential, record, registry, securefile, and service; vet and both diff checks were silent.
|
|
|
|
## Self-review and concern
|
|
|
|
The new location parser rejects comment-only and non-exact declarations because it operates on the
|
|
same normalized effective representation used by the rest of the contract. The TCP positive control
|
|
binds only `127.0.0.1` on a kernel-selected temporary port and is stopped through the same exact-PID
|
|
path under test. The bounded cleanup avoids arbitrary process lookup or broad signaling.
|
|
|
|
The environment still intermittently rejects `apply_patch` with the sandbox loopback error noted in
|
|
the original report; only narrowly scoped fallback edits to the two authorized scripts were used and
|
|
all final gates were rerun. No remaining review concern is known.
|