feat(attestation-type): custom attestation type summaries - #1099
Conversation
The backend accepts an optional `summary` list at the top level of the
create/update endpoint's data_json body — ordered {name, expression}
entries whose expressions are jq, evaluated against attestation data.
Because it lives in that JSON blob rather than the uploaded schema file,
there was no user-facing way to set it.
Adds --summary-json, taking the list as JSON so it maps 1:1 to the API.
The repeatable --summary "Name=.expr" alternative was rejected: jq
expressions contain both `=` and `,`, and cobra's StringToStringVar
CSV-splits values and returns an unordered map, where summary is an
ordered list.
Parsing validates that each entry has a non-empty name and expression,
reporting the offending entry by 1-based index. Without that, a typo
such as {"name":"X","expr":".y"} unmarshals silently into an empty
expression and ships a broken summary to the API, surfacing far from
its cause. DisallowUnknownFields would catch that more directly but
would hard-reject payloads if the backend later adds an optional field
to the entry shape, which it has reserved the right to do.
The field uses omitempty, so existing invocations produce a byte-
identical request. Verified against the local server that the endpoint
replaces rather than merges: re-running without the flag clears an
existing summary, so omitempty loses no expressiveness.
Refs #1097
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tput
The summary was write-only in the default view: --summary-json could set
it, but `kosli get attestation-type X` printed type_schema and evaluator
with no summary branch, so the only way to see what had been set was
--output json. Combined with the endpoint's full-replace semantics, a
later create without the flag silently cleared the summary with nothing
in the table output to show it had gone.
Prints entries under a Summary block after Evaluator, one name/expression
pair per line, in the order the server returns them.
Types created without a summary have a null "summary" in the response,
which fails the []interface{} type assertion and prints nothing, so their
output is unchanged — the existing golden files still pass untouched.
Refs #1097
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @FayeSGW's task in 1m 31s —— View job PR Review: custom attestation type summaries
Static checks: This is a well-executed, well-tested change. The flag-shape reasoning in the PR body is sound: What's good
Minor points (non-blocking)
TestingGolden files, unit tests for Overall: clean, focused, mergeable. 👍 |
Snyk Dependency Test fails on every branch, flagging seven vulnerabilities in the Go standard library at 1.26.5: High DoS in std/crypto/tls CVE-2026-56862 High DoS in std/net/http CVE-2026-56858 High DoS in std/net/url CVE-2026-56859 High Uncaught Exception in std/net CVE-2026-56860 High Uncontrolled Recursion in std/encoding/asn1 High Uncontrolled Recursion in std/encoding/xml CVE-2026-56853 Medium XSS in std/html/template Snyk derives the stdlib version from the `go` directive, so go.mod was the only place needing a change — .go-version and the Dockerfile's GO_VERSION arg both float on 1.26 and pick up the patch release on their own. Verified locally: `snyk test --policy-path=.snyk` reports 7 issues and 940 vulnerable paths on 1.26.5, and no vulnerable paths on 1.26.6. Full suite and lint pass on the new toolchain. Not caused by this branch — the unrelated empty-flag-audit branch fails the same check. Included here to unblock CI; can be split into its own PR if preferred. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
sami-alajrami
left a comment
There was a problem hiding this comment.
I assume expression validation to be a valid JQ is still happening on the API?
Closes #1097
What
Adds
--summary-jsontokosli create attestation-type, and shows the summary inkosli get attestation-typetable output.The backend accepts an optional
summarylist at the top level of the create/update endpoint'sdata_jsonbody — ordered{name, expression}entries whose expressions are jq, evaluated against attestation data. Because it lives in that JSON blob rather than the uploaded schema file, there was no user-facing way to set it.Why this flag shape
The issue floated three options. This takes the JSON-passthrough one, which maps 1:1 to the API.
The repeatable
--summary "Name=.expr"alternative was rejected because jq expressions contain both=and,. Note the existing key=value idiom in this repo (--annotate,--set,--link) uses cobra'sStringToStringVar, which would have been wrong here twice over: it CSV-splits values, so an expression like[.a, .b] | map(select(.x == 1)) | lengthgets mangled, and it returns an unordered map wheresummaryis an ordered list.Notes for review
Validation. Each entry must have a non-empty name and expression, reported by 1-based index. Without that, a typo such as
{"name":"X","expr":".y"}unmarshals silently into an empty expression and ships a broken summary to the API, surfacing far from its cause.DisallowUnknownFieldswould catch that more directly, but would hard-reject payloads if the backend later adds an optional field to the entry shape — which the issue explicitly reserves the right to do.omitemptyis safe. Verified against a local server that the endpoint replaces rather than merges: creating a type with a summary and re-running without the flag produces a version withsummary: null. So omitting the flag clears the summary, and[]vs "omitted" being indistinguishable on the wire loses no expressiveness. Existing invocations produce a byte-identical request.Second commit came from code review — the summary was write-only in the default view, visible only via
--output json. Combined with full-replace semantics, a later create without the flag silently cleared it with nothing in the table to show it had gone.Testing
Full suite green: 2205 tests, 51 skipped.
make lintclean.Beyond the goldens, I queried the local server after the suite to confirm the data actually lands rather than just being accepted: the comma/
==expression stores byte-identical, andsummarycoexists with both a jqevaluatorand an uploadedtype_schema.Types created without a summary have a null
summaryin the response, which fails the[]interface{}assertion and prints nothing — existing golden files pass untouched.Known gap
No test pins
CreateAttestationTypePayloadto the OpenAPI contract — it is not in theopenapiContract_test.godrift-guard registry, so a server-side rename would leave the suite green while the summary is silently dropped. The new get-with-summary golden partly mitigates this by round-tripping the field through a real server. Happy to add the registry entry here or as a follow-up.🤖 Generated with Claude Code