feat(cli): inject policy inputs from CSV/JSON files at attestation add#3244
feat(cli): inject policy inputs from CSV/JSON files at attestation add#3244migmartri wants to merge 1 commit into
Conversation
AI Session Analysis
|
| Status | Policy | Material | Messages |
|---|---|---|---|
| ✅ Passed | ai-config-ai-agents-allowed |
ai-coding-session-fe524d |
- |
ai-config-no-dangerous-commands |
ai-coding-session-fe524d |
|
|
| ✅ Passed | ai-config-no-secrets |
ai-coding-session-fe524d |
- |
ai-config-mcp-servers-allowed |
ai-coding-session-fe524d |
MCP server 'claude_ai_Mermaid_Chart' is not in the allowed list |
Powered by Chainloop and Chainloop Trace
There was a problem hiding this comment.
3 issues found across 11 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/cli/pkg/action/attestation_add.go">
<violation number="1" location="app/cli/pkg/action/attestation_add.go:206">
P2: Newline join in buildRuntimeInputs is lossy for CSV/JSON values containing embedded newlines: the comment says "File paths never contain newlines" but the joined values are column data, not file paths.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| if err != nil { | ||
| return nil, fmt.Errorf("extracting %q from %q: %w", pif.Column, pif.File, err) | ||
| } | ||
| joined := strings.Join(values, "\n") |
There was a problem hiding this comment.
P2: Newline join in buildRuntimeInputs is lossy for CSV/JSON values containing embedded newlines: the comment says "File paths never contain newlines" but the joined values are column data, not file paths.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/cli/pkg/action/attestation_add.go, line 206:
<comment>Newline join in buildRuntimeInputs is lossy for CSV/JSON values containing embedded newlines: the comment says "File paths never contain newlines" but the joined values are column data, not file paths.</comment>
<file context>
@@ -160,6 +178,60 @@ func (action *AttestationAdd) Run(ctx context.Context, attestationID, materialNa
+ if err != nil {
+ return nil, fmt.Errorf("extracting %q from %q: %w", pif.Column, pif.File, err)
+ }
+ joined := strings.Join(values, "\n")
+ if existing := runtimeInputs[pif.Input]; existing != "" {
+ runtimeInputs[pif.Input] = existing + "\n" + joined
</file context>
There was a problem hiding this comment.
Reworded. The newline join matches the engine's existing multi-value encoding (it splits inputs back on newlines and commas), so individual values must not embed those delimiters — the same constraint as contract with: arguments. Path globs, the intended use, never do. This is inherent to the input mechanism rather than specific to this code path.
🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri
There was a problem hiding this comment.
Got it — that boundary makes the original comment too broad here. Since the engine already re-splits multi-value inputs on newlines and commas, this isn’t a buildRuntimeInputs-specific lossiness bug; it’s the same delimiter constraint as contract with: arguments, and path globs fit that shape.
Thanks for the feedback! I've saved this as a new learning to improve future reviews.
df975e0 to
61552c0
Compare
| --kind string kind of the material to be recorded: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] | ||
| --name string name of the material as shown in the contract | ||
| --no-strict-validation skip strict schema validation for structured materials (SBOM_CYCLONEDX_JSON, OPENAPI_SPEC, ASYNCAPI_SPEC, OSSF_SCORECARD_JSON) | ||
| --policy-input-from-file stringArray feed a policy input from a column of a CSV or JSON file, in the format <input>[:<column>]=<file> (e.g. ignored_paths:Path=exception.csv); repeatable. The file is also recorded as EVIDENCE. |
There was a problem hiding this comment.
I like ignored_paths=exceptions.csv:path better.
There was a problem hiding this comment.
Agreed — that is the implemented format: --policy-input-from-file ignored_paths=exception.csv:Path (the :<column> is optional and defaults to the input name). The column is the segment after the last :, and since a column name never contains a path separator, a Windows drive letter (C:\...) or URL scheme (https://) is not mistaken for a column.
🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri
6ce17d5 to
784ee26
Compare
| } | ||
|
|
||
| func extractCSVColumn(content []byte, column string) ([]string, error) { | ||
| report, err := sigcheck.Parse(content) |
There was a problem hiding this comment.
shouldn't we move the CSV parser to a generic package or use that one generically? now it looks like we can only support sigcheck.
There was a problem hiding this comment.
Done — moved the parser out of the sigcheck subpackage into a generic pkg/tabular (Parse returning a Table, with a Column(name) case-insensitive extractor). The sigcheck material crafter, the JSON ingestion in crafting_state.go, and this CLI extraction all consume it now, and the old materials/sigcheck subpackage is removed.
🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri
| // a bare array of strings, an array of objects (the column field of each), or | ||
| // an object mapping the column to an array of strings. The column is matched | ||
| // only against top-level keys; nested paths are not interpreted. | ||
| func extractJSONColumn(content []byte, column string) ([]string, error) { |
There was a problem hiding this comment.
isn't there a better way of doing this maybe by marshalling to the expected object?
There was a problem hiding this comment.
Reworked: the array-of-objects shape now decodes straight into []map[string]string (dropped the json.RawMessage + manual string decode), and the object→array shape keeps json.RawMessage only for the key lookup before decoding the selected value into a typed []string, so sibling fields of other types do not break parsing. matchKey is now a single generic helper. Trade-off: array-of-objects now requires string-valued fields (documented in the code and spec).
🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri
Add a repeatable --policy-input-from-file <input>[:<column>]=<file> flag to `chainloop attestation add` that extracts... The source file is recorded as an EVIDENCE material, cross-linked to the evaluated material via a generic chainloop.m... CSV parsing reuses the existing sigcheck parser; JSON accepts a string array, an array of objects, or an object mappi... Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev> Chainloop-Trace-Sessions: fe524d3a-ae31-482c-8675-45df3bfe4d81
784ee26 to
8a6f47a
Compare
Closes #3250
Summary
Adds a repeatable
--policy-input-from-file <input>[:<column>]=<file>flag tochainloop attestation addthat sources a policy input from a column of a CSV or JSON file and injects it during material policy evaluation. The primary use case is feeding theignored_pathsinput of the Sysinternals sigcheck binary-signing policies from a large, data-driven exemption list, but the flag is generic over both the input name and the source column.Details
EVIDENCEmaterial and cross-linked to the evaluated material via a genericchainloop.material.referencesannotation (by material name, modeled on the OCI referrers API). The same primitive can connect other material kinds in the future.PolicyEvaluationgains aruntime_input_overridesfield listing which input names were supplied at runtime; the effective merged values remain inwith.AI assistance
This change was developed with the assistance of Claude Code. Affected commits carry an
Assisted-by: Claude Codetrailer.🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri