docs(16): re-review clean + code review fix report
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
---
|
||||
phase: 16-ci-dependency-audit-and-security-checks
|
||||
fixed_at: 2026-06-13T00:00:00Z
|
||||
review_path: .planning/phases/16-ci-dependency-audit-and-security-checks/16-REVIEW.md
|
||||
iteration: 1
|
||||
findings_in_scope: 9
|
||||
fixed: 6
|
||||
skipped: 3
|
||||
status: partial
|
||||
---
|
||||
|
||||
# Phase 16: Code Review Fix Report
|
||||
|
||||
**Fixed at:** 2026-06-13
|
||||
**Source review:** .planning/phases/16-ci-dependency-audit-and-security-checks/16-REVIEW.md
|
||||
**Iteration:** 1
|
||||
|
||||
**Summary:**
|
||||
- Findings in scope: 9 (CR-01; WR-01..WR-05; IN-01..IN-04 — IN-04 carries no fix)
|
||||
- Fixed: 6 (CR-01, WR-01, WR-02, WR-03, WR-04, WR-05, plus IN-01)
|
||||
- Skipped: 3 (IN-02, IN-03, IN-04 — all optional/no-action)
|
||||
|
||||
IN-01 was applied inside the CR-01 commit (same file, `scripts/check-audit.mjs`),
|
||||
so it does not get a standalone commit line below but is counted as fixed.
|
||||
|
||||
## Validation
|
||||
|
||||
All validation run inside the isolated review-fix worktree:
|
||||
|
||||
- `pnpm lint` — PASS (apps/api + apps/pwa, `--max-warnings 0`). Note: the project
|
||||
lint scope does not include `scripts/*.mjs`, so the edited `.mjs` scripts are
|
||||
syntax-checked via `node -c` instead (all pass).
|
||||
- `pnpm typecheck` — PASS (apps/api + apps/pwa `tsc --noEmit`, PWA e2e tsconfig).
|
||||
- `node --test scripts/__tests__/check-audit.test.mjs` — 8 pass / 0 fail
|
||||
(includes 3 new expired-waiver / isWaived assertions for CR-01).
|
||||
- `python3 -c 'yaml.safe_load(...)'` for `.gitea/workflows/ci.yml` and
|
||||
`.gitea/workflows/publish.yml` — both parse OK after edits.
|
||||
- Working tree clean after all commits (no stray/uncommitted changes).
|
||||
|
||||
The API DB-integration tests (`tests/routes/lists.test.ts`,
|
||||
`tests/lib/listAccess.test.ts`, etc.) fail with `ER_ACCESS_DENIED` in this
|
||||
environment because no dev MariaDB is reachable. That is pre-existing/environmental
|
||||
and unrelated to these fixes; no new test failures were introduced.
|
||||
|
||||
## Fixed Issues
|
||||
|
||||
### CR-01: Audit-waiver `expires` field is decorative — expired waivers never re-block
|
||||
|
||||
**Files modified:** `scripts/check-audit.mjs`, `scripts/__tests__/check-audit.test.mjs`
|
||||
**Commit:** 4bb205f
|
||||
**Applied fix:** Added an exported `isWaived(adv, allowlist)` predicate. An allowlist
|
||||
entry with no `expires`, or a future `expires`, waives the advisory; an entry whose
|
||||
`expires` is in the past (`<= Date.now()`) is treated as absent so the High/Critical
|
||||
advisory re-blocks. Applied `isWaived` in BOTH `selectBlocking` and
|
||||
`partitionAdvisories` (replacing the bare `!allowlist[...]` checks). Added three
|
||||
unit tests: expired waiver re-blocks via `selectBlocking`, expired waiver re-blocks
|
||||
via `partitionAdvisories`, and a direct `isWaived` truth-table test (future expiry →
|
||||
waived, past expiry → not waived, no-expiry → waived, missing → not waived). All 8
|
||||
tests pass.
|
||||
|
||||
### WR-01: `github.base_ref` interpolated into a shell command (script-injection vector)
|
||||
|
||||
**Files modified:** `.gitea/workflows/ci.yml`
|
||||
**Commit:** 26a6b2e
|
||||
**Applied fix:** Bound `github.event.pull_request.base.sha`, `head.sha`, and
|
||||
`github.base_ref` through an `env:` block (`PR_BASE_SHA`, `PR_HEAD_SHA`,
|
||||
`PR_BASE_REF`) on the "Probe PR base/head SHA" step. The `run:` body now references
|
||||
only the already-quoted shell variables — no `${{ ... }}` context interpolation
|
||||
inside the script. The merge-base fallback uses `git rev-parse "origin/$PR_BASE_REF"`.
|
||||
|
||||
### WR-03: `HEAD_SHA` has no fallback while `BASE_SHA` does — asymmetric defense
|
||||
|
||||
**Files modified:** `.gitea/workflows/ci.yml`
|
||||
**Commit:** 26a6b2e (committed together with WR-01 — same step in the same file)
|
||||
**Applied fix:** Added a symmetric head fallback (`if [ -z "$HEAD_SHA" ]; then
|
||||
HEAD_SHA=$(git rev-parse HEAD); fi`) and an `echo "Secret-scan range:
|
||||
${BASE_SHA}..${HEAD_SHA}"` line before the gitleaks invocation so the scanned range
|
||||
is logged rather than relying on git's `A..` → `A..HEAD` default.
|
||||
|
||||
### WR-02: Boot-smoke false-PASS if a regressed image emits ≥20 lines before binding
|
||||
|
||||
**Files modified:** `.gitea/workflows/publish.yml`
|
||||
**Commit:** 3daa351
|
||||
**Applied fix:** Captured `docker run` output into `OUT=$(...)` and read `EXIT=$?`
|
||||
from the docker command directly (no `| head -20` in the exit-bearing command), so a
|
||||
chatty-but-booting image can no longer SIGPIPE docker to exit 141 and false-PASS.
|
||||
`head -20` is now applied only to the printed `echo "$OUT"`. Treat both `0` and `124`
|
||||
as FAIL ("did not refuse boot"). Added a positive belt-and-suspenders assertion:
|
||||
the output must contain `DEV_AUTH_BYPASS=true is set in a production environment`
|
||||
(the exact D-08 guard marker from `bootGuards.ts`), so a refusal for an unrelated
|
||||
reason cannot masquerade as the guard working.
|
||||
|
||||
> Logic note: this change alters the PASS/FAIL decision logic of a security smoke
|
||||
> test. The shell logic was reviewed against the guard marker string, but the actual
|
||||
> container behavior under the forbidden env is not exercisable in this environment
|
||||
> (no docker daemon / built image). Recommend a human confirm the smoke step on a
|
||||
> real publish run.
|
||||
|
||||
### WR-05: Static `.dockerignore` assertions use unquoted-regex `grep` (false-positive prone)
|
||||
|
||||
**Files modified:** `.gitea/workflows/publish.yml`
|
||||
**Commit:** 3daa351 (committed together with WR-02 — same file)
|
||||
**Applied fix:** Switched the per-pattern assertion to comment-stripped, fixed-string
|
||||
matching: `grep -v '^[[:space:]]*#' .dockerignore | grep -qF "$pattern"`. Patterns
|
||||
are no longer treated as regexes (`.env` can't match `denv`) and a commented-out
|
||||
rule (`# .env was here`) no longer satisfies the check. Failure message updated to
|
||||
"missing active rule".
|
||||
|
||||
### WR-04: AUDIT-ADVISORY tier in the outdated report is effectively dead code
|
||||
|
||||
**Files modified:** `scripts/check-outdated.mjs`
|
||||
**Commit:** 3e609b2
|
||||
**Applied fix:** Took the SAFE relabel option (no risky full-tree rewrite). Renamed
|
||||
the tier from `AUDIT-ADVISORY` to `OUTDATED-WITH-ADVISORY` and documented, in the
|
||||
file header, the inline classification comment, and the printed header text, that it
|
||||
only matches outdated *direct* deps against advisory `module_name`s (most advisories
|
||||
are on transitive deps, so it rarely fires) and that the authoritative advisory gate
|
||||
is `check-audit.mjs`. The report still always exits 0 (advisory-only). The
|
||||
audit-parse-failure warning message was updated to the new tier name.
|
||||
|
||||
## Skipped Issues
|
||||
|
||||
### IN-02: `pnpm audit --json` is run twice per CI security job
|
||||
|
||||
**File:** `scripts/check-audit.mjs:84`, `scripts/check-outdated.mjs:65`
|
||||
**Reason:** Skipped — explicitly optional and out of v1 performance scope per REVIEW.md
|
||||
("Out of v1 performance scope and harmless"). The suggested fix (pipe one audit pass
|
||||
to both scripts via stdin/arg, or merge the two scripts) is a structural change to
|
||||
script interfaces and CI invocation with no correctness benefit; applying it here
|
||||
would be speculative scope creep.
|
||||
**Original issue:** Both scripts independently spawn `pnpm audit --json`, doubling
|
||||
the audit work in the security job.
|
||||
|
||||
### IN-03: `outdated-pins.json` reasons are not cross-checked against the audit allowlist
|
||||
|
||||
**File:** `scripts/outdated-pins.json` / `scripts/audit-allowlist.json`
|
||||
**Reason:** Skipped — explicitly optional ("Documentation-level coupling only").
|
||||
Both JSON files are flat maps that the consuming scripts iterate directly:
|
||||
`check-outdated.mjs` reads `pins[pkgName]` as a pin reason, and `check-audit.mjs`
|
||||
reads `allowlist[github_advisory_id]`. Injecting a meta `__note`/cross-reference key
|
||||
risks the consumers misreading it as real data (a `__note` pin would be treated as a
|
||||
pin reason if a package were ever named `__note`). The safer choice is to leave the
|
||||
data files as pure data rather than add inert-but-fragile meta keys. The suggested
|
||||
lint-step variant is net-new tooling, out of scope for a review fix.
|
||||
**Original issue:** Two independent suppression lists with no linkage between a
|
||||
pinned package and an audit waiver for the same package.
|
||||
|
||||
### IN-04: `expand.test.ts` is in scope but unrelated to this CI/security phase
|
||||
|
||||
**File:** `apps/api/tests/broker/expand.test.ts`
|
||||
**Reason:** Skipped — no action required. REVIEW.md states "**Fix:** None." The
|
||||
reviewer found no defects; the file appears in the review set only because it was
|
||||
touched/moved and is orthogonal to this phase.
|
||||
**Original issue:** Well-constructed test noted for completeness; no defect.
|
||||
|
||||
---
|
||||
|
||||
_Fixed: 2026-06-13_
|
||||
_Fixer: Claude (gsd-code-fixer)_
|
||||
_Iteration: 1_
|
||||
Reference in New Issue
Block a user