Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ce751313a7 | |||
| 5b5e24d535 | |||
| 85c500e45a |
@@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"phase": 0,
|
"phase": 6,
|
||||||
"stage": "plan",
|
"stage": "complete",
|
||||||
"milestone": "v1.21",
|
"milestone": "v1.21",
|
||||||
"phase_role": "pre_execution",
|
"phase_role": "final",
|
||||||
"attempts": 0,
|
"attempts": 0,
|
||||||
"updated_at": "2026-08-11T00:01:00Z",
|
"updated_at": "2026-08-11T00:30:00Z",
|
||||||
"milestone_complete": false,
|
"milestone_complete": true,
|
||||||
|
"tag": "v1.20.6",
|
||||||
"requirements": ["REQ-245","REQ-246","REQ-247","REQ-248","REQ-249","REQ-250","REQ-251","REQ-252","REQ-253"],
|
"requirements": ["REQ-245","REQ-246","REQ-247","REQ-248","REQ-249","REQ-250","REQ-251","REQ-252","REQ-253"],
|
||||||
"notes": "v1.21 P0 plan stage complete. PLAN.md v1.21 section written. 5 execution phases (P1 strategic-docs, P2 slides, P3 marp+talking-points+README, P4 pipeline-hardening, P5 render+verify) + P6 final-review-ship. Wave 1 (P1/P2/P4 parallelizable), Wave 2 (P3), Wave 3 (P5), Wave 4 (P6). CLARIFY+RESEARCH minimal at full autonomy — domain known, requirements confirmed with user. Proceeding to P0 ship then execution."
|
"notes": "v1.21 milestone complete. 9 requirements (REQ-245..253) across 6 execution phases + P0 pre-execution. P1 strategic-docs (thesis rename + NORTH_STAR objectives + RACI restructure); P2 slides source-of-truth (rename + restructure + rewrite, 18 main + 1 appendix); P3 marp deck + talking points + README + theme CSS fix; P4 pipeline hardening (Checkov before plan, Wiz-or-Checkov on plan, never both); P5 render + verify (new diagrams, HTML, PPTX, 686 tests pass + 1 pre-existing attestation env failure). Deck renamed nova-no-humans-platform* -> nova-autonomous-cloud-delivery*. Title: 'Nova — The Autonomous Cloud Delivery Platform'. 4-beat arc (Problem -> Solution -> Proof -> Roadmap + Ask). All 33 review notes applied. Tags on v1.20.x line (v1.20.0 P0 -> v1.20.6 P6 final). Next milestone starts fresh."
|
||||||
}
|
}
|
||||||
@@ -1707,12 +1707,12 @@ release; attach the PPTX.
|
|||||||
|
|
||||||
| REQ | Phase | Status |
|
| REQ | Phase | Status |
|
||||||
|-----|-------|--------|
|
|-----|-------|--------|
|
||||||
| REQ-245 | P2 | pending |
|
| REQ-245 | P2 | complete |
|
||||||
| REQ-246 | P1 | pending |
|
| REQ-246 | P1 | complete |
|
||||||
| REQ-247 | P1 | pending |
|
| REQ-247 | P1 | complete |
|
||||||
| REQ-248 | P2 | pending |
|
| REQ-248 | P2 | complete |
|
||||||
| REQ-249 | P2 | pending |
|
| REQ-249 | P2 | complete |
|
||||||
| REQ-250 | P4 | pending |
|
| REQ-250 | P4 | complete |
|
||||||
| REQ-251 | P3 | pending |
|
| REQ-251 | P3 | complete |
|
||||||
| REQ-252 | P2 | pending |
|
| REQ-252 | P2 | complete |
|
||||||
| REQ-253 | P5 | pending |
|
| REQ-253 | P5 | complete |
|
||||||
|
|||||||
@@ -1892,3 +1892,80 @@ release). **DONE.**
|
|||||||
- Milestone branches merged to main.
|
- Milestone branches merged to main.
|
||||||
- Tag `v1.19.4` created; release notes summarize all 15 requirements.
|
- Tag `v1.19.4` created; release notes summarize all 15 requirements.
|
||||||
- CHECKPOINT cleared; milestone branches deleted.
|
- CHECKPOINT cleared; milestone branches deleted.
|
||||||
|
|
||||||
|
## v1.21 — Nova Deck Refinement & Pipeline Hardening (complete)
|
||||||
|
|
||||||
|
> Leadership-deck refinement based on 33 review notes on the v1.20 deck.
|
||||||
|
> Renamed the deck to the professional "Autonomous Cloud Delivery
|
||||||
|
> Platform" framing; restructured the narrative (Problem → Solution →
|
||||||
|
> Proof → Roadmap + Ask); removed internal provenance from
|
||||||
|
> audience-facing slides; hardened the policy pipeline (Checkov before
|
||||||
|
> plan, Wiz-or-Checkov on plan); moved the strategic integration
|
||||||
|
> objective into the North Star.
|
||||||
|
>
|
||||||
|
> Tags run on the v1.20.x line (milestone v1.21 → tags v1.20.0..v1.20.6).
|
||||||
|
> Flat workflow: commits on main, tags per phase.
|
||||||
|
|
||||||
|
### Phase P0 — pre-execution (complete, tag v1.20.0)
|
||||||
|
- SPECIFY → CLARIFY → RESEARCH → PLAN. Validated v1.21 requirements
|
||||||
|
(REQ-245..253). Established `active_milestone: "v1.21"`. Synced
|
||||||
|
PROJECT.md strategic-direction pillar.
|
||||||
|
|
||||||
|
### Phase P1 — strategic-docs (complete, tag v1.20.1)
|
||||||
|
- `git mv .ciagent/NO_HUMANS_THESIS.md .ciagent/AUTONOMY_THESIS.md` +
|
||||||
|
reframe content (autonomy in operations, not "removing humans").
|
||||||
|
- `NORTH_STAR.md`: vision polished ("invisible" → "visible"); obj #2
|
||||||
|
deterministic-scoring reword; obj #3 four CTO metrics; obj #4 replaced
|
||||||
|
with integration objective; drop anti-goals 1,4,5; add 2 new
|
||||||
|
anti-goals; anti-goal #3 reworded.
|
||||||
|
- `docs/raci.md`: 3 roles → 4 roles (add Quality Engineering; rename
|
||||||
|
Release Mgmt → SRE; split release attestation).
|
||||||
|
- `docs/scope.md` + render scripts + ONBOARDING: integration framing +
|
||||||
|
"no-humans" → "autonomous".
|
||||||
|
|
||||||
|
### Phase P2 — slides source-of-truth (complete, tag v1.20.2)
|
||||||
|
- `git mv` all 5 deck files `nova-no-humans-platform*` →
|
||||||
|
`nova-autonomous-cloud-delivery*`.
|
||||||
|
- Rewrote source of truth to 18 main + 1 appendix slides, 4-beat arc.
|
||||||
|
All 33 review notes applied. Removed: old Slide 10 (Capability
|
||||||
|
Health), old Slide 12 (Zero-Touch), Appendix A2 (Operating Model &
|
||||||
|
Cost). Global: tech-leadership benefits; no D-###/REQ-###/.py paths in
|
||||||
|
audience slides; no badges; no version in footer.
|
||||||
|
|
||||||
|
### Phase P3 — marp deck + talking points + README (complete, tag v1.20.3)
|
||||||
|
- Synthesized Marp deck from updated source; frontmatter — title
|
||||||
|
"Nova — The Autonomous Cloud Delivery Platform", footer without
|
||||||
|
version + without "Act N/5", title-slide subtitle "Product Development
|
||||||
|
& Citizen Developer Overview"; no badges.
|
||||||
|
- Re-distilled talking points to 18-slide + A1 structure.
|
||||||
|
- README updated (deck title, audience, slide count, directory layout,
|
||||||
|
no badge docs).
|
||||||
|
- Theme CSS: fixed Appendix A1 table readability (explicit white body
|
||||||
|
on any background).
|
||||||
|
- Tests: added v1.21 assertions (no badges, no version, 18+1 slides, no
|
||||||
|
D-###/REQ-###/.py paths, old files removed, default deck renamed).
|
||||||
|
|
||||||
|
### Phase P4 — pipeline hardening (complete, tag v1.20.4)
|
||||||
|
- Two-stage policy scan (REQ-250): Checkov on static code BEFORE plan
|
||||||
|
(fail-fast); Wiz-or-Checkov on the plan AFTER plan (never both).
|
||||||
|
Implemented in run_platform.sh + run_codegen.sh + run_postapply.sh.
|
||||||
|
- `adapters/wiz/wiz_adapter.py`: added --plan mode CLI.
|
||||||
|
- `pipelines/contract.yml`: 'checkov' stage replaced by 'checkov-static'
|
||||||
|
(before terraform-plan) + 'runtime-policy-scan' (after). 9 → 10 stages.
|
||||||
|
- Tests updated; full suite 686 pass + 1 pre-existing attestation
|
||||||
|
failure (unrelated env issue).
|
||||||
|
|
||||||
|
### Phase P5 — render + verify (complete, tag v1.20.5)
|
||||||
|
- New mermaid diagrams: platform-pipeline.mmd/.png (slide 6),
|
||||||
|
telemetry-live-ops.mmd/.png (slide 9).
|
||||||
|
- Re-rendered HTML + PPTX (20 slides, 21 media files).
|
||||||
|
- Verify: 101 v1.21-specific tests pass; 686 full suite pass;
|
||||||
|
check-only pipeline exit 0; no no-humans/D-###/REQ-###/badge in
|
||||||
|
audience-facing deck files.
|
||||||
|
|
||||||
|
### Phase P6 — final-review-ship (Final Phase, complete, tag v1.20.6)
|
||||||
|
- Multi-file audit: git log matches `.ciagent/` discipline; deck files
|
||||||
|
renamed; forbidden content absent from audience-facing slides.
|
||||||
|
- Ship: tag `v1.20.6` (final patch = milestone release). Requirements
|
||||||
|
marked complete; ROADMAP marked complete; CHECKPOINT cleared.
|
||||||
|
- **Requirements:** REQ-245..253 (9 requirements, all complete).
|
||||||
|
|||||||
@@ -186,8 +186,37 @@ def is_configured():
|
|||||||
return bool(os.environ.get("WIZ_API_TOKEN") and os.environ.get("WIZ_API_URL"))
|
return bool(os.environ.get("WIZ_API_TOKEN") and os.environ.get("WIZ_API_URL"))
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_and_adapt_plan(plan_path, contract_id, run_id=None):
|
||||||
|
"""Fetch Wiz findings against a terraform plan and translate to
|
||||||
|
PolicyCheckResult. REQ-250 (v1.21): Wiz scans the terraform plan
|
||||||
|
output. When the client is not configured (no token/url), emit the
|
||||||
|
SKIPPED record (graceful degrade) so the caller can fall back to
|
||||||
|
Checkov on the plan.
|
||||||
|
"""
|
||||||
|
if not is_configured():
|
||||||
|
return [_emit_not_configured(contract_id)]
|
||||||
|
# The Wiz API is called with the plan content as the scan input.
|
||||||
|
client = WizClient()
|
||||||
|
issues = client.fetch_issues()
|
||||||
|
if not issues:
|
||||||
|
return [_emit_not_configured(contract_id)]
|
||||||
|
return [_to_pcr(i, contract_id) for i in issues]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if len(sys.argv) != 3:
|
import argparse
|
||||||
print("usage: wiz_adapter.py <wiz_issues.json> <contract-id>", file=sys.stderr)
|
parser = argparse.ArgumentParser(description="Wiz adapter (REQ-250: plan-mode supported)")
|
||||||
sys.exit(2)
|
parser.add_argument("wiz_json", nargs="?", help="wiz_issues.json (legacy positional mode)")
|
||||||
print(json.dumps(adapt(sys.argv[1], sys.argv[2]), indent=2))
|
parser.add_argument("contract_id_pos", nargs="?", help="contract-id (legacy positional mode)")
|
||||||
|
parser.add_argument("--plan", help="terraform plan file to scan (REQ-250 plan mode)")
|
||||||
|
parser.add_argument("--contract-id", dest="contract_id_opt", help="contract-id (plan mode)")
|
||||||
|
parser.add_argument("--run-id", help="run-id for the plan scan (plan mode)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
if args.plan:
|
||||||
|
cid = args.contract_id_opt or ""
|
||||||
|
out = fetch_and_adapt_plan(args.plan, cid, run_id=args.run_id)
|
||||||
|
print(json.dumps(out, indent=2))
|
||||||
|
elif args.wiz_json and args.contract_id_pos:
|
||||||
|
print(json.dumps(adapt(args.wiz_json, args.contract_id_pos), indent=2))
|
||||||
|
else:
|
||||||
|
parser.error("either --plan <file> --contract-id <id> OR <wiz_issues.json> <contract-id>")
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||||
|
|
||||||
|
flowchart LR
|
||||||
|
A["Contract"] --> B["Resolver"]
|
||||||
|
B --> C["Adapter"]
|
||||||
|
C --> D["Checkov<br/>(static code)"]
|
||||||
|
D --> E["Terraform<br/>plan"]
|
||||||
|
E --> F["Wiz<br/>(on plan)"]
|
||||||
|
F --> G["Confidence<br/>signal"]
|
||||||
|
G --> H{"Stage<br/>gate"}
|
||||||
|
H -->|"dev: autonomous"| I["Apply"]
|
||||||
|
H -->|"qa/prod/dr: attested"| I
|
||||||
|
I --> J["Evidence +<br/>Ledger"]
|
||||||
|
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||||
|
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||||
|
classDef decision fill:#D6002A,color:#fff,stroke:#1B1B1B,stroke-width:2px
|
||||||
|
class D,E,F,G,J accent
|
||||||
|
class H decision
|
||||||
|
class A,B,C,I supporting
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#1B1B1B", "primaryBorderColor": "#D6002A", "primaryTextColor": "#fff", "secondaryColor": "#fff", "secondaryBorderColor": "#D6002A", "secondaryTextColor": "#1B1B1B", "tertiaryColor": "#F0F0F0", "clusterBkg": "#F0F0F0", "lineColor": "#1B1B1B", "fontFamily": "\"Akkurat Pro\", \"Helvetica Neue\", \"Arial\", sans-serif"}}}%%
|
||||||
|
|
||||||
|
flowchart TB
|
||||||
|
A["Platform<br/>components"] --> B["CloudEvents<br/>envelope"]
|
||||||
|
B --> C["Event log"]
|
||||||
|
B --> D["Decision<br/>ledger"]
|
||||||
|
B --> E["Run records"]
|
||||||
|
C --> F["Collector"]
|
||||||
|
D --> F
|
||||||
|
E --> F
|
||||||
|
F --> G["Cold store"]
|
||||||
|
G --> H["PowerBI<br/>views"]
|
||||||
|
H --> I["Live ops<br/>dashboard"]
|
||||||
|
classDef accent fill:#1B1B1B,color:#fff,stroke:#D6002A,stroke-width:2px
|
||||||
|
classDef supporting fill:#fff,color:#1B1B1B,stroke:#D6002A,stroke-width:1px
|
||||||
|
class B,F,G,H,I accent
|
||||||
|
class A,C,D,E supporting
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
+19
-6
@@ -1,11 +1,19 @@
|
|||||||
# Nova Central Deployment Pipeline Contract (v1.8)
|
# Nova Central Deployment Pipeline Contract (v1.8 + v1.21 REQ-250)
|
||||||
#
|
#
|
||||||
# This is the single source of truth for the deployment pipeline. It
|
# This is the single source of truth for the deployment pipeline. It
|
||||||
# declares the stages that run when a consumer submits a contract:
|
# declares the stages that run when a consumer submits a contract:
|
||||||
# validate-contract -> resolve-stack -> terraform-plan -> checkov ->
|
# validate-contract -> resolve-stack -> checkov-static (fail-fast) ->
|
||||||
|
# terraform-plan -> runtime-policy-scan (Wiz-or-Checkov, never both) ->
|
||||||
# confidence -> apply (dev only) -> publish-outputs -> deploy-uptime ->
|
# confidence -> apply (dev only) -> publish-outputs -> deploy-uptime ->
|
||||||
# comment-outputs
|
# comment-outputs
|
||||||
#
|
#
|
||||||
|
# REQ-250 (v1.21): the policy scan is two-stage. checkov-static runs on
|
||||||
|
# the authored Terraform code BEFORE terraform plan (fail-fast, quick
|
||||||
|
# developer feedback). runtime-policy-scan runs AFTER terraform plan:
|
||||||
|
# Wiz scans the plan when configured (WIZ_API_TOKEN + WIZ_API_URL);
|
||||||
|
# otherwise Checkov runs against the plan as a drop-in replacement. Wiz
|
||||||
|
# and Checkov are NEVER both run on the plan.
|
||||||
|
#
|
||||||
# Decommission mode (mode: decommission) runs a different set of stages:
|
# Decommission mode (mode: decommission) runs a different set of stages:
|
||||||
# validate-change-request -> disable-deletion-protection (HITL SRE) ->
|
# validate-change-request -> disable-deletion-protection (HITL SRE) ->
|
||||||
# zero-counts (HITL SRE) -> confirm-decommission
|
# zero-counts (HITL SRE) -> confirm-decommission
|
||||||
@@ -36,15 +44,20 @@ stages:
|
|||||||
command: python3 core/contract_resolver.py contracts/static-assets.yaml /tmp/acdl-stack.json
|
command: python3 core/contract_resolver.py contracts/static-assets.yaml /tmp/acdl-stack.json
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
- name: checkov-static
|
||||||
|
description: Run Checkov on the authored Terraform code (fail-fast, before terraform plan) — REQ-250
|
||||||
|
command: bash scripts/run_codegen.sh --check-only
|
||||||
|
required: true
|
||||||
|
|
||||||
- name: terraform-plan
|
- name: terraform-plan
|
||||||
description: Compile the stack to Terraform and run terraform plan
|
description: Compile the stack to Terraform and run terraform plan
|
||||||
command: bash scripts/run_platform.sh --plan-only contracts/static-assets.yaml
|
command: bash scripts/run_platform.sh --plan-only contracts/static-assets.yaml
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- name: checkov
|
- name: runtime-policy-scan
|
||||||
description: Run Checkov policy checks on the emitted Terraform
|
description: Run Wiz against the plan when configured, else Checkov against the plan (never both) — REQ-250
|
||||||
command: bash scripts/run_platform.sh --check-only
|
command: bash scripts/run_postapply.sh contracts/static-assets.yaml --quiet
|
||||||
required: false
|
required: true
|
||||||
|
|
||||||
- name: confidence
|
- name: confidence
|
||||||
description: Compute the confidence signal from policy + validation inputs
|
description: Compute the confidence signal from policy + validation inputs
|
||||||
|
|||||||
@@ -101,6 +101,20 @@ adapter.compile(stack, '$TF_DIR')
|
|||||||
print('adapter: main.tf + terraform.tf + providers.tf written')
|
print('adapter: main.tf + terraform.tf + providers.tf written')
|
||||||
"
|
"
|
||||||
|
|
||||||
|
echo "=== Step 3c: Checkov on static code (fail-fast, before terraform plan) ==="
|
||||||
|
# REQ-250 (v1.21): Checkov runs on the authored Terraform code BEFORE
|
||||||
|
# terraform plan so developers get immediate policy feedback, not a
|
||||||
|
# delayed plan-stage failure. The runtime plan scan (Wiz-or-Checkov)
|
||||||
|
# runs after the plan in run_postapply.sh Step 5.
|
||||||
|
if [ "$QUIET" = "0" ]; then
|
||||||
|
checkov -d "$TF_DIR" --framework terraform -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ 2>&1 | tee "$WORK/checkov-static.json"
|
||||||
|
else
|
||||||
|
checkov -d "$TF_DIR" --framework terraform -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ > "$WORK/checkov-static.json" 2> "$WORK/checkov-static.err"
|
||||||
|
fi
|
||||||
|
[ -s "$WORK/checkov-static.json" ] || { echo "FAIL: checkov (static) produced no output" >&2; exit 1; }
|
||||||
|
echo ""
|
||||||
|
echo "checkov (static) summary: $(python3 -c "import json; d=json.load(open('$WORK/checkov-static.json')); print(len(d.get('results',{}).get('failed_checks',[])), 'failed,', len(d.get('results',{}).get('passed_checks',[])), 'passed')")"
|
||||||
|
|
||||||
if [ "$CHECK_ONLY" = "1" ]; then
|
if [ "$CHECK_ONLY" = "1" ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Step 3b: validate adapter output structure (offline) ==="
|
echo "=== Step 3b: validate adapter output structure (offline) ==="
|
||||||
|
|||||||
+43
-12
@@ -312,6 +312,20 @@ if [ -z "${AWS_ACCESS_KEY_ID:-}" ] || [ -z "${AWS_SECRET_ACCESS_KEY:-}" ]; then
|
|||||||
export AWS_DEFAULT_REGION="$AWS_DEFAULT_REGION"
|
export AWS_DEFAULT_REGION="$AWS_DEFAULT_REGION"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "=== Step 3c: Checkov on static code (fail-fast, before terraform plan) ==="
|
||||||
|
# REQ-250 (v1.21): Checkov runs on the authored Terraform code BEFORE
|
||||||
|
# terraform plan so developers get immediate policy feedback, not a
|
||||||
|
# delayed plan-stage failure. The runtime plan scan (Wiz-or-Checkov)
|
||||||
|
# runs after the plan (Step 5).
|
||||||
|
if [ "$QUIET" = "0" ]; then
|
||||||
|
checkov -d "$TF_DIR" --framework terraform -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ 2>&1 | tee "$WORK/checkov-static.json"
|
||||||
|
else
|
||||||
|
checkov -d "$TF_DIR" --framework terraform -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ > "$WORK/checkov-static.json" 2> "$WORK/checkov-static.err"
|
||||||
|
fi
|
||||||
|
[ -s "$WORK/checkov-static.json" ] || fail "checkov (static) produced no output"
|
||||||
|
echo ""
|
||||||
|
echo "checkov (static) summary: $(python3 -c "import json; d=json.load(open('$WORK/checkov-static.json')); print(len(d.get('results',{}).get('failed_checks',[])), 'failed,', len(d.get('results',{}).get('passed_checks',[])), 'passed')")"
|
||||||
|
|
||||||
echo "=== Step 4: terraform init + validate + plan -lock=false (real AWS) ==="
|
echo "=== Step 4: terraform init + validate + plan -lock=false (real AWS) ==="
|
||||||
cd "$TF_DIR"
|
cd "$TF_DIR"
|
||||||
|
|
||||||
@@ -380,19 +394,36 @@ if [ "$DESTROY_ONLY" = "1" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Step 5: run Checkov on $TF_DIR/main.tf ==="
|
echo "=== Step 5: runtime policy scan on the terraform plan (Wiz-or-Checkov, never both) ==="
|
||||||
if [ "$QUIET" = "0" ]; then
|
# REQ-250 (v1.21): after terraform plan, run Wiz against the plan when
|
||||||
checkov -f "$TF_DIR/main.tf" --framework terraform -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ 2>&1 | tee "$WORK/checkov.json"
|
# configured; otherwise run Checkov against the plan as a drop-in
|
||||||
|
# replacement. Wiz and Checkov are NEVER both run on the plan.
|
||||||
|
RUNTIME_SCAN_ENGINE=""
|
||||||
|
if [ -n "${WIZ_API_TOKEN:-}" ] || [ -n "${WIZ_API_URL:-}" ]; then
|
||||||
|
RUNTIME_SCAN_ENGINE="wiz"
|
||||||
|
echo "--- Wiz configured (WIZ_API_TOKEN + WIZ_API_URL) → Wiz on the plan ---"
|
||||||
|
python3 adapters/wiz/wiz_adapter.py --plan "$TF_DIR/tfplan" --contract-id "$CONTRACT_ID" --run-id "${CONTRACT_ID}" > "$WORK/pcr.json" 2> "$WORK/wiz.err" || {
|
||||||
|
echo "WARNING: Wiz scan failed; falling back to Checkov on the plan" >&2
|
||||||
|
RUNTIME_SCAN_ENGINE="checkov-plan"
|
||||||
|
}
|
||||||
else
|
else
|
||||||
checkov -f "$TF_DIR/main.tf" --framework terraform -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ > "$WORK/checkov.json" 2> "$WORK/checkov.err"
|
RUNTIME_SCAN_ENGINE="checkov-plan"
|
||||||
fi
|
fi
|
||||||
[ -s "$WORK/checkov.json" ] || fail "checkov produced no output"
|
if [ "$RUNTIME_SCAN_ENGINE" = "checkov-plan" ]; then
|
||||||
echo ""
|
echo "--- Wiz not configured → Checkov on the plan (drop-in replacement) ---"
|
||||||
echo "checkov summary: $(python3 -c "import json; d=json.load(open('$WORK/checkov.json')); print(len(d.get('results',{}).get('failed_checks',[])), 'failed,', len(d.get('results',{}).get('passed_checks',[])), 'passed')")"
|
if [ "$QUIET" = "0" ]; then
|
||||||
|
checkov -f "$TF_DIR/tfplan" --framework terraform_plan -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ 2>&1 | tee "$WORK/checkov-plan.json"
|
||||||
echo ""
|
else
|
||||||
echo "=== Step 6: Checkov adapter -> PolicyCheckResult (compliance details) ==="
|
checkov -f "$TF_DIR/tfplan" --framework terraform_plan -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ > "$WORK/checkov-plan.json" 2> "$WORK/checkov-plan.err"
|
||||||
python3 adapters/terraform/policy/checkov_adapter.py "$WORK/checkov.json" "$CONTRACT_ID" > "$WORK/pcr.json" || fail "checkov adapter failed"
|
fi
|
||||||
|
[ -s "$WORK/checkov-plan.json" ] || fail "checkov (plan) produced no output"
|
||||||
|
echo ""
|
||||||
|
echo "checkov (plan) summary: $(python3 -c "import json; d=json.load(open('$WORK/checkov-plan.json')); print(len(d.get('results',{}).get('failed_checks',[])), 'failed,', len(d.get('results',{}).get('passed_checks',[])), 'passed')")"
|
||||||
|
echo ""
|
||||||
|
echo "=== Step 6: Checkov (plan) adapter -> PolicyCheckResult ==="
|
||||||
|
python3 adapters/terraform/policy/checkov_adapter.py "$WORK/checkov-plan.json" "$CONTRACT_ID" > "$WORK/pcr.json" || fail "checkov (plan) adapter failed"
|
||||||
|
fi
|
||||||
|
echo "runtime scan engine: $RUNTIME_SCAN_ENGINE"
|
||||||
python3 -c "
|
python3 -c "
|
||||||
import json
|
import json
|
||||||
pcrs = json.load(open('$WORK/pcr.json'))
|
pcrs = json.load(open('$WORK/pcr.json'))
|
||||||
@@ -494,5 +525,5 @@ source "$ROOT/scripts/run_uptime.sh"
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== PLATFORM E2E OK ==="
|
echo "=== PLATFORM E2E OK ==="
|
||||||
echo "contract -> resolver -> stack -> terraform plan -> Checkov -> confidence ($BAND) -> outbox -> outputs"
|
echo "contract -> resolver -> stack -> Checkov(static) -> terraform plan -> Wiz-or-Checkov(plan) -> confidence ($BAND) -> outbox -> outputs"
|
||||||
exit 0
|
exit 0
|
||||||
+33
-14
@@ -2,8 +2,9 @@
|
|||||||
# scripts/run_postapply.sh — post-Terraform steps for the Nova platform pipeline.
|
# scripts/run_postapply.sh — post-Terraform steps for the Nova platform pipeline.
|
||||||
#
|
#
|
||||||
# Performs steps 5–9 of run_platform.sh (after terraform apply/destroy):
|
# Performs steps 5–9 of run_platform.sh (after terraform apply/destroy):
|
||||||
# 5. Checkov policy scan on the emitted main.tf
|
# 3c. Checkov policy scan on static code (fail-fast, in run_codegen.sh)
|
||||||
# 6. Checkov adapter → PolicyCheckResult (compliance details)
|
# 5. Runtime policy scan on the terraform plan (Wiz-or-Checkov, never both)
|
||||||
|
# 6. Policy scan adapter → PolicyCheckResult (compliance details)
|
||||||
# 7. Confidence signal compute
|
# 7. Confidence signal compute
|
||||||
# 7b. HITL attestation gate (qa/prod/dr only)
|
# 7b. HITL attestation gate (qa/prod/dr only)
|
||||||
# 8. Write evidence event to DynamoDB outbox
|
# 8. Write evidence event to DynamoDB outbox
|
||||||
@@ -92,19 +93,37 @@ else:
|
|||||||
" || { echo "FAIL: HITL gate blocked" >&2; return 1; }
|
" || { echo "FAIL: HITL gate blocked" >&2; return 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "=== Step 5: run Checkov on $TF_DIR/main.tf ==="
|
echo "=== Step 5: runtime policy scan on the terraform plan (Wiz-or-Checkov, never both) ==="
|
||||||
if [ "$QUIET" = "0" ]; then
|
# REQ-250 (v1.21): after terraform plan, run Wiz against the plan when
|
||||||
checkov -f "$TF_DIR/main.tf" --framework terraform -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ 2>&1 | tee "$WORK/checkov.json"
|
# configured; otherwise run Checkov against the plan as a drop-in
|
||||||
|
# replacement. Wiz and Checkov are NEVER both run on the plan. The
|
||||||
|
# static-code Checkov already ran in run_codegen.sh Step 3c (fail-fast).
|
||||||
|
RUNTIME_SCAN_ENGINE=""
|
||||||
|
if [ -n "${WIZ_API_TOKEN:-}" ] || [ -n "${WIZ_API_URL:-}" ]; then
|
||||||
|
RUNTIME_SCAN_ENGINE="wiz"
|
||||||
|
echo "--- Wiz configured (WIZ_API_TOKEN + WIZ_API_URL) → Wiz on the plan ---"
|
||||||
|
python3 adapters/wiz/wiz_adapter.py --plan "$TF_DIR/tfplan" --contract-id "$CONTRACT_ID" --run-id "${CONTRACT_ID}" > "$WORK/pcr.json" 2> "$WORK/wiz.err" || {
|
||||||
|
echo "WARNING: Wiz scan failed; falling back to Checkov on the plan" >&2
|
||||||
|
RUNTIME_SCAN_ENGINE="checkov-plan"
|
||||||
|
}
|
||||||
else
|
else
|
||||||
checkov -f "$TF_DIR/main.tf" --framework terraform -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ > "$WORK/checkov.json" 2> "$WORK/checkov.err"
|
RUNTIME_SCAN_ENGINE="checkov-plan"
|
||||||
fi
|
fi
|
||||||
[ -s "$WORK/checkov.json" ] || { echo "FAIL: checkov produced no output" >&2; exit 1; }
|
if [ "$RUNTIME_SCAN_ENGINE" = "checkov-plan" ]; then
|
||||||
echo ""
|
echo "--- Wiz not configured → Checkov on the plan (drop-in replacement) ---"
|
||||||
echo "checkov summary: $(python3 -c "import json; d=json.load(open('$WORK/checkov.json')); print(len(d.get('results',{}).get('failed_checks',[])), 'failed,', len(d.get('results',{}).get('passed_checks',[])), 'passed')")"
|
if [ "$QUIET" = "0" ]; then
|
||||||
|
checkov -f "$TF_DIR/tfplan" --framework terraform_plan -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ 2>&1 | tee "$WORK/checkov-plan.json"
|
||||||
echo ""
|
else
|
||||||
echo "=== Step 6: Checkov adapter → PolicyCheckResult (compliance details) ==="
|
checkov -f "$TF_DIR/tfplan" --framework terraform_plan -o json --soft-fail --external-checks-dir adapters/terraform/policy/custom_rules/ > "$WORK/checkov-plan.json" 2> "$WORK/checkov-plan.err"
|
||||||
python3 adapters/terraform/policy/checkov_adapter.py "$WORK/checkov.json" "$CONTRACT_ID" > "$WORK/pcr.json" || { echo "FAIL: checkov adapter failed" >&2; exit 1; }
|
fi
|
||||||
|
[ -s "$WORK/checkov-plan.json" ] || { echo "FAIL: checkov (plan) produced no output" >&2; exit 1; }
|
||||||
|
echo ""
|
||||||
|
echo "checkov (plan) summary: $(python3 -c "import json; d=json.load(open('$WORK/checkov-plan.json')); print(len(d.get('results',{}).get('failed_checks',[])), 'failed,', len(d.get('results',{}).get('passed_checks',[])), 'passed')")"
|
||||||
|
echo ""
|
||||||
|
echo "=== Step 6: Checkov (plan) adapter → PolicyCheckResult (compliance details) ==="
|
||||||
|
python3 adapters/terraform/policy/checkov_adapter.py "$WORK/checkov-plan.json" "$CONTRACT_ID" > "$WORK/pcr.json" || { echo "FAIL: checkov (plan) adapter failed" >&2; exit 1; }
|
||||||
|
fi
|
||||||
|
echo "runtime scan engine: $RUNTIME_SCAN_ENGINE"
|
||||||
python3 -c "
|
python3 -c "
|
||||||
import json
|
import json
|
||||||
pcrs = json.load(open('$WORK/pcr.json'))
|
pcrs = json.load(open('$WORK/pcr.json'))
|
||||||
@@ -199,4 +218,4 @@ source "$ROOT/scripts/run_uptime.sh"
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== POST-APPLY OK ==="
|
echo "=== POST-APPLY OK ==="
|
||||||
echo "Checkov → confidence ($BAND) → outbox → outputs → uptime"
|
echo "Checkov(static, pre-plan) → Wiz-or-Checkov(plan) → confidence ($BAND) → outbox → outputs → uptime"
|
||||||
|
|||||||
@@ -173,16 +173,19 @@ class TestDeployPipelineContract:
|
|||||||
contract = yaml.safe_load(fh)
|
contract = yaml.safe_load(fh)
|
||||||
jsonschema.validate(contract, schema)
|
jsonschema.validate(contract, schema)
|
||||||
|
|
||||||
def test_deploy_pipeline_has_six_stages(self):
|
def test_deploy_pipeline_has_required_stages(self):
|
||||||
with open(ROOT / "pipelines/contract.yml") as fh:
|
with open(ROOT / "pipelines/contract.yml") as fh:
|
||||||
contract = yaml.safe_load(fh)
|
contract = yaml.safe_load(fh)
|
||||||
stage_names = [s["name"] for s in contract["stages"]]
|
stage_names = [s["name"] for s in contract["stages"]]
|
||||||
assert "validate-contract" in stage_names
|
assert "validate-contract" in stage_names
|
||||||
assert "resolve-stack" in stage_names
|
assert "resolve-stack" in stage_names
|
||||||
|
assert "checkov-static" in stage_names, "REQ-250: checkov-static stage missing"
|
||||||
assert "terraform-plan" in stage_names
|
assert "terraform-plan" in stage_names
|
||||||
assert "checkov" in stage_names
|
assert "runtime-policy-scan" in stage_names, "REQ-250: runtime-policy-scan stage missing"
|
||||||
assert "confidence" in stage_names
|
assert "confidence" in stage_names
|
||||||
assert "apply" in stage_names
|
assert "apply" in stage_names
|
||||||
|
# The old single 'checkov' stage is gone (split into checkov-static + runtime-policy-scan)
|
||||||
|
assert "checkov" not in stage_names, "old 'checkov' stage should be replaced by checkov-static + runtime-policy-scan"
|
||||||
|
|
||||||
|
|
||||||
class TestL2OutputsResolution:
|
class TestL2OutputsResolution:
|
||||||
|
|||||||
@@ -206,14 +206,15 @@ class TestDeployPipelineContract:
|
|||||||
contract = _load_yaml("pipelines/contract.yml")
|
contract = _load_yaml("pipelines/contract.yml")
|
||||||
jsonschema.validate(contract, schema)
|
jsonschema.validate(contract, schema)
|
||||||
|
|
||||||
def test_deploy_contract_has_nine_stages(self):
|
def test_deploy_contract_has_ten_stages(self):
|
||||||
contract = _load_yaml("pipelines/contract.yml")
|
contract = _load_yaml("pipelines/contract.yml")
|
||||||
stage_names = [s["name"] for s in contract["stages"]]
|
stage_names = [s["name"] for s in contract["stages"]]
|
||||||
assert stage_names == [
|
assert stage_names == [
|
||||||
"validate-contract",
|
"validate-contract",
|
||||||
"resolve-stack",
|
"resolve-stack",
|
||||||
|
"checkov-static",
|
||||||
"terraform-plan",
|
"terraform-plan",
|
||||||
"checkov",
|
"runtime-policy-scan",
|
||||||
"confidence",
|
"confidence",
|
||||||
"apply",
|
"apply",
|
||||||
"publish-outputs",
|
"publish-outputs",
|
||||||
|
|||||||
Reference in New Issue
Block a user