Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/actionlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ self-hosted-runner:
- crabbox
- openclaw
- clawscan
- xcode-27
33 changes: 31 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ on:
permissions:
contents: read

env:
MACOSX_DEPLOYMENT_TARGET: '13.0'

concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
Expand All @@ -30,9 +33,17 @@ jobs:
uses: rhysd/actionlint@914e7df21a07ef503a81201c76d2b11c789d3fca # v1.7.12

test:
name: Test
runs-on: ubuntu-latest
name: ${{ matrix.check_name }}
runs-on: ${{ matrix.os }}
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
check_name: Test
- os: xcode-27
check_name: Test (macOS 27)
Comment on lines +45 to +46

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep untrusted PRs off the release signing runner

For pull requests, this matrix entry checks out and executes contributor-controlled Go and Node tests on xcode-27, which .github/actionlint.yaml identifies as a self-hosted runner label. The release workflow also uses that runner pool for the signing job and exposes the Developer ID and App Store Connect credentials there (release.yml:26,65-73), so on any reusable worker a malicious PR can establish persistence and exfiltrate those secrets during a later release. Use a separate ephemeral/no-secret macOS runner for PR validation, or restrict this entry to trusted branches.

Useful? React with 👍 / 👎.

steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -67,6 +78,7 @@ jobs:
run: go build ./cmd/clawscan

- name: Test Docker mount paths and permissions
if: runner.os == 'Linux'
run: |
docker pull alpine:3.23
node --test scripts/docker-mounts.test.mjs
Expand All @@ -78,6 +90,23 @@ jobs:
node --test scripts/release-notes.test.mjs
node scripts/build-npm-package.mjs --version v0.0.0 --pack --smoke

- name: Verify macOS minimum and both architectures
if: runner.os == 'macOS'
run: |
if ! arch -x86_64 /usr/bin/true; then
softwareupdate --install-rosetta --agree-to-license
fi
arch -x86_64 /usr/bin/true
for npm_arch in arm64 x64; do
binary="dist/npm/package/binaries/darwin-${npm_arch}/clawscan"
minos=$(xcrun vtool -show-build "$binary" | awk '$1 == "minos" { print $2 }')
test "$minos" = 13.0
done
dist/npm/package/binaries/darwin-arm64/clawscan --version
dist/npm/package/binaries/darwin-arm64/clawscan --help
arch -x86_64 dist/npm/package/binaries/darwin-x64/clawscan --version
arch -x86_64 dist/npm/package/binaries/darwin-x64/clawscan --help

- name: Test runtime update branch push
run: node --test scripts/push-runtime-tool-update-branch.test.mjs

Expand Down
23 changes: 18 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ on:
required: false

permissions: {}
env:
MACOSX_DEPLOYMENT_TARGET: '13.0'
concurrency:
group: release-${{ github.event.inputs.version || github.ref_name }}
cancel-in-progress: false

jobs:
build:
runs-on: macos-15
runs-on: xcode-27
permissions:
actions: read
contents: read
Expand Down Expand Up @@ -99,10 +101,10 @@ jobs:
strategy:
matrix:
include:
- runner: macos-15
- runner: xcode-27
arch: arm64
npm_arch: arm64
- runner: macos-15-intel
- runner: xcode-27
arch: amd64
npm_arch: x64
runs-on: ${{ matrix.runner }}
Expand All @@ -129,8 +131,19 @@ jobs:
cmp "$binary" "npm/package/binaries/darwin-${NPM_ARCH}/clawscan"
codesign --verify --strict --check-notarization -R=notarized "$binary"
codesign -dv --verbose=4 "$binary" 2>&1 | grep -Fx 'TeamIdentifier=FWJYW4S8P8'
"$binary" --version
"$binary" --help
minos=$(xcrun vtool -show-build "$binary" | awk '$1 == "minos" { print $2 }')
test "$minos" = 13.0
if [ "$ARCH" = amd64 ]; then
if ! arch -x86_64 /usr/bin/true; then
softwareupdate --install-rosetta --agree-to-license
fi
arch -x86_64 /usr/bin/true
arch -x86_64 "$binary" --version
arch -x86_64 "$binary" --help
else
"$binary" --version
"$binary" --help
fi

npm:
timeout-minutes: 45
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Move macOS CI and release verification to Xcode 27 with Rosetta for x86_64, retain the existing macOS 13 minimum in both release and npm binaries, and select Go 1.27.2 to fix reachable standard-library vulnerabilities while keeping the Go 1.27.1 source minimum.
- Keep mocked scanner/context tests independent of an installed Docker CLI while preserving sandbox enforcement checks.

