From a8b8757aa226628598cebbd7ed72683d40e7f415 Mon Sep 17 00:00:00 2001 From: Vibhav Bobade Date: Fri, 27 Mar 2026 18:23:11 +0530 Subject: [PATCH 1/2] fix(ci): add least-privilege permissions to workflow files Add top-level permissions blocks following the two-tier permission pattern recommended by OpenSSF Scorecard: - stale.yml: add `permissions: {}` at workflow level (job already has issues: write + pull-requests: write) - build_external_container_images.yaml: move `packages: write` from workflow level to job level; set workflow level to `permissions: read-all` scm_configuration_check.yaml already had `permissions: read-all` at workflow level so no change was needed. Fixes chainloop-dev/chainloop#2841 Signed-off-by: Vibhav Bobade --- .github/workflows/build_external_container_images.yaml | 4 +++- .github/workflows/codeql.yml | 1 - .github/workflows/stale.yml | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_external_container_images.yaml b/.github/workflows/build_external_container_images.yaml index 7876dbfcb..8c90f59bf 100644 --- a/.github/workflows/build_external_container_images.yaml +++ b/.github/workflows/build_external_container_images.yaml @@ -5,12 +5,14 @@ on: permissions: contents: read - packages: write jobs: build_and_push_images: name: Build and Push ${{ matrix.image.name }} Image runs-on: ubuntu-latest + permissions: + contents: read + packages: write strategy: matrix: image: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 56950d13f..fec86ea7c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -11,7 +11,6 @@ on: permissions: contents: read - id-token: write # required for SLSA attestation jobs: analyze: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index b28cd78ff..2e20ae0e4 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -5,6 +5,8 @@ on: - cron: '30 1 * * *' workflow_dispatch: +permissions: {} + jobs: close-issues: runs-on: ubuntu-latest From dcdd23fe95a76aa9c927173ce624574b79d8049b Mon Sep 17 00:00:00 2001 From: Vibhav Bobade Date: Fri, 27 Mar 2026 18:23:29 +0530 Subject: [PATCH 2/2] fix(crafter): use deterministic filename for AI config collector Replace os.CreateTemp (which generates a random suffix) with a deterministic filename derived from the config's content hash (ConfigHash[:12]). This ensures that retries produce the same temp file path, preventing duplicate CAS uploads when the attestation is retried due to remote state conflicts. Fixes: chainloop-dev/chainloop#2907 Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Vibhav Bobade --- .../crafter/collector_aiagentconfig.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/pkg/attestation/crafter/collector_aiagentconfig.go b/pkg/attestation/crafter/collector_aiagentconfig.go index 4b67f97e6..901513fcb 100644 --- a/pkg/attestation/crafter/collector_aiagentconfig.go +++ b/pkg/attestation/crafter/collector_aiagentconfig.go @@ -20,6 +20,7 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "sort" schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" @@ -99,20 +100,16 @@ func (c *AIAgentConfigCollector) uploadAgentConfig( return fmt.Errorf("marshaling AI agent config for %s: %w", agentName, err) } - tmpFile, err := os.CreateTemp("", fmt.Sprintf("ai-agent-config-%s-*.json", agentName)) - if err != nil { - return fmt.Errorf("creating temp file: %w", err) - } - defer os.Remove(tmpFile.Name()) - - if _, err := tmpFile.Write(jsonData); err != nil { - tmpFile.Close() + // Use a deterministic filename based on the config hash so that retries + // produce the same file path and avoid duplicate CAS uploads. + tmpPath := filepath.Join(os.TempDir(), fmt.Sprintf("ai-agent-config-%s-%s.json", agentName, data.ConfigHash[:12])) + if err := os.WriteFile(tmpPath, jsonData, 0o600); err != nil { return fmt.Errorf("writing temp file: %w", err) } - tmpFile.Close() + defer os.Remove(tmpPath) materialName := fmt.Sprintf("ai-agent-config-%s", agentName) - if _, err := cr.AddMaterialContractFree(ctx, attestationID, schemaapi.CraftingSchema_Material_CHAINLOOP_AI_AGENT_CONFIG.String(), materialName, tmpFile.Name(), casBackend, nil); err != nil { + if _, err := cr.AddMaterialContractFree(ctx, attestationID, schemaapi.CraftingSchema_Material_CHAINLOOP_AI_AGENT_CONFIG.String(), materialName, tmpPath, casBackend, nil); err != nil { return fmt.Errorf("adding AI agent config material for %s: %w", agentName, err) }