Skip to content

Commit 201f9fc

Browse files
committed
chore: sync to socket-repo-template@899813d
- Adopt canonical scripts/security.mts (cross-platform which + async spawn replaces inline agentshield+zizmor one-liner) - Update scripts/update.mts + .config/taze.config.mts to template - package.json: scripts.security delegates to wrapper Verified: 0 findings against template.
1 parent 91871c7 commit 201f9fc

4 files changed

Lines changed: 183 additions & 122 deletions

File tree

.config/taze.config.mts

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,40 @@
1-
/**
2-
* @fileoverview Taze dependency-update configuration for socket-lib.
3-
*/
4-
51
import { defineConfig } from 'taze'
62

7-
const tazeConfig = defineConfig({
8-
// Exclude these packages.
9-
exclude: [
10-
'debug',
11-
'eslint-plugin-unicorn',
12-
'make-fetch-happen',
13-
'minimatch',
14-
'normalize-package-data',
15-
],
3+
/* Socket-owned scopes bypass the 7-day maturity cooldown.
4+
*
5+
* The cooldown (maturityPeriod: 7) exists to catch compromised
6+
* upstream packages before we adopt them — but Socket-published
7+
* packages go through our own provenance + publish pipeline, so
8+
* we trust them to ship fresh.
9+
*
10+
* The scopes listed here are EXCLUDED from pass 1 (the
11+
* cooldown-respecting pass) and INCLUDED in pass 2 (the
12+
* immediate-bump pass). Keep this list in sync with
13+
* scripts/update.mts if the repo ships one, or with whatever
14+
* second-pass mechanism the consuming repo's update script
15+
* uses.
16+
*/
17+
const SOCKET_SCOPES = [
18+
'@socketregistry/*',
19+
'@socketsecurity/*',
20+
'@socketdev/*',
21+
'socket-*',
22+
'ecc-agentshield',
23+
'sfw',
24+
]
25+
26+
export default defineConfig({
1627
// Interactive mode disabled for automation.
1728
interactive: false,
18-
// Silent logging.
19-
loglevel: 'silent',
20-
// Only update packages that have been stable for 7 days.
29+
// Minimal logging.
30+
loglevel: 'warn',
31+
// Socket scopes handled by a second pass with maturityPeriod 0.
32+
exclude: SOCKET_SCOPES,
33+
// 7-day cooldown on third-party deps — matches `.npmrc`'s
34+
// min-release-age setting for install-time enforcement.
2135
maturityPeriod: 7,
22-
// Update mode: 'latest'.
36+
// Bump to latest across major boundaries.
2337
mode: 'latest',
24-
// Recursive mode to handle all package.json files.
25-
recursive: true,
26-
// Write to package.json automatically.
38+
// Edit package.json in place.
2739
write: true,
2840
})
29-
30-
export { tazeConfig }
31-
export default tazeConfig

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@
728728
"format:check": "oxfmt --check .",
729729
"lint": "node scripts/lint.mts",
730730
"prim": "node tools/prim/bin/prim.mts",
731-
"security": "agentshield scan && { command -v zizmor >/dev/null && zizmor .github/ || echo 'zizmor not installed — run pnpm run setup to install'; }",
731+
"security": "node scripts/security.mts",
732732
"prepare": "husky && node scripts/build/main.mts --quiet",
733733
"prepublishOnly": "pnpm run build",
734734
"test": "node scripts/test/main.mts",

scripts/security.mts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* @fileoverview Canonical fleet security-scan runner.
3+
*
4+
* Runs the two static-analysis tools the fleet uses for local security
5+
* checks before push:
6+
*
7+
* 1. AgentShield — scans `.claude/` config for prompt-injection,
8+
* leaked secrets, and overly-permissive tool permissions.
9+
* 2. zizmor — static analysis for `.github/workflows/*.yml`
10+
* (unpinned actions, secret exposure, template injection,
11+
* permission issues).
12+
*
13+
* Either tool missing prints a "run pnpm run setup" hint (which
14+
* downloads + verifies the pinned binary via the setup-security-tools
15+
* hook) and skips that scan rather than failing the entire run.
16+
*
17+
* Cross-platform: uses `which` from npm for binary discovery (handles
18+
* Windows .exe/.cmd resolution) and `spawn` from
19+
* `@socketsecurity/lib/spawn` for proper async lifecycle.
20+
*
21+
* Wired in via `package.json`:
22+
*
23+
* "security": "node scripts/security.mts"
24+
*
25+
* Byte-identical across every fleet repo. Sync-scaffolding flags
26+
* drift.
27+
*/
28+
29+
import process from 'node:process'
30+
31+
import which from 'which'
32+
33+
import { WIN32 } from '@socketsecurity/lib/constants/platform'
34+
import { getDefaultLogger } from '@socketsecurity/lib/logger'
35+
import { spawn } from '@socketsecurity/lib/spawn'
36+
37+
const logger = getDefaultLogger()
38+
39+
async function hasExecutable(name: string): Promise<boolean> {
40+
try {
41+
await which(name)
42+
return true
43+
} catch {
44+
return false
45+
}
46+
}
47+
48+
async function runTool(command: string, args: string[]): Promise<number> {
49+
try {
50+
const result = await spawn(command, args, {
51+
stdio: 'inherit',
52+
shell: WIN32,
53+
})
54+
return result.code ?? 1
55+
} catch (e) {
56+
if (e && typeof e === 'object' && 'code' in e) {
57+
const code = (e as { code: unknown }).code
58+
return typeof code === 'number' ? code : 1
59+
}
60+
throw e
61+
}
62+
}
63+
64+
async function main(): Promise<void> {
65+
if (!(await hasExecutable('agentshield'))) {
66+
logger.info('agentshield not installed; run "pnpm run setup" to install')
67+
} else {
68+
const agentshieldCode = await runTool('agentshield', ['scan'])
69+
if (agentshieldCode !== 0) {
70+
process.exitCode = agentshieldCode
71+
return
72+
}
73+
}
74+
75+
if (!(await hasExecutable('zizmor'))) {
76+
logger.info('zizmor not installed; run "pnpm run setup" to install')
77+
return
78+
}
79+
80+
const zizmorCode = await runTool('zizmor', ['.github/'])
81+
if (zizmorCode !== 0) {
82+
process.exitCode = zizmorCode
83+
}
84+
}
85+
86+
main().catch((e: unknown) => {
87+
logger.error(e)
88+
process.exitCode = 1
89+
})

scripts/update.mts

Lines changed: 61 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,72 @@
11
/**
2-
* @fileoverview Monorepo-aware dependency update script.
3-
* Uses taze to update dependencies across all packages in the monorepo.
2+
* Update: two-pass taze to apply the fleet's maturity policy
3+
* correctly.
44
*
5-
* Usage:
6-
* node scripts/update.mts [options]
5+
* Pass 1: default config (.config/taze.config.mts) —
6+
* non-Socket deps respect maturityPeriod: 7.
77
*
8-
* Options:
9-
* --quiet Suppress progress output
10-
* --verbose Show detailed output
8+
* Pass 2: CLI-flag override — Socket-owned scopes only,
9+
* maturityPeriod: 0. taze's config auto-discovery is
10+
* path-based and doesn't support a --config override, so
11+
* the second pass uses `--include <scopes> --maturity-
12+
* period 0` flags instead of a second config file.
13+
*
14+
* Pass 3: pnpm install to refresh the lockfile against the
15+
* updated package.json.
16+
*
17+
* SOCKET_SCOPES below MUST match the `exclude` list in
18+
* .config/taze.config.mts — drift causes double-bumps or
19+
* misses.
20+
*
21+
* This is a reference script. Consuming repos can drop it into
22+
* their own scripts/ dir and wire it in via a `"update": "node
23+
* scripts/update.mts"` package.json entry.
1124
*/
25+
import { spawn } from '@socketsecurity/lib/spawn'
1226

13-
import process from 'node:process'
14-
15-
import { isQuiet, isVerbose } from '@socketsecurity/lib-stable/argv/flags'
16-
import { WIN32 } from '@socketsecurity/lib-stable/constants/platform'
17-
import { getDefaultLogger } from '@socketsecurity/lib-stable/logger'
18-
import { spawn } from '@socketsecurity/lib-stable/spawn'
19-
20-
async function main(): Promise<void> {
21-
const quiet = isQuiet()
22-
const verbose = isVerbose()
23-
const logger = getDefaultLogger()
24-
27+
async function run(cmd: string, args: string[]): Promise<boolean> {
2528
try {
26-
if (!quiet) {
27-
logger.log('\n🔨 Dependency Update\n')
28-
}
29-
30-
// Build taze command with appropriate flags for monorepo
31-
const tazeArgs = ['exec', 'taze', '-r', '-w']
32-
33-
if (!quiet) {
34-
logger.progress('Updating dependencies...')
35-
}
36-
37-
// Run taze at root level (recursive flag will check all packages).
38-
const result = await spawn('pnpm', tazeArgs, {
39-
shell: WIN32,
40-
stdio: quiet ? 'pipe' : 'inherit',
41-
})
42-
43-
// Clear progress line.
44-
if (!quiet) {
45-
process.stdout.write('\r\x1b[K')
46-
}
47-
48-
// Update Socket packages — bypass minimum-release-age since these are
49-
// our own packages and we trust them immediately.
50-
if (!quiet) {
51-
logger.progress('Updating Socket packages...')
52-
}
53-
54-
const socketResult = await spawn(
55-
'pnpm',
56-
[
57-
'update',
58-
'@socketsecurity/*',
59-
'@socketregistry/*',
60-
'@socketbin/*',
61-
'--latest',
62-
'-r',
63-
],
64-
{
65-
env: { ...process.env, npm_config_minimum_release_age: '0' },
66-
shell: WIN32,
67-
stdio: quiet ? 'pipe' : 'inherit',
68-
},
69-
)
29+
await spawn(cmd, args, { stdio: 'inherit' })
30+
return true
31+
} catch (e) {
32+
process.exitCode = (e as { code?: number }).code ?? 1
33+
return false
34+
}
35+
}
7036

71-
if (!quiet) {
72-
process.stdout.write('\r\x1b[K')
73-
}
37+
/* Socket-owned scopes — keep in lockstep with the exclude list
38+
* in .config/taze.config.mts. */
39+
const SOCKET_SCOPES = [
40+
'@socketregistry/*',
41+
'@socketsecurity/*',
42+
'@socketdev/*',
43+
'socket-*',
44+
'ecc-agentshield',
45+
'sfw',
46+
]
7447

75-
if (socketResult.code !== 0) {
76-
if (!quiet) {
77-
logger.fail('Failed to update Socket packages')
78-
}
79-
process.exitCode = 1
80-
return
81-
}
48+
const steps: Array<[string, string[]]> = [
49+
/* Pass 1 — third-party deps, respects the 7-day cooldown. */
50+
['pnpm', ['exec', 'taze']],
51+
/* Pass 2 — Socket deps, no cooldown. --include is comma-separated. */
52+
[
53+
'pnpm',
54+
[
55+
'exec',
56+
'taze',
57+
'--include',
58+
SOCKET_SCOPES.join(','),
59+
'--maturity-period',
60+
'0',
61+
'--write',
62+
],
63+
],
64+
/* Pass 3 — resync lockfile against the updated package.json. */
65+
['pnpm', ['install']],
66+
]
8267

83-
if (result.code !== 0) {
84-
if (!quiet) {
85-
logger.fail('Failed to update dependencies')
86-
}
87-
process.exitCode = 1
88-
} else {
89-
if (!quiet) {
90-
logger.success('Dependencies updated')
91-
logger.log('')
92-
}
93-
}
94-
} catch (e) {
95-
if (!quiet) {
96-
logger.fail(`Update failed: ${e.message}`)
97-
}
98-
if (verbose) {
99-
logger.error(e)
100-
}
101-
process.exitCode = 1
68+
for (const [cmd, args] of steps) {
69+
if (!(await run(cmd, args))) {
70+
break
10271
}
10372
}
104-
105-
main().catch((e: unknown) => {
106-
const logger = getDefaultLogger()
107-
logger.error(e)
108-
process.exitCode = 1
109-
})

0 commit comments

Comments
 (0)