diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b42987e..10ea1ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: timeout-minutes: 5 steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Lint workflows uses: rhysd/actionlint@914e7df21a07ef503a81201c76d2b11c789d3fca # v1.7.12 @@ -35,16 +35,16 @@ jobs: timeout-minutes: 20 steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod cache: true - name: Set up Node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 @@ -71,3 +71,6 @@ jobs: - name: Test runtime update branch push run: node --test scripts/push-runtime-tool-update-branch.test.mjs + + - name: Test Security Signals publishing + run: node --test scripts/publish-security-signals-results.test.mjs diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c4e1b89..7e20b76 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -36,14 +36,14 @@ jobs: - actions steps: - name: Check out - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Initialize CodeQL - uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 + uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 with: languages: ${{ matrix.language }} - name: Analyze - uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 + uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 with: category: /language:${{ matrix.language }} diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 35d1954..0156d84 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -22,10 +22,10 @@ jobs: pull-requests: read steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Review dependency changes - uses: actions/dependency-review-action@v4 + uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 with: fail-on-severity: high comment-summary-in-pr: always diff --git a/.github/workflows/npm-release.yml b/.github/workflows/npm-release.yml index 70e4b45..1979d06 100644 --- a/.github/workflows/npm-release.yml +++ b/.github/workflows/npm-release.yml @@ -40,19 +40,19 @@ jobs: exit 1 - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: refs/tags/${{ inputs.tag }} fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod cache: true - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ env.NODE_VERSION }} registry-url: https://registry.npmjs.org @@ -66,7 +66,7 @@ jobs: run: node scripts/build-npm-package.mjs --version "${{ inputs.tag }}" --pack --smoke - name: Upload prepared npm publish bundle - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: clawscan-npm-preflight-${{ inputs.tag }} path: | @@ -113,20 +113,20 @@ jobs: id-token: write steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: refs/tags/${{ inputs.tag }} fetch-depth: 0 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ env.NODE_VERSION }} registry-url: https://registry.npmjs.org - name: Ensure npm supports trusted publishing run: | - npm install -g npm@^11.15.0 + npm install -g npm@^11.19.1 node --version npm --version @@ -141,7 +141,7 @@ jobs: printf '%s' "$RUN_JSON" | node --input-type=module -e 'const chunks=[]; process.stdin.on("data", (chunk) => chunks.push(chunk)); process.stdin.on("end", () => { const run = JSON.parse(Buffer.concat(chunks).toString("utf8")); const checks = [["workflowName", "ClawScan Binary NPM Promotion"], ["headBranch", "main"], ["event", "workflow_dispatch"], ["conclusion", "success"]]; for (const [key, expected] of checks) { if (run[key] !== expected) { console.error(`Referenced npm preflight run ${process.env.PREFLIGHT_RUN_ID} must have ${key}=${expected}, got ${run[key] ?? ""}.`); process.exit(1); } } console.log(`Using npm preflight run ${process.env.PREFLIGHT_RUN_ID}: ${run.url}`); });' - name: Download prepared npm tarball - uses: actions/download-artifact@v4 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: clawscan-npm-preflight-${{ inputs.tag }} path: dist/npm diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index c41a665..805a816 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -31,10 +31,10 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 @@ -55,15 +55,15 @@ jobs: - name: Configure Pages if: steps.pages-site.outputs.enabled == 'true' - uses: actions/configure-pages@v5 + uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 - name: Upload Pages artifact if: steps.pages-site.outputs.enabled == 'true' - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 with: path: dist/docs-site - name: Deploy to GitHub Pages if: steps.pages-site.outputs.enabled == 'true' id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 diff --git a/.github/workflows/publish-security-signals-results.yml b/.github/workflows/publish-security-signals-results.yml index 7156d47..54bb2ab 100644 --- a/.github/workflows/publish-security-signals-results.yml +++ b/.github/workflows/publish-security-signals-results.yml @@ -15,10 +15,10 @@ jobs: HF_TOKEN: ${{ secrets.HF_TOKEN }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod cache: true @@ -41,7 +41,7 @@ jobs: - name: Upload dry-run/published payload if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: security-signals-results-payload path: dist/security-signals-results/results.jsonl diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0c5c6f1..c7b65ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,12 +54,12 @@ jobs: echo "checkout_ref=$checkout_ref" >> "$GITHUB_OUTPUT" - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ steps.version.outputs.checkout_ref }} - name: Set up Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod cache: true @@ -68,7 +68,7 @@ jobs: run: make release VERSION="${{ steps.version.outputs.value }}" - name: Upload artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: clawscan-${{ steps.version.outputs.value }}-release path: | @@ -84,7 +84,7 @@ jobs: if: github.event_name == 'push' || inputs.publish steps: - name: Download artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: dist merge-multiple: true @@ -110,19 +110,19 @@ jobs: id-token: write steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ needs.build.outputs.checkout_ref }} fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod cache: true - name: Set up Node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ env.NODE_VERSION }} registry-url: https://registry.npmjs.org diff --git a/.github/workflows/run-clawscan-benchmark.yml b/.github/workflows/run-clawscan-benchmark.yml index 1bf31d0..05a6f28 100644 --- a/.github/workflows/run-clawscan-benchmark.yml +++ b/.github/workflows/run-clawscan-benchmark.yml @@ -154,7 +154,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 @@ -166,7 +166,7 @@ jobs: run: gh pr checkout "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod cache: true @@ -268,7 +268,7 @@ jobs: - name: Upload benchmark artifact if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ inputs.artifact_name }} path: | diff --git a/.github/workflows/runtime-image.yml b/.github/workflows/runtime-image.yml index ff04325..38167ed 100644 --- a/.github/workflows/runtime-image.yml +++ b/.github/workflows/runtime-image.yml @@ -25,17 +25,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 - name: Log in to GitHub Container Registry if: github.event_name != 'pull_request' - uses: docker/login-action@v3 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -43,7 +43,7 @@ jobs: - name: Resolve image metadata id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 with: images: ghcr.io/${{ github.repository_owner }}/clawscan-runtime tags: | @@ -53,7 +53,7 @@ jobs: type=raw,value=latest,enable={{is_default_branch}} - name: Build image - uses: docker/build-push-action@v6 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: context: . file: docker/clawscan-runtime/Dockerfile diff --git a/.github/workflows/runtime-tool-updates.yml b/.github/workflows/runtime-tool-updates.yml index edddf6a..8494133 100644 --- a/.github/workflows/runtime-tool-updates.yml +++ b/.github/workflows/runtime-tool-updates.yml @@ -19,10 +19,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Node - uses: actions/setup-node@v4 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 24 diff --git a/.github/workflows/security-signals-submissions.yml b/.github/workflows/security-signals-submissions.yml index 792953a..a84315e 100644 --- a/.github/workflows/security-signals-submissions.yml +++ b/.github/workflows/security-signals-submissions.yml @@ -20,12 +20,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod cache: true @@ -35,7 +35,7 @@ jobs: - name: Upload score previews if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: security-signals-submission-scores path: dist/security-signals-submission-scores diff --git a/.github/workflows/skilltrustbench-profile-gate.yml b/.github/workflows/skilltrustbench-profile-gate.yml index ab4db6c..93f573a 100644 --- a/.github/workflows/skilltrustbench-profile-gate.yml +++ b/.github/workflows/skilltrustbench-profile-gate.yml @@ -31,7 +31,7 @@ jobs: baseline_path: ${{ steps.baseline.outputs.path }} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 @@ -128,7 +128,7 @@ jobs: BASELINE_PATH: ${{ needs.validate-proposal.outputs.baseline_path }} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 @@ -139,13 +139,13 @@ jobs: run: gh pr checkout "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: go.mod cache: true - name: Download benchmark artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: skilltrustbench-candidate path: ./downloaded-artifact diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index ecd8076..67bf45c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -16,7 +16,7 @@ jobs: timeout-minutes: 10 steps: - name: Mark inactive issues and pull requests - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10 + uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0 with: days-before-issue-stale: 30 days-before-issue-close: 14 diff --git a/cmd/clawscan/main_test.go b/cmd/clawscan/main_test.go index 50f9b0f..65bc6a4 100644 --- a/cmd/clawscan/main_test.go +++ b/cmd/clawscan/main_test.go @@ -1340,7 +1340,10 @@ func TestRunCommandProfilePlusOverride(t *testing.T) { writeSkill(t, target, "# Override\n") stdout := captureStdout(t, func() { - if err := run([]string{target, "--profile", "clawhub", "--scanner", "clawscan-static", "--json"}, []string{}); err != nil { + if err := run([]string{ + target, "--profile", "clawhub", "--scanner", "clawscan-static", + "--judge", clawHubReceiptJudgeCommand(), "--sandbox", "off", "--json", + }, []string{}); err != nil { t.Fatal(err) } }) diff --git a/go.mod b/go.mod index 50684df..e08f198 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.26.1 require ( github.com/alchemy/json5 v0.2.0 - github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 + github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 gopkg.in/yaml.v3 v3.0.1 ) -require golang.org/x/text v0.14.0 // indirect +require golang.org/x/text v0.41.0 // indirect diff --git a/go.sum b/go.sum index a1209df..4692162 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,10 @@ github.com/alchemy/json5 v0.2.0 h1:M8hmUpCyGlzdiWaxY4RI/rDcLAlFTtjB6j49eUpk+FE= github.com/alchemy/json5 v0.2.0/go.mod h1:kVE7UoCjGVIlxOXFCzKn6T34nyC/OctNTNG81MLOqiw= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 h1:1EYB5IzjZawrrnELUi78f9fPu57HuXjmddZPjrls/28= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.3/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/installpolicy/stages_test.go b/internal/installpolicy/stages_test.go index 4d20b29..2e767e2 100644 --- a/internal/installpolicy/stages_test.go +++ b/internal/installpolicy/stages_test.go @@ -277,11 +277,14 @@ func TestPrepareDependencyTreeScanTargetCopiesSafePackageSymlinkTargets(t *testi } func TestCopyDependencyPackageEnforcesEntryAndByteBudgets(t *testing.T) { - source := t.TempDir() + source, err := filepath.EvalSymlinks(t.TempDir()) + if err != nil { + t.Fatal(err) + } writeStageTestFile(t, filepath.Join(source, "package.json"), `{"name":"demo"}`) entryBudget := dependencyCopyBudget{entries: maxDependencyEntries} - err := copyDependencyPackage(source, source, filepath.Join(t.TempDir(), "entries"), &entryBudget) + err = copyDependencyPackage(source, source, filepath.Join(t.TempDir(), "entries"), &entryBudget) if err == nil || !strings.Contains(err.Error(), "filesystem entries") { t.Fatalf("entry budget error = %v", err) } @@ -292,7 +295,10 @@ func TestCopyDependencyPackageEnforcesEntryAndByteBudgets(t *testing.T) { t.Fatalf("byte budget error = %v", err) } - largeSource := t.TempDir() + largeSource, err := filepath.EvalSymlinks(t.TempDir()) + if err != nil { + t.Fatal(err) + } largePath := filepath.Join(largeSource, "large.bin") file, err := os.Create(largePath) if err != nil { diff --git a/internal/runner/runner_test.go b/internal/runner/runner_test.go index 817df76..a6115cd 100644 --- a/internal/runner/runner_test.go +++ b/internal/runner/runner_test.go @@ -5384,6 +5384,7 @@ func TestRunJudgeDoesNotPersistRenderedCommand(t *testing.T) { ScannerRunner: staticScannerRunner{results: map[string]ScannerResult{ "skillspector": {Status: "completed", Raw: json.RawMessage(`{"status":"clean"}`)}, }}, + CommandRunner: &recordingCommandRunner{writeOutput: `{"ok":true}`}, }) if err != nil { t.Fatal(err) diff --git a/internal/runner/target_test.go b/internal/runner/target_test.go index 6ec8027..ad0bce4 100644 --- a/internal/runner/target_test.go +++ b/internal/runner/target_test.go @@ -41,12 +41,8 @@ func TestResolveTargetClassifiesPluginDirectory(t *testing.T) { if resolved.id != "probe-plugin" { t.Fatalf("id = %q", resolved.id) } - expected, err := filepath.EvalSymlinks(dir) - if err != nil { - expected = dir - } - if resolved.resolvedPath != expected { - t.Fatalf("resolvedPath = %q, want %q", resolved.resolvedPath, expected) + if resolved.resolvedPath != dir { + t.Fatalf("resolvedPath = %q, want %q", resolved.resolvedPath, dir) } } @@ -60,12 +56,8 @@ func TestResolveTargetClassifiesPluginManifestFile(t *testing.T) { if resolved.kind != targetKindPlugin || resolved.id != "probe-plugin" { t.Fatalf("resolved = %#v", resolved) } - expected, evalErr := filepath.EvalSymlinks(dir) - if evalErr != nil { - expected = dir - } - if resolved.resolvedPath != expected { - t.Fatalf("manifest-file target must scan the plugin directory: resolvedPath = %q, want %q", resolved.resolvedPath, expected) + if resolved.resolvedPath != dir { + t.Fatalf("manifest-file target must scan the plugin directory: resolvedPath = %q, want %q", resolved.resolvedPath, dir) } } diff --git a/leaderboard/results/README.md b/leaderboard/results/README.md index aa70d3b..e40dfe1 100644 --- a/leaderboard/results/README.md +++ b/leaderboard/results/README.md @@ -26,6 +26,9 @@ The publish path is intentionally separate from PR validation: - Post-merge publishing runs `scripts/publish-security-signals-results.sh`. - Dry-run mode writes `dist/security-signals-results/results.jsonl` locally. +Publishing requires the `hf` CLI from `huggingface_hub` and reads credentials +from `HF_TOKEN`. The deprecated `huggingface-cli` command is not supported. + Each result row is JSONL with this shape: ```json diff --git a/leaderboard/space/requirements.txt b/leaderboard/space/requirements.txt index 290b493..70f00ea 100644 --- a/leaderboard/space/requirements.txt +++ b/leaderboard/space/requirements.txt @@ -1,2 +1,2 @@ -gradio>=6.19,<7 -huggingface_hub>=0.30 +gradio>=6.26,<7 +huggingface_hub>=1.29 diff --git a/scripts/build-release.sh b/scripts/build-release.sh index adfd978..08a4ec4 100755 --- a/scripts/build-release.sh +++ b/scripts/build-release.sh @@ -43,6 +43,6 @@ for platform in "${platforms[@]}"; do fi done -(cd "$dist_dir" && shasum -a 256 *.tar.gz *.zip > checksums.txt) +(cd "$dist_dir" && shasum -a 256 -- *.tar.gz *.zip > checksums.txt) printf 'Built release artifacts in %s/\n' "$dist_dir" diff --git a/scripts/publish-security-signals-results.sh b/scripts/publish-security-signals-results.sh index d2ac22a..59ac217 100755 --- a/scripts/publish-security-signals-results.sh +++ b/scripts/publish-security-signals-results.sh @@ -46,7 +46,7 @@ Usage: scripts/publish-security-signals-results.sh [--dry-run|--publish] [--root Builds the Security Signals results dataset payload from accepted submission directories. Dry-run mode writes the JSONL payload locally. Publish mode uploads -that payload to the private Hugging Face dataset with huggingface-cli. +that payload to the private Hugging Face dataset with hf. USAGE exit 0 ;; @@ -116,9 +116,9 @@ if [ -z "${HF_TOKEN:-}" ]; then echo "HF_TOKEN is required for --publish" >&2 exit 1 fi -if ! command -v huggingface-cli >/dev/null 2>&1; then - echo "huggingface-cli is required for --publish. Install with: python -m pip install huggingface_hub" >&2 +if ! command -v hf >/dev/null 2>&1; then + echo "hf is required for --publish. Install with: python -m pip install huggingface_hub" >&2 exit 1 fi -huggingface-cli upload "$results_dataset" "$output_path" results.jsonl --repo-type dataset --token "$HF_TOKEN" +hf upload "$results_dataset" "$output_path" results.jsonl --repo-type dataset diff --git a/scripts/publish-security-signals-results.test.mjs b/scripts/publish-security-signals-results.test.mjs new file mode 100644 index 0000000..a1e3408 --- /dev/null +++ b/scripts/publish-security-signals-results.test.mjs @@ -0,0 +1,55 @@ +import assert from 'node:assert/strict'; +import { spawnSync } from 'node:child_process'; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import test from 'node:test'; + +const scriptPath = fileURLToPath(new URL('./publish-security-signals-results.sh', import.meta.url)); + +function fixture(t) { + const root = mkdtempSync(join(tmpdir(), 'clawscan-publish-')); + t.after(() => rmSync(root, { recursive: true, force: true })); + const bin = join(root, 'bin'); + mkdirSync(bin); + writeFileSync(join(bin, 'hf'), '#!/bin/sh\nprintf "%s\\n" "$@" > "$HF_ARGS_FILE"\n', { mode: 0o755 }); + const argsFile = join(root, 'hf-args'); + const output = join(root, 'results.jsonl'); + return { + argsFile, + output, + run(mode, token = '') { + return spawnSync('bash', [scriptPath, mode, '--root', join(root, 'submissions'), '--output', output], { + encoding: 'utf8', + env: { PATH: `${bin}:/usr/bin:/bin`, HF_TOKEN: token, HF_ARGS_FILE: argsFile }, + }); + }, + }; +} + +test('publishes through hf without passing credentials in arguments', (t) => { + const f = fixture(t); + const result = f.run('--publish', 'fixture'); + assert.equal(result.status, 0, result.stderr); + assert.deepEqual(readFileSync(f.argsFile, 'utf8').trim().split('\n'), [ + 'upload', 'OpenClaw/clawhub-security-signals-results', f.output, 'results.jsonl', '--repo-type', 'dataset', + ]); + assert.equal(readFileSync(f.output, 'utf8'), ''); +}); + +test('dry run writes a payload without invoking hf', (t) => { + const f = fixture(t); + const result = f.run('--dry-run'); + assert.equal(result.status, 0, result.stderr); + assert.equal(readFileSync(f.output, 'utf8'), ''); + assert.equal(existsSync(f.argsFile), false); +}); + +test('publishing still requires a token', (t) => { + const f = fixture(t); + const result = f.run('--publish'); + assert.notEqual(result.status, 0); + assert.match(result.stderr, /HF_TOKEN is required/); + assert.equal(existsSync(f.argsFile), false); +});