Files
acdl/.ciagent/REVIEW.md
T
Jon Chery 6e41f09c6e
acdl-ci / Lint (push) Successful in 6s
acdl-ci / Test (push) Successful in 26s
acdl-ci / Platform check-only (offline) (push) Successful in 9s
verify(P43): code review — 1 P0 auto-fixed, 1 P1 auto-fixed, 3 P1 flagged
---ci---
phase: 43
milestone: v1.9
status: verify
lessons:
  - P0 fix: run_platform.sh HITL gate passed approver via string interpolation into Python (GITHUB_ACTOR injection vector) — fixed by passing env vars (ACDL_HITL_*) read via os.environ
  - P1 fix: attestation_matrix._is_fresh accepted future-dated artifacts (negative age bypassed freshness) — fixed with negative-age guard + test
  - P1 flagged: WizClient._post does not check GraphQL errors (silent empty-list mask)
  - P1 flagged: WizClient._post no SSRF validation on WIZ_API_URL
  - P1 flagged: contract_resolver._load_env duplicates environment_check.load (can drift)
---/ci---

Multi-persona review of the v1.9 diff (v1.8.0..HEAD). Review pass 2
(post-complete) caught issues the initial self-review missed:

P0-INJECT (auto-fixed): scripts/run_platform.sh Step 7b interpolated
$APPROVER (GITHUB_ACTOR/GITEA_ACTOR) directly into a Python string
literal — an attacker-controllable username containing shell/python
metacharacters would execute arbitrary Python. Fixed: approver, contract
id, and env are now passed as environment variables to the subprocess
and read via os.environ[...] (no string interpolation).

P1-FRESHNESS (auto-fixed): core/attestation_matrix.py _is_fresh
accepted future-dated artifacts (negative age.days <= window_days).
Fixed: added age.total_seconds() < 0 guard rejecting future timestamps.
Test added: test_freshness_rejects_future_dated_artifact.

3 P1 flagged for post-hoc:
- WizClient._post does not surface GraphQL errors (silent empty mask)
- WizClient._post no SSRF validation on WIZ_API_URL (operator-supplied, low risk)
- contract_resolver._load_env duplicates environment_check.load (drift risk)

REVIEW.md updated with the findings. 494 tests pass; run_ci.sh + run_platform.sh --check-only green.
2026-07-23 11:54:58 +00:00

