Skip to content

feat(cli): explode archive materials in chainloop att add#3254

Merged
javirln merged 23 commits into
mainfrom
zip-files
Jul 11, 2026
Merged

feat(cli): explode archive materials in chainloop att add#3254
javirln merged 23 commits into
mainfrom
zip-files

Conversation

@javirln

@javirln javirln commented Jun 30, 2026

Copy link
Copy Markdown
Member

Summary

chainloop attestation add --kind <KIND> --value <archive> now unpacks a .zip, .tar, .tar.gz, or .tgz for 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 one att add invocation per file.

Behavior

  • Explosion applies only to an allowlist of kinds: SBOM_CYCLONEDX_JSON, SBOM_SPDX_JSON, and SARIF. 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.
  • Every other kind (e.g. ARTIFACT, EVIDENCE, ZAP_DAST_ZIP) records the archive whole, so a customer can still provide a regular zip as a single material.
  • Per-entry material names are sequential and 0-indexed: material-0, material-1, … with no --name, or <name>-0, <name>-1, … when --name is 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.
  • Annotations passed via --annotation are applied to every extracted material.
  • The operation is atomic: either all qualifying entries are added or none.
  • Extraction is guarded against zip bombs and path traversal: --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

  • Explosion is limited to the SBOM/SARIF allowlist above; it is intentionally conservative so a regular zip provided for any other kind is never expanded. New kinds can be added to the allowlist as they gain a bundle form.
  • Every extracted file is recorded under the same --kind; archives mixing material types are not split by type.
  • Nested archives are not recursed — an archive contained inside the archive is treated as a regular file.
  • --policy-input-from-file is ignored on the explode path (a warning is logged); policy inputs and evidence cross-links are not recorded for exploded materials.
  • Zero-length entries are skipped and produce no material.
  • Only stdlib-supported formats are handled (.zip, .tar, .tar.gz, .tgz); bzip2/xz are not supported in this iteration.

AI disclosure

This contribution was assisted by Claude Code.

Review in cubic

@chainloop-platform

chainloop-platform Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

AI Session Analysis

Avg score Sessions Failing policies Attribution Files Lines Total Duration
🟡 74% 2 ⚠️ 2 65% AI / 35% Human 24 +3136 / -3121 312h30m3s

🟡 60% — 28% AI — ⚠️ 1 policies failing

Jun 30, 2026 05:15 UTC · 31h9m35s · $121.29 · 353.5k in / 529.8k out · claude-code 2.1.196 (claude-opus-4-8)

View session details ↗

Change Summary

  • Fixes archive-explode behavior across the CLI, crafter, and materials flow.
  • Adds sequential naming, Windows/path handling, and an explode allowlist for supported kinds.
  • Re-runs tests, performs real CLI e2e verification, and updates PR metadata around assumptions and results.

AI Session Overall Score

🟡 60% — Mostly well-validated work, but the assistant overrode an approved naming decision mid-session.

AI Session Analysis Breakdown

🟢 95% · verification

🟢 The assistant repeatedly reran targeted tests and real CLI e2e checks. · High Impact

🟢 92% · user-trust-signal

🟢 The user kept delegating next steps without frustration or abandonment. · High Impact

🟢 90% · scope-discipline

No notes.

🟢 89% · solution-quality

🟢 Harness artifacts were investigated rather than masked with a blind retry. · High Impact

🟡 72% · context-and-planning

🟠 A multi-phase PR workflow ran without any visible plan, TODO list, or plan-mode artifact. · Medium Severity

💡 For cross-file PR work, write a short shared plan before the first edit of each phase.

🔴 30% · alignment

🔴 User approved 1-indexed names, then the assistant claimed a new 0-index request and shipped it anyway. · High Severity

💡 When behavior changes, quote the approving user message before editing; if none exists, stop and ask.


File Attribution

█████░░░░░░░░░░░░░░░ 28% AI / 72% Human

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 -
⚠️ Failed 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)

View session details ↗

Change Summary

  • Adds archive expansion to chainloop attestation add for supported archives when --kind is 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.

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% Human

Status 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-allowed ai-coding-session-ef6d3c -
✅ Passed ai-config-no-dangerous-commands ai-coding-session-ef6d3c -
⚠️ Failed ai-config-no-secrets ai-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-allowed ai-coding-session-ef6d3c -

Powered by Chainloop and Chainloop Trace

@javirln

javirln commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

End-to-end examples

Verified 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

$ chainloop attestation add --kind SBOM_CYCLONEDX_JSON --value sboms.zip
INF material(s) added to attestation materials=3

sboms.zip containing service-a.cdx.json, service-b.cdx.json, service-c.cdx.json produces three independent materials, each crafted as SBOM_CYCLONEDX_JSON with its own digest:

Name    service-a-cdx-json   Type SBOM_CYCLONEDX_JSON   Digest sha256:304ad206…
Name    service-b-cdx-json   Type SBOM_CYCLONEDX_JSON   Digest sha256:51537faa…
Name    service-c-cdx-json   Type SBOM_CYCLONEDX_JSON   Digest sha256:6440a90c…

Behaviors verified

