Skip to content

feat(cli): inject policy inputs from CSV/JSON files at attestation add#3244

Open
migmartri wants to merge 1 commit into
mainfrom
feat/policy-input-from-file
Open

feat(cli): inject policy inputs from CSV/JSON files at attestation add#3244
migmartri wants to merge 1 commit into
mainfrom
feat/policy-input-from-file

Conversation

@migmartri

@migmartri migmartri commented Jun 26, 2026

Copy link
Copy Markdown
Member

Closes #3250

Summary

Adds a repeatable --policy-input-from-file <input>[:<column>]=<file> flag to chainloop attestation add that 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 the ignored_paths input 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

  • Runtime inputs are merged additively onto contract-declared arguments through a dedicated channel, kept separate from policy-group bindings so group evaluations are not mis-flagged as runtime overrides.
  • The source file is recorded as an EVIDENCE material and cross-linked to the evaluated material via a generic chainloop.material.references annotation (by material name, modeled on the OCI referrers API). The same primitive can connect other material kinds in the future.
  • PolicyEvaluation gains a runtime_input_overrides field listing which input names were supplied at runtime; the effective merged values remain in with.
  • CSV parsing reuses the existing sigcheck parser (BOM decoding, comma/tab detection, case-insensitive header match); JSON accepts a bare string array, an array of objects, or an object mapping the column to an array.

AI assistance

This change was developed with the assistance of Claude Code. Affected commits carry an Assisted-by: Claude Code trailer.

🤖 Posted by Maximus bot (Claude Code) on behalf of @migmartri

Review in cubic

@chainloop-platform

chainloop-platform Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

AI Session Analysis

Avg score Sessions Failing policies Attribution Files Lines Total Duration
🔴 40% 1 ⚠️ 2 78% AI / 22% Human 17 +1150 / -339 6h13m19s

🔴 40% — 78% AI — ⚠️ 2 policies failing

Jun 26, 2026 10:19 UTC · 6h13m19s · $89.23 · 295.9k in / 686.0k out · claude-code 2.1.191 (claude-opus-4-8)

View session details ↗

Change Summary

  • Adds --policy-input-from-file support for CSV/JSON inputs during attestation add.
  • Records runtime input provenance and links uploaded evidence materials.
  • Extracts shared tabular parsing into pkg/tabular and updates affected consumers.

AI Session Overall Score

🔴 40% — Two red lanes: alignment drift and repeated workflow bypasses outweigh otherwise solid testing.

AI Session Analysis Breakdown

🟢 88% · user-trust-signal

🟢 The user stayed engaged through late corrections without a frustration spiral. · Medium Impact

🟢 84% · scope-discipline

🟢 The touched surface stayed centered on the requested feature and follow-up review fixes. · High Impact

🟡 78% · verification

🟢 Focused tests were added and rerun across the new input-file paths. · High Impact

🟠 Automated tests ran often, but the user never explicitly confirmed the final shipped behavior. · Medium Severity

💡 When the user is active in-session, capture one concrete manual confirmation before declaring completion.

🟡 74% · context-and-planning

🟠 A multi-package implementation proceeded without a visible shared plan despite strong upstream design context. · Medium Severity

💡 For large changes, publish a short plan before editing so later corrections have a stable frame.

🔴 30% · solution-quality

🔴 Repeated --no-verify amend commits bypassed commit-time checks while the PR was still changing. · High Severity

💡 Treat --no-verify as exceptional and user-approved only; surface hook failures instead of skipping them.

🔴 26% · alignment

🔴 The AI moved from planning into implementation without the explicit approval the carry-over summary required. · High Severity

💡 When a carried-over summary leaves a hard approval gate open, stop and get that approval before editing.

🔴 The AI declared the feature verified before CI follow-up exposed failing workflows and missing generated files. · High Severity

💡 Do not call a change verified while known validation steps are still pending or already failing.


File Attribution

███████████████░░░░░ 78% AI / 22% Human

Status Attribution File Lines
created ai app/cli/pkg/action/policy_input_file.go +197 / -0
created ai app/cli/pkg/action/policy_input_file_test.go +195 / -0
created ai pkg/tabular/tabular.go +170 / -0
created ai pkg/tabular/tabular_test.go +169 / -0
deleted human pkg/attestation/crafter/materials/sigcheck/sigcheck_test.go +0 / -155
deleted human pkg/attestation/crafter/materials/sigcheck/sigcheck.go +0 / -146
modified ai app/cli/pkg/action/attestation_add.go +106 / -6
created ai pkg/policies/runtime_inputs_test.go +80 / -0
modified ai pkg/policies/policies.go +66 / -9
created ai app/cli/pkg/action/attestation_add_test.go +63 / -0
modified ai app/cli/cmd/attestation_add.go +42 / -1
modified ai pkg/attestation/crafter/crafter.go +35 / -8
modified human app/cli/documentation/cli-reference.mdx +11 / -10
modified ai pkg/attestation/crafter/api/attestation/v1/crafting_state.proto +6 / -0
modified ai pkg/attestation/crafter/materials/materials.go +6 / -0
modified ai pkg/attestation/crafter/api/attestation/v1/crafting_state.go +2 / -2
modified ai pkg/attestation/crafter/materials/sigcheck.go +2 / -2

Policies (4, 2 failing)

Status Policy Material Messages
✅ Passed ai-config-ai-agents-allowed ai-coding-session-fe524d -
⚠️ Failed ai-config-no-dangerous-commands ai-coding-session-fe524d
  • Forbidden bash pattern /--no-verify/ matched command: cd /home/migmartri/work/chainloop/chainloop/.claude/worktrees/cryptic-noodling-globe; D=/tmp/claude-1000/-home-migmartri-work-chainloop-chainloop/fe524d3a-ae31-...
  • Forbidden bash pattern /--no-verify/ matched command: cd /home/migmartri/work/chainloop/chainloop/.claude/worktrees/cryptic-noodling-globe; git add -A; echo "=== staged delta vs HEAD ==="; git status --short; echo ...
  • Forbidden bash pattern /--no-verify/ matched command: cd /home/migmartri/work/chainloop/chainloop/.claude/worktrees/cryptic-noodling-globe; git add app/cli/cmd/attestation_add.go app/cli/documentation/cli-reference...
  • Forbidden bash pattern /--no-verify/ matched command: cd /home/migmartri/work/chainloop/chainloop/.claude/worktrees/cryptic-noodling-globe; git add app/cli/pkg/action/policy_input_file.go app/cli/pkg/action/attesta...
✅ Passed ai-config-no-secrets ai-coding-session-fe524d -
⚠️ Failed 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

@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.

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

Comment thread app/cli/pkg/action/policy_input_file.go
Comment thread app/cli/pkg/action/attestation_add.go Outdated
if err != nil {
return nil, fmt.Errorf("extracting %q from %q: %w", pif.Column, pif.File, err)
}
joined := strings.Join(values, "\n")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

@migmartri migmartri force-pushed the feat/policy-input-from-file branch 3 times, most recently from df975e0 to 61552c0 Compare June 26, 2026 13:35
Comment thread app/cli/documentation/cli-reference.mdx Outdated
--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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like ignored_paths=exceptions.csv:path better.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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

@migmartri migmartri force-pushed the feat/policy-input-from-file branch 2 times, most recently from 6ce17d5 to 784ee26 Compare June 26, 2026 15:33
Comment thread app/cli/pkg/action/policy_input_file.go Outdated
}

func extractCSVColumn(content []byte, column string) ([]string, error) {
report, err := sigcheck.Parse(content)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

isn't there a better way of doing this maybe by marshalling to the expected object?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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
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.

Support data-driven policy inputs (e.g. ignored_paths) sourced from CSV/JSON files

2 participants