-
Notifications
You must be signed in to change notification settings - Fork 44
Expand file tree
/
Copy pathsecurity.mts
More file actions
81 lines (72 loc) · 2.8 KB
/
security.mts
File metadata and controls
81 lines (72 loc) · 2.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
* @file Canonical fleet scanning-security runner. Runs the two static-analysis
* tools the fleet uses for local security checks before push:
*
* 1. AgentShield — scans `.claude/` config for prompt-injection, leaked secrets,
* and overly-permissive tool permissions.
* 2. zizmor — static analysis for `.github/workflows/*.yml` (unpinned actions,
* secret exposure, template injection, permission issues). Either tool
* missing prints a "run pnpm run setup-security-tools" hint (which
* downloads + verifies the pinned binary via the setup-security-tools hook
* + prompts for a Socket API token if none is stored) and skips that scan
* rather than failing the entire run. Cross-platform: uses `which` from
* `@socketsecurity/lib-stable/bin` for binary discovery (handles Windows
* .exe/.cmd resolution; returns null rather than throwing on miss) and
* `spawn` from `@socketsecurity/lib-stable/spawn` for proper async
* lifecycle. Wired in via `package.json`: "security": "node
* scripts/security.mts" Byte-identical across every fleet repo.
* Sync-scaffolding flags drift.
*/
import process from 'node:process'
import { which } from '@socketsecurity/lib-stable/bin/which'
import { WIN32 } from '@socketsecurity/lib-stable/constants/platform'
import { getDefaultLogger } from '@socketsecurity/lib-stable/logger/default'
import { spawn } from '@socketsecurity/lib-stable/process/spawn/child'
const logger = getDefaultLogger()
async function hasExecutable(name: string): Promise<boolean> {
// socket-lib's `which` returns null when the binary isn't on PATH
// (no throw), so a simple truthy check suffices.
return Boolean(await which(name))
}
async function runTool(command: string, args: string[]): Promise<number> {
try {
const result = await spawn(command, args, {
stdio: 'inherit',
shell: WIN32,
})
return result.code ?? 1
} catch (e) {
if (e && typeof e === 'object' && 'code' in e) {
const code = (e as { code: unknown }).code
return typeof code === 'number' ? code : 1
}
throw e
}
}
async function main(): Promise<void> {
if (!(await hasExecutable('agentshield'))) {
logger.info(
'agentshield not installed; run "pnpm run setup-security-tools" to install',
)
} else {
const agentshieldCode = await runTool('agentshield', ['scan'])
if (agentshieldCode !== 0) {
process.exitCode = agentshieldCode
return
}
}
if (!(await hasExecutable('zizmor'))) {
logger.info(
'zizmor not installed; run "pnpm run setup-security-tools" to install',
)
return
}
const zizmorCode = await runTool('zizmor', ['.github/'])
if (zizmorCode !== 0) {
process.exitCode = zizmorCode
}
}
main().catch((e: unknown) => {
logger.error(e)
process.exitCode = 1
})