165 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ACDL v1.9 Milestone — Multi-Persona Code Review
**Reviewer:** ci-code-reviewer (model: glm-5.2)
**Scope:** v1.9 milestone — Phases 3942 (tags v1.8.1..v1.8.4), diff `v1.8.0..HEAD`
**Date:** 2026-07-23
**Verdict:** **READY TO SHIP** — 1 P0 auto-fixed, 1 P1 auto-fixed, 3 P1 flagged for post-hoc
> **Note (D-086):** This REVIEW.md was reconstructed at v1.9 complete.
> The previous content was the v1.2 milestone review (v1.3v1.8 reviews
> were not persisted to this file). No git history was rewritten; the
> v1.2 review is preserved in git history at the v1.2 review commit.
>
> **Review pass 2 (post-complete):** this review was re-run after the
> milestone COMPLETE to catch issues the initial self-review missed. The
> P0 (approver injection) and P1 (future-dated freshness) were auto-fixed.
---
## Summary
v1.9 closes four gaps left by v1.8 (user-directed, 2026-07-23): stale
design docs, no contract interpolation, promotion requires editing the
`environment` field, and unimplemented stubs. It also closes P1-1
(adapter hardcoded defaults, deferred from v1.2). 4 phases shipped
(3942): design-doc refresh + P1-1 parameterization, contract
interpolation + env schema, per-environment CI jobs, stub implementation.
## P0 issues
### P0-INJECT (auto-fixed)
**Shell→Python code injection via `GITHUB_ACTOR` in `scripts/run_platform.sh`
Step 7b (HITL gate).** The approver identity was interpolated directly
into a Python string literal (`attest('$CONTRACT_ID', '$RESOLVED_ENV',
'$APPROVER' ...)`). `GITHUB_ACTOR` (and `GITEA_ACTOR`) are attacker-
controllable in some CI configurations; a username containing `'; import
os; os.system(...); y='` would execute arbitrary Python.
**Fix (auto-applied):** the approver, contract id, and env are now passed
as environment variables to the Python subprocess
(`ACDL_HITL_CONTRACT_ID`, `ACDL_HITL_ENV`, `ACDL_HITL_APPROVER`) and read
via `os.environ[...]` inside the Python code — no string interpolation of
user-controllable values.
## P1 issues
### P1-FRESHNESS (auto-fixed)
**`core/attestation_matrix.py` `_is_fresh` accepted future-dated
artifacts.** A `timestamp` in the future produced a negative `age`, and
`age.days <= window_days` evaluated `True` for negative values, so a
backdated/future artifact bypassed freshness validation.
**Fix (auto-applied):** added a `age.total_seconds() < 0` guard that
rejects future-dated artifacts. Test added
(`test_freshness_rejects_future_dated_artifact`).
### P1-WIZ-ERRORS (flagged for post-hoc)
**`adapters/wiz/wiz_adapter.py` `WizClient._post` does not check for
GraphQL `errors` in the response.** A GraphQL API returns
`{data: ..., errors: [...]}`; if `errors` is present, `data.issues` can
be `null` and `.get("nodes", [])` silently masks the error as an empty
list (which then emits `WIZ_NOT_CONFIGURED`). Should surface GraphQL
errors as a failed PolicyCheckResult or raise.
### P1-WIZ-SSRF (flagged for post-hoc)
**`WizClient._post` performs no SSRF validation on `WIZ_API_URL`.** A
malicious `WIZ_API_URL` env var could target an internal endpoint. The
URL is operator-supplied (not consumer-controllable), so the risk is
low, but a allowlist/scheme check (`https://`) would harden it.
### P1-OBSOLETE-CHECK (flagged for post-hoc)
**`core/contract_resolver.py` `_load_env` duplicates
`core/environment_check.load`.** The duplication was intentional (so the
resolver works as both a package import and a script), but the two can
drift. A future refactor should extract a shared helper that both
import safely.
## Per-lens review
### Correctness
- The contract interpolation (`_expand_vars`) is recursive over
dicts/lists/strings; unknown tokens raise `ValueError` (fail loud).
Expansion is post-schema-validation, pre-IR-resolution — the schema
sees raw tokens (valid strings), the resolver sees concrete values.
- The `environment_override` (D-088) is applied BEFORE schema validation
so the interpolation context is consistent.
- P1-1: the adapter reads `desired_count`, `launch_type`, `family`,
`target_type`, `load_balancer_type` from inputs (with interface
defaults). The resolver's `child_input_map` routes wires to the
sub-resource that declares the input (desired_count → aws:ecs:service,
family → aws:ecs:task_definition). The v1.1 S3 regression is preserved
(byte-identical `main.tf` for S3-only stacks).
- The HITL attestation gate records the approver to the outbox, runs SoD
on prod (blocks on `approver_qa == approver_prod`), invokes the
attestation matrix. Dev skips (autonomous).
- The attestation matrix's freshness validation uses the §10.4 windows;
signature verification skips when the signing key is unset (D-089) and
is required when set.
- The Wiz real client uses the GraphQL API with pagination; graceful
degrade when unconfigured.
- The Kyverno translator handles pass/fail/skip/warn + severity + skip-
with-reason + resource construction; the inactive-for-TF guard is
preserved.
### Testing
- 493 offline tests (was 350 at v1.8 → 493 at v1.9, +143 new). Each new
feature has dedicated tests:
- P1-1: `test_p1_1_adapter_parameterization.py` (override + default + regression).
- Design docs: `test_design_docs_current.py` (no stale framing).
- Interpolation: `test_interpolation.py` + `test_sample_contracts_interpolate.py`
+ `test_environment_schema.py`.
- Per-env jobs: `test_per_env_contracts.py` + `test_deploy_workflow_env_input.py`
+ `test_consumer_guide_per_env_section.py`.
- Stubs: `test_route_halt_artifact.py` + `test_hitl_gates.py` +
`test_attestation_matrix.py` + `test_wiz_adapter_real_client.py` +
expanded `test_kyverno_adapter.py`.
- `run_ci.sh` exits 0; `run_platform.sh --check-only` exits 0.
### Security
- No credentials introduced. The SNS topic is KMS-encrypted.
- SoD blocks on identity equality; the halt artifact is in the audit chain.
- The attestation matrix fails loud on missing/expired evidence for prod/dr.
- Signature verification is required when the signing key is set.
- The adapter has no hardcoded resource defaults (P1-1 closed) — defaults
live in the L1 interface, not the adapter.
### Performance
- N/A (this milestone is about correctness + design-doc accuracy + stub
implementation, not perf).
### Maintainability
- The interpolation is a single recursive walker; the env context is
loaded via a self-contained `_load_env` (works as script + package import).
- The `child_input_map` makes multi-resource L1 wire routing deterministic
(the sub-resource that declares the input receives the value).
- The attestation matrix's concern lists + freshness table are data-driven
(adding a concern is a table extension, not new logic).
- The Wiz `WizClient` is a clean class with a single `_post` seam (testable
with `mock.patch.object`).
### Adversarial
- The interpolation fail-loud (`ValueError` on unknown tokens) prevents
silent mis-resolution — a typo in a token name surfaces immediately,
not as a stale literal in the emitted Terraform.
- The `environment_override` is applied before schema validation, so a
contract with `environment: dev` cannot silently interpolate against
the dev env when the workflow passes `environment: prod` — the override
is authoritative.
- The SoD check reads `approver_qa` from the outbox (the platform is the
only writer); a consumer cannot forge the approver identity.
- The attestation matrix's signature skip is explicit + logged (not silent).
## Conclusion
v1.9 is READY TO SHIP after the review auto-fixes. 1 P0 (approver
injection — auto-fixed by passing env vars instead of string
interpolation) and 1 P1 (future-dated freshness — auto-fixed with a
negative-age guard + test). 3 P1 flagged for post-hoc (Wiz GraphQL
error handling, Wiz SSRF validation, `_load_env` duplication). The
milestone's code is complete + verified: design docs are current,
contract interpolation works, per-env promotion requires no field
editing, all stubs are implemented (audit ledger Object Lock/JWS
build-out deferred per D-083), and P1-1 is closed. Ship tag: `v1.9.0`
(feature milestone, next minor per run.md — v1.8 shipped `v1.8.0`).
494 offline tests pass (was 350 at v1.8, +144 new); `run_ci.sh` + `run_platform.sh --check-only` green.