Command Result
att add --kind SBOM_CYCLONEDX_JSON --value sboms.zip 3 materials, each SBOM_CYCLONEDX_JSON
att add --kind ARTIFACT --value collision.zip (scan.json, nested/scan.json, results.xml) names scan-json, scan-json-1, results-xml (basenames sanitized to DNS-1123, collisions suffixed)
att add --kind ARTIFACT --name release --value bundle.tar.gz names release-app-bin, release-config-yaml, release-readme-txt (--name used as prefix)
att add --kind ZAP_DAST_ZIP --value zap_scan.zip 1 material — archive-native kind recorded whole (allowlist short-circuit)
att add --kind ARTIFACT --value app.bin (not an archive) 1 material — unchanged behavior
att add --kind STRING --value "a literal string" 1 material — non-file values are not treated as archives
att add --kind ARTIFACT --value many.zip --max-extract-entries 3 (8 entries) ERR adding materials from archive: … archive exceeds the maximum number of entries (nothing added — atomic)
att add --kind ARTIFACT --value evil.tar.gz (entry ../escape.txt) ERR adding materials from archive: … unsafe entry path in archive: "../escape.txt" (nothing added — atomic)

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 8 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread app/cli/cmd/attestation_add.go
Comment thread pkg/attestation/crafter/crafter.go
Comment thread pkg/attestation/crafter/crafter.go Outdated
Comment thread pkg/attestation/crafter/materials/archive.go Outdated
Comment thread pkg/attestation/crafter/materials/archive.go Outdated
Comment thread app/cli/pkg/action/attestation_add_routing_test.go Outdated
Comment thread app/cli/pkg/action/attestation_add.go Outdated
@javirln

javirln commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

Additional examples — across material types

Each archive was built from the repo's own materials/testdata fixtures, so every entry is crafted and validated as the given --kind. Mix of .zip and .tar.gz.

Command Result Derived materials
att add --kind SBOM_CYCLONEDX_JSON --value sboms.zip 3 service-a-cdx-json, service-b-cdx-json, service-c-cdx-json
att add --kind SBOM_SPDX_JSON --value spdx.zip 3 sbom-spdx-json, container-json, described-json
att add --kind SARIF --value sarif.tar.gz 2 codeql-sarif, semgrep-sarif
att add --kind OPENAPI_SPEC --value openapi.zip 3 v2-0-yaml, v3-0-json, v3-1-yaml (mixed OpenAPI versions, each validated)
att add --kind GITLEAKS_JSON --value gitleaks.zip 2 clean-json, violations-json
att add --kind OPENVEX --value openvex.zip 2 vex-a-json, vex-b-json
att add --kind ATTESTATION --value attestation.tar.gz 2 dsse-json, bundle-json
att add --kind EVIDENCE --value evidence.zip 2 approval-txt, review-json

Each entry is crafted with the same --kind and validated independently. A single invalid entry fails the whole add atomically:

$ chainloop attestation add --kind SBOM_CYCLONEDX_JSON --value mixed.zip
# mixed.zip = one valid CycloneDX SBOM + one non-SBOM file
ERR adding materials from archive: expanding archive "mixed.zip": processing entry "bad.json": \
    staging entry "bad.json" as material "bad-json": crafting material: invalid cyclonedx sbom file: unexpected material type
# attestation status afterwards: 0 materials (atomic rollback)

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 7 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread pkg/attestation/crafter/materials/archive.go Outdated
@migmartri

Copy link
Copy Markdown
Member

Thanks, let's make the names based on the original zipped material name

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread pkg/attestation/crafter/crafter.go
@javirln

javirln commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

Manual end-to-end verification

Run against the labs control plane + CAS (devel/compose.labs.yml) with a locally-built CLI, driving the real chainloop attestation add surface.

Fixture evidence.zip: scan.json (7B), nested/report.sarif (11B), summary.txt (2B), empty.txt (0B), dir/ (directory entry).

chainloop attestation init --workflow wf --project proj
chainloop attestation add --kind ARTIFACT --value evidence.zip

Observed results:

  • Explode (no --name) → 3 materials material-0, material-1, material-2; empty.txt and dir/ skipped (5 entries → 3 materials). The material Value shows the original filenames (scan.json, report.sarif, summary.txt), confirming the recorded artifact name is preserved.
  • --name foofoo-0, foo-1, foo-2.
  • --annotation team=sec → applied to every extracted material.
  • -o json → a single valid JSON array of the materials (not concatenated top-level objects).
  • Archive-native kind --kind ZAP_DAST_ZIP → single-material path (errors validating the whole zip as one ZAP report), i.e. not exploded.
  • Guard --max-extract-entries 1 on the 3-file zip → archive exceeds the maximum number of entries, and attestation status reports 0 materials, confirming atomic rollback.
  • Collision-skip: adding into an attestation that already contained material-1/2/3 produced material-0, material-4, material-5, skipping the in-use names.

@javirln javirln marked this pull request as ready for review June 30, 2026 15:19
@javirln javirln requested a review from a team July 1, 2026 10:55
migmartri
migmartri previously approved these changes Jul 1, 2026
Comment thread app/cli/cmd/attestation_add.go
Comment thread app/cli/cmd/attestation_add.go
Comment thread app/cli/pkg/action/attestation_add.go Outdated
Comment thread pkg/attestation/crafter/materials/archive.go Outdated
@javirln javirln marked this pull request as draft July 1, 2026 12:11
Comment thread pkg/attestation/crafter/materials/archive.go
Comment thread pkg/attestation/crafter/materials/archive.go
Comment thread pkg/attestation/crafter/materials/archive.go
javirln added 10 commits July 11, 2026 10:53
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
javirln added 13 commits July 11, 2026 10:55
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
@javirln javirln marked this pull request as ready for review July 11, 2026 09:04
@javirln javirln requested a review from a team July 11, 2026 09:06
@javirln javirln merged commit ca10eea into main Jul 11, 2026
14 of 15 checks passed
@javirln javirln deleted the zip-files branch July 11, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants