Conversation
AI Session Analysis
|
| Status | Attribution | File | Lines |
|---|---|---|---|
| deleted | human | docs/superpowers/plans/2026-06-29-explode-archive-materials.md |
+0 / -1105 |
| deleted | human | .superpowers/sdd/final-fix-report.md |
+0 / -228 |
| modified | ai | pkg/attestation/crafter/materials/archive.go |
+98 / -51 |
| modified | ai | pkg/attestation/crafter/crafter_test.go |
+57 / -43 |
| modified | ai | pkg/attestation/crafter/materials/archive_test.go |
+63 / -18 |
| modified | ai | app/cli/pkg/action/attestation_add_routing_test.go |
+33 / -31 |
| modified | ai | app/cli/pkg/action/attestation_add.go |
+20 / -35 |
| modified | ai | app/cli/cmd/attestation_add.go |
+27 / -18 |
| modified | ai | pkg/attestation/crafter/crafter.go |
+24 / -12 |
| modified | human | app/cli/documentation/cli-reference.mdx |
+1 / -1 |
| modified | ai | app/cli/cmd/output/output.go |
+1 / -0 |
| deleted | human | pkg/attestation/crafter/testdata/two-files.zip |
+0 / -0 |
Policies (4, 1 failing)
| Status | Policy | Material | Messages |
|---|---|---|---|
| ✅ Passed | ai-config-ai-agents-allowed |
ai-coding-session-da72e1 |
- |
| ✅ Passed | ai-config-no-dangerous-commands |
ai-coding-session-da72e1 |
- |
ai-config-no-secrets |
ai-coding-session-da72e1 |
Potential secret (JWT) found in session content [turn=811, source=tool_result, line=7, value=eyJhbGci...ooG8] | |
| ✅ Passed | ai-config-mcp-servers-allowed |
ai-coding-session-da72e1 |
- |
🟢 89% — 81% AI — ⚠️ 1 policies failing
-
Jun 29, 2026 15:36 UTC · 281h20m28s · $105.50 · 123.2k in / 669.3k out · claude-code 2.1.195 (claude-opus-4-8)
Change Summary
-
- Adds archive expansion to
chainloop attestation addfor supported archives when--kindis set. - Adds traversal and size guards, atomic crafter batching, derived naming, and extraction-limit CLI flags.
- Adds tests, regenerates CLI docs, performs live CLI verification, and rebases the branch onto
main.
- Adds archive expansion to
AI Session Overall Score
-
🟢 89% — Well-planned, in-scope work with strong validation, but no explicit user confirmation.
AI Session Analysis Breakdown
-
🟢 96% · context-and-planning
-
🟢 A full implementation plan landed before coding began. · High Impact
🟢 94% · alignment
-
No notes.
🟢 93% · user-trust-signal
-
No notes.
🟢 91% · scope-discipline
-
No notes.
🟢 90% · solution-quality
-
No notes.
🟡 78% · verification
-
🟢 The rebased tree was rebuilt, vetted, and re-tested before analysis. · High Impact
🟢 The live CLI was exercised across multiple archive and material edge cases. · High Impact
🟠 The AI supplied strong test and runtime evidence, but the user never explicitly confirmed the feature worked. · Medium Severity
💡 When runtime verification is complete, ask for an explicit go/no-go before moving to the next task.
-
File Attribution
████████████████░░░░81% AI / 19% HumanStatus Attribution File Lines deleted ai docs/superpowers/plans/2026-06-29-explode-archive-materials.md+1105 / -1105 modified ai pkg/attestation/crafter/materials/archive.go+420 / -63 deleted human .superpowers/sdd/final-fix-report.md+228 / -228 modified ai pkg/attestation/crafter/crafter_test.go+334 / -43 modified ai pkg/attestation/crafter/materials/archive_test.go+304 / -18 modified ai pkg/attestation/crafter/crafter.go+173 / -30 modified ai app/cli/pkg/action/attestation_add_routing_test.go+113 / -31 modified ai app/cli/pkg/action/attestation_add.go+81 / -37 modified ai app/cli/cmd/attestation_add.go+50 / -23 modified human app/cli/documentation/cli-reference.mdx+3 / -1 modified human app/cli/cmd/output/output.go+1 / -0 deleted human pkg/attestation/crafter/testdata/two-files.zip+0 / -0
Policies (4, 1 failing)
Status Policy Material Messages ✅ Passed ai-config-ai-agents-allowedai-coding-session-ef6d3c- ✅ Passed ai-config-no-dangerous-commandsai-coding-session-ef6d3c- ⚠️ Failedai-config-no-secretsai-coding-session-ef6d3c- Potential secret (JWT) found in session content [turn=691, source=tool_result, line=11, value=eyJhbGci...H_fY]
- Potential secret (JWT) found in session content [turn=708, source=tool_result, line=14, value=eyJhbGci...6sp0]
- Potential secret (Quoted API key/password) found in session content [turn=691, source=tool_result, line=11, value=token = ..._fY']
✅ Passed ai-config-mcp-servers-allowedai-coding-session-ef6d3c- -
Powered by Chainloop and Chainloop Trace
End-to-end examplesVerified by running the locally built CLI against a control plane (dry-run / local state). Server side is unchanged, so this needs only a CLI build. Explode a zip into one material per file
Behaviors verified
|
There was a problem hiding this comment.
7 issues found across 8 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Additional examples — across material typesEach archive was built from the repo's own
Each entry is crafted with the same |
There was a problem hiding this comment.
1 issue found across 7 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
Thanks, let's make the names based on the original zipped material name |
There was a problem hiding this comment.
All reported issues were addressed across 4 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Manual end-to-end verificationRun against the labs control plane + CAS ( Fixture Observed results:
|
Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4
Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4 Signed-off-by: Javier Rodriguez <javier@chainloop.dev>
Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4
Strengthen path validation in archive extraction to explicitly reject: - Absolute paths (e.g., /etc/passwd) - Relative path traversal attempts (e.g., ../ or foo/../..) Added direct unit test of safeArchivePath and integration test via tar with traversal entry to verify rejection. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4
Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4
Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4
Adds an exported AddMaterialsFromArchive method on the Crafter that walks every entry in a zip/tar/tar.gz archive, stages each entry as an independent material via stageMaterial, and commits the in-memory state in a single stateManager.Write call. If any entry fails, previously staged entries are rolled back so no partial state is ever persisted. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4
…ry on archive expand Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4
Add MaxExtractEntries/MaxExtractSize to AttestationAddOpts and wire them into AttestationAdd with defaults from materials.DefaultArchiveLimits(). Change Run to return ([]*AttestationStatusMaterial, error) and insert an explode branch before the single-material switch: when --kind is set and the value is a non-archive-native archive, delegate to crafter.AddMaterialsFromArchive and return N results; otherwise the single-material path continues unchanged and its result is wrapped in a 1-element slice. Add shouldExplode helper and TestShouldExplode. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4
Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4
Add TestAddMaterialsFromArchiveBehavior to crafter_test.go covering five end-to-end scenarios via AddMaterialsFromArchive: name collision suffixing (scan-json / scan-json-1), name prefix, skipping dirs and symlinks in tar.gz, path-traversal rejection with atomic rollback, and tar.gz happy path with two materials. Fixtures are built programmatically with buildZip/buildTarGz helpers using t.TempDir(); no binary fixtures committed. Also regenerate app/cli/documentation/cli-reference.mdx to include the --max-extract-entries and --max-extract-size flags added in earlier tasks. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4
… tests Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4
…uards - detectByMagic now returns (ArchiveNone, nil) when os.Open fails so non-file material values (STRING, CONTAINER_IMAGE) never produce a spurious "no such file" error through the shouldExplode path. - safeArchivePath drops the over-broad strings.Contains(name, "..") early-return that wrongly rejected legitimate filenames like foo..bar.json; traversal detection now relies solely on path.Clean against a virtual root (/root/), which is the only reliable check. - Add a Warn log when --policy-input-from-file is supplied with an archive value so users know evidence cross-links are skipped on the explode path. - Name per-entry temp files with the allocated unique material name (matName) instead of filepath.Base(name) to eliminate basename collisions; remove each temp file immediately after staging to keep disk usage bounded. - Extend TestDetectArchive with .tar and .tgz cases; add writeTar helper mirroring writeTarGz without the gzip layer. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: ef6d3cdb-5a23-445c-b39a-510b659023e4
Render multi-material explode output as a single JSON array so --output json stays a parseable document; only the table renderer is emitted per material. Drop the unused size parameter from the archive visit signature, document the intentional zero-length-entry skips and the zip symlink-detection limitation, and correct the --max-extract-size default label to 1GiB. Build the crafter archive test fixture in-process and drop the checked-in binary blob and SDD process artifacts. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: da72e107-14e9-4da1-add0-28004f542628, ef6d3cdb-5a23-445c-b39a-510b659023e4
Validate --max-extract-size before the uint64->int64 cast so oversized values are rejected instead of wrapping negative. Check the per-entry temp file Close error before staging so a failed flush never produces an incomplete material, and write each entry into its own temp subdirectory under its original basename so the recorded artifact filename keeps the real name rather than the sanitized material key. Reject Windows drive-letter absolute paths in safeArchivePath, and only swallow not-found errors in detectByMagic so permission/I/O errors surface. Treat non-positive extraction limits as use-default. Assert the exact detected archive format and the preserved filename in tests. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: da72e107-14e9-4da1-add0-28004f542628, ef6d3cdb-5a23-445c-b39a-510b659023e4
A non-file material value whose first path segment is an existing regular file (e.g. a CONTAINER_IMAGE ref like "registry/app:v1") yields ENOTDIR on open rather than not-found. Swallow it alongside fs.ErrNotExist so such values detect as non-archive instead of failing detection; genuine permission/I/O errors still surface. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: da72e107-14e9-4da1-add0-28004f542628, ef6d3cdb-5a23-445c-b39a-510b659023e4
…tics filepath.Base treats backslash as a separator only on Windows, so an entry name embedding a literal backslash produced different material names across platforms. Add materials.ArchiveEntryBaseName, which normalizes separators and uses path.Base, and derive the per-entry basename through it so exploded material names are identical on every OS. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: da72e107-14e9-4da1-add0-28004f542628, ef6d3cdb-5a23-445c-b39a-510b659023e4
Per-entry material names are now sequential rather than derived from the entry basename: material-1, material-2, … with no --name, or <name>-1, <name>-2, … when --name is provided (used as the prefix). The original entry filename is still preserved in the recorded artifact metadata. Replaces NameAllocator.Allocate with AllocateSequential. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: da72e107-14e9-4da1-add0-28004f542628, ef6d3cdb-5a23-445c-b39a-510b659023e4
Exploded archive materials are now numbered from 0 (material-0, material-1, … or <name>-0, <name>-1, …) instead of 1. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: da72e107-14e9-4da1-add0-28004f542628, ef6d3cdb-5a23-445c-b39a-510b659023e4
Avoid the repeated "material" string literal (goconst). Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: da72e107-14e9-4da1-add0-28004f542628, ef6d3cdb-5a23-445c-b39a-510b659023e4
Fixes revive redefines-builtin-id on the archive behavior test. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: da72e107-14e9-4da1-add0-28004f542628, ef6d3cdb-5a23-445c-b39a-510b659023e4
Drop the redundant explode bool from shouldExplode (derivable from the returned ArchiveFormat) and branch on the format at the call site. Reuse output.EncodeOutput for the multi-material result instead of a hand-rolled format switch, adding []*AttestationStatusMaterial to the tabulated-data union so the slice renders as a single JSON array. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: da72e107-14e9-4da1-add0-28004f542628, ef6d3cdb-5a23-445c-b39a-510b659023e4
Address review feedback: explode only kinds with a meaningful bundle form (SBOM_CYCLONEDX_JSON, SBOM_SPDX_JSON, SARIF) instead of exploding any archive that is not archive-native. Every other kind (ARTIFACT, EVIDENCE, ZAP_DAST_ZIP, …) records the archive whole, so a regular zip provided as a single material is never expanded. Replaces the archive-native denylist with an explodable-kind allowlist. Also consolidate name sanitization: SanitizeMaterialName now returns the bare sanitized component (empty when nothing remains) and callers supply their fallback, so the action layer's sanitizeMaterialNamePart reuses it instead of duplicating the logic. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: da72e107-14e9-4da1-add0-28004f542628, ef6d3cdb-5a23-445c-b39a-510b659023e4
Summary
chainloop attestation add --kind <KIND> --value <archive>now unpacks a.zip,.tar,.tar.gz, or.tgzfor the kinds that have a meaningful "bundle of the same kind" archive form and records each contained regular file as its own material of the given kind, instead of requiring oneatt addinvocation per file.Behavior
SBOM_CYCLONEDX_JSON,SBOM_SPDX_JSON, andSARIF. For one of these, when the value resolves to a supported archive, each contained regular file is added as an individual material of that kind.ARTIFACT,EVIDENCE,ZAP_DAST_ZIP) records the archive whole, so a customer can still provide a regular zip as a single material.material-0,material-1, … with no--name, or<name>-0,<name>-1, … when--nameis provided (used as the prefix, sanitized to DNS-1123). Names already present in the attestation are skipped. The recorded artifact filename preserves the original entry basename.--annotationare applied to every extracted material.--max-extract-entries(default 10000) and--max-extract-size(default 1GiB) are enforced while streaming, and directory, symlink, absolute, and path-traversal entries are skipped or rejected.For non-explodable kinds, non-archive values, and no
--kind, behavior is unchanged. There are no server-side, contract, or gRPC changes.Assumptions and scope
--kind; archives mixing material types are not split by type.--policy-input-from-fileis ignored on the explode path (a warning is logged); policy inputs and evidence cross-links are not recorded for exploded materials..zip,.tar,.tar.gz,.tgz); bzip2/xz are not supported in this iteration.AI disclosure
This contribution was assisted by Claude Code.