Skip to content

Commit 586a787

Browse files
committed
fix(opencode): gate host access and preserve evidence
Signed-off-by: Narendran Raghavan <nraghavan@nvidia.com>
1 parent 413006c commit 586a787

5 files changed

Lines changed: 1050 additions & 62 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ concurrency:
3333
env:
3434
UV_VERSION: "0.10.x"
3535
PYTHON_VERSION: "3.12"
36+
NODE_VERSION: "22.20.0"
3637
UV_CACHE_DIR: .uv-cache
3738
UV_LINK_MODE: copy
3839

@@ -90,6 +91,18 @@ jobs:
9091
- run: uv run skillspector --version
9192
- run: uv run make test-ci
9293

94+
test-opencode:
95+
name: OpenCode TypeScript Tests
96+
runs-on: ubuntu-latest
97+
steps:
98+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
99+
- name: Set up Node.js
100+
# Pinned to a full commit SHA (third-party action); comment tracks the tag.
101+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
102+
with:
103+
node-version: ${{ env.NODE_VERSION }}
104+
- run: node --test tests/opencode/skillspector_scan_lib.test.ts
105+
93106
docker-smoke:
94107
needs: changes
95108
if: needs.changes.outputs.docker == 'true'
Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,10 @@
11
import { tool } from "@opencode-ai/plugin"
22
import { execFile } from "node:child_process"
33
import fs from "node:fs"
4-
import path from "node:path"
54
import { promisify } from "node:util"
6-
import {
7-
TIMEOUT_MS,
8-
buildCliArgs,
9-
formatExecError,
10-
formatSuccess,
11-
isUrlOrAbsolute,
12-
resolveBinary,
13-
} from "./skillspector_scan_lib.ts"
5+
import { executeScan, type RunFile } from "./skillspector_scan_lib.ts"
146

15-
const runFile = promisify(execFile)
7+
const runFile = promisify(execFile) as unknown as RunFile
168

179
export default tool({
1810
description: "Scan an AI agent skill for security risks with SkillSpector. Static analysis only by default; opt into LLM analysis explicitly.",
@@ -23,27 +15,10 @@ export default tool({
2315
output: tool.schema.string().optional().describe("Write the report to this file instead of returning it (resolved against the session directory if relative)"),
2416
},
2517
async execute(args, context) {
26-
const baseDir = context.directory ?? context.worktree ?? process.cwd()
27-
const target = isUrlOrAbsolute(args.target) ? args.target : path.resolve(baseDir, args.target)
28-
const output = args.output
29-
? (isUrlOrAbsolute(args.output) ? args.output : path.resolve(baseDir, args.output))
30-
: undefined
31-
const bin = resolveBinary(context.worktree ?? baseDir, {
18+
return executeScan(args, context, {
19+
runFile,
3220
existsSync: fs.existsSync,
21+
lstatSync: fs.lstatSync,
3322
})
34-
const cliArgs = buildCliArgs({ target, format: args.format, noLlm: args.noLlm, output })
35-
36-
let result: { stdout: string; stderr: string }
37-
try {
38-
result = (await runFile(bin, cliArgs, {
39-
timeout: TIMEOUT_MS,
40-
maxBuffer: 32 * 1024 * 1024,
41-
cwd: baseDir,
42-
})) as { stdout: string; stderr: string }
43-
} catch (err: unknown) {
44-
return formatExecError(bin, err)
45-
}
46-
47-
return formatSuccess(output, result.stdout, result.stderr)
4823
},
4924
})

0 commit comments

Comments
 (0)