- Refresh bundled Cisco, Snyk, Socket, Claude Code, and Codex tools while retaining the existing Node.js and Python runtime floors and two-day dependency cooldown.
- Preserve Docker sandbox paths containing commas or quotation marks without changing read-only or writable mount permissions. Thanks @SebTardif (#64).
- Use one shared autoreview installation from `openclaw/agent-skills`; repository entrypoints now receive upstream fixes without copied helpers or test suites.
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ Install ClawScan:
npm install -g @openclaw/clawscan
```

The macOS binaries support macOS 13 or later on Apple Silicon and Intel. CI uses
the Apple Silicon `xcode-27` runner and verifies x86_64 binaries under Rosetta.
Release and npm builds pin the macOS 13 deployment target and check both binaries' minimum OS metadata.
Source builds require Go 1.27.1 or later; the module selects the patched Go 1.27.2 build toolchain.

Command-backed scanners and judges run in ClawScan's Docker runtime by default,
so keep Docker running for local scans.

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module github.com/openclaw/clawscan

go 1.27.1

toolchain go1.27.2

require (
github.com/alchemy/json5 v0.2.0
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3
Expand Down
7 changes: 6 additions & 1 deletion internal/runner/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1674,13 +1674,14 @@ func TestNewArtifactAlwaysIncludesPassingGate(t *testing.T) {
}

func TestRunWritesScannerOnlyArtifact(t *testing.T) {
t.Setenv("PATH", t.TempDir())
dir := t.TempDir()
target := filepath.Join(dir, "skill")
if err := os.Mkdir(target, 0o755); err != nil {
t.Fatal(err)
}
out := filepath.Join(dir, "run.json")
opts, err := ParseArgs([]string{target, "--scanner", "skillspector", "--output", out})
opts, err := ParseArgs([]string{target, "--scanner", "skillspector", "--sandbox", "off", "--output", out})
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -4737,6 +4738,7 @@ func TestValidateArtifactInspectionReceiptStillRequiresChallengedFile(t *testing
}

func TestRunLoadsExplicitContextJSON(t *testing.T) {
t.Setenv("PATH", t.TempDir())
dir := t.TempDir()
target := filepath.Join(dir, "skill")
if err := os.MkdirAll(target, 0o755); err != nil {
Expand All @@ -4751,6 +4753,7 @@ func TestRunLoadsExplicitContextJSON(t *testing.T) {
Target: target,
ContextPath: contextPath,
Scanners: []string{"skillspector"},
Sandbox: SandboxOptions{Mode: SandboxModeOff},
}
artifact, err := Run(opts, RunContext{
Env: map[string]string{},
Expand All @@ -4767,6 +4770,7 @@ func TestRunLoadsExplicitContextJSON(t *testing.T) {
}

func TestRunRejectsInvalidContextJSON(t *testing.T) {
t.Setenv("PATH", t.TempDir())
dir := t.TempDir()
target := filepath.Join(dir, "skill")
if err := os.MkdirAll(target, 0o755); err != nil {
Expand All @@ -4780,6 +4784,7 @@ func TestRunRejectsInvalidContextJSON(t *testing.T) {
Target: target,
ContextPath: contextPath,
Scanners: []string{"skillspector"},
Sandbox: SandboxOptions{Mode: SandboxModeOff},
}, RunContext{
Env: map[string]string{},
ScannerRunner: staticScannerRunner{results: map[string]ScannerResult{
Expand Down
3 changes: 2 additions & 1 deletion scripts/build-npm-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ async function stagePackage(options) {
"build",
"-trimpath",
"-ldflags",
ldflags,
target.goos === "darwin" ? `${ldflags} -macos=13.0` : ldflags,
"-o",
join(binaryDir, binaryNameForTarget(target)),
"github.com/openclaw/clawscan/cmd/clawscan",
Expand All @@ -186,6 +186,7 @@ async function stagePackage(options) {
GOOS: target.goos,
GOARCH: target.goarch,
CGO_ENABLED: "0",
MACOSX_DEPLOYMENT_TARGET: "13.0",
},
},
);
Expand Down
6 changes: 5 additions & 1 deletion scripts/build-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ for platform in "${platforms[@]}"; do
fi

mkdir -p "$workdir"
GOOS="$os" GOARCH="$arch" CGO_ENABLED=0 go build -trimpath -ldflags "$ldflags" -o "${workdir}/${binary}" "$package"
target_ldflags="$ldflags"
if [[ "$os" == darwin ]]; then
target_ldflags="$target_ldflags -macos=13.0"
fi
MACOSX_DEPLOYMENT_TARGET=13.0 GOOS="$os" GOARCH="$arch" CGO_ENABLED=0 go build -trimpath -ldflags "$target_ldflags" -o "${workdir}/${binary}" "$package"
cp README.md "${workdir}/README.md"

if [[ "$os" == "windows" ]]; then
Expand Down
Loading