Skip to content

Commit 6bb3275

Browse files
committed
chore: sync to socket-repo-template@899813d
- Adopt canonical scripts/security.mts (cross-platform which + spawn) - Update scripts/update.mts + .config/taze.config.mts to template - package.json: scripts.security delegates to wrapper Verified: 0 findings against template.
1 parent 5ddc43a commit 6bb3275

4 files changed

Lines changed: 162 additions & 148 deletions

File tree

.config/taze.config.mts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,40 @@
11
import { defineConfig } from 'taze'
22

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+
326
export default defineConfig({
4-
// Exclude these packages.
5-
exclude: [
6-
'all-the-package-names',
7-
'all-the-package-names-v1.3905.0',
8-
'eslint-plugin-unicorn',
9-
],
1027
// Interactive mode disabled for automation.
1128
interactive: false,
12-
// Use minimal logging.
29+
// Minimal logging.
1330
loglevel: 'warn',
14-
// Only update packages that have been stable for 7 days.
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.
1535
maturityPeriod: 7,
16-
// Update mode: 'latest'.
36+
// Bump to latest across major boundaries.
1737
mode: 'latest',
18-
// Write to package.json automatically.
38+
// Edit package.json in place.
1939
write: true,
2040
})

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
"format": "oxfmt --write .",
5555
"format:check": "oxfmt --check .",
5656
"lint": "node scripts/lint.mts",
57-
"security": "agentshield scan && { command -v zizmor >/dev/null && zizmor .github/ || echo 'zizmor not installed — run pnpm run setup to install'; }",
57+
"security": "node scripts/security.mts",
5858
"precommit": "pnpm run check --lint --staged",
5959
"prepare": "husky",
6060
"prepublishOnly": "echo 'ERROR: Use GitHub Actions workflow for publishing' && exit 1",

scripts/security.mts

Lines changed: 71 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,89 @@
11
/**
2-
* @fileoverview Security scan runner. Runs agentshield on Claude config then
3-
* optionally runs zizmor against .github/. Cross-platform replacement for the
4-
* previous inline shell script.
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.
527
*/
628

729
import process from 'node:process'
830

31+
import which from 'which'
32+
33+
import { WIN32 } from '@socketsecurity/lib/constants/platform'
934
import { getDefaultLogger } from '@socketsecurity/lib/logger'
10-
import type { Logger } from '@socketsecurity/lib/logger'
11-
import { spawnSync } from '@socketsecurity/lib/spawn'
12-
13-
import { runCommand } from './utils/run-command.mts'
14-
import { errorMessage } from './utils/error-message.mts'
15-
16-
const logger: Logger = getDefaultLogger()
17-
18-
function hasCommand(command: string): boolean {
19-
const probe = process.platform === 'win32' ? 'where' : 'command'
20-
const args = process.platform === 'win32' ? [command] : ['-v', command]
21-
const result = spawnSync(probe, args, {
22-
stdio: 'ignore',
23-
shell: process.platform === 'win32',
24-
})
25-
return result.status === 0
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+
}
2646
}
2747

28-
async function main(): Promise<void> {
29-
const agentshieldCode = await runCommand('agentshield', ['scan'])
30-
if (agentshieldCode !== 0) {
31-
process.exitCode = agentshieldCode
32-
return
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
3361
}
62+
}
3463

35-
if (hasCommand('zizmor')) {
36-
const zizmorCode = await runCommand('zizmor', ['.github/'])
37-
if (zizmorCode !== 0) {
38-
process.exitCode = zizmorCode
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
3971
return
4072
}
41-
} else {
42-
logger.info('zizmor not installed — run pnpm run setup to install')
4373
}
4474

45-
process.exitCode = 0
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+
}
4684
}
4785

48-
void main().catch((e: unknown) => {
49-
const message = errorMessage(e)
50-
logger.error(`security scan failed: ${message}`)
86+
main().catch((e: unknown) => {
87+
logger.error(e)
5188
process.exitCode = 1
5289
})

scripts/update.mts

Lines changed: 60 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +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
*/
12-
13-
import process from 'node:process'
14-
15-
import { isQuiet, isVerbose } from '@socketsecurity/lib/argv/flags'
16-
import { WIN32 } from '@socketsecurity/lib/constants/platform'
17-
import type { Logger } from '@socketsecurity/lib/logger'
18-
import { getDefaultLogger } from '@socketsecurity/lib/logger'
19-
import type { SpawnResult } from '@socketsecurity/lib/spawn'
2025
import { spawn } from '@socketsecurity/lib/spawn'
21-
import { errorMessage } from './utils/error-message.mts'
22-
23-
function getErrorMessage(error: unknown): string {
24-
return errorMessage(error)
25-
}
26-
27-
async function main(): Promise<void> {
28-
const quiet: boolean = isQuiet()
29-
const verbose: boolean = isVerbose()
30-
const logger: Logger = getDefaultLogger()
3126

27+
async function run(cmd: string, args: string[]): Promise<boolean> {
3228
try {
33-
if (!quiet) {
34-
logger.log('\n🔨 Dependency Update\n')
35-
}
36-
37-
// Build taze command with appropriate flags for monorepo
38-
const tazeArgs: string[] = ['exec', 'taze', '-r', '-w']
39-
40-
if (!quiet) {
41-
logger.progress('Updating dependencies...')
42-
}
43-
44-
// Run taze at root level (recursive flag will check all packages).
45-
const result: Awaited<SpawnResult> = await spawn('pnpm', tazeArgs, {
46-
shell: WIN32,
47-
stdio: quiet ? 'pipe' : 'inherit',
48-
})
49-
50-
// Clear progress line.
51-
if (!quiet) {
52-
process.stdout.write('\r\x1b[K')
53-
}
54-
55-
// Always update Socket packages (bypass taze maturity period).
56-
if (!quiet) {
57-
logger.progress('Updating Socket packages...')
58-
}
59-
60-
const socketResult: Awaited<SpawnResult> = await spawn(
61-
'pnpm',
62-
[
63-
'update',
64-
'@socketsecurity/*',
65-
'@socketregistry/*',
66-
'@socketbin/*',
67-
'--latest',
68-
'-r',
69-
],
70-
{
71-
shell: WIN32,
72-
stdio: quiet ? 'pipe' : 'inherit',
73-
},
74-
)
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+
}
7536

76-
// Clear progress line.
77-
if (!quiet) {
78-
process.stdout.write('\r\x1b[K')
79-
}
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+
]
8047

81-
if (socketResult.code !== 0) {
82-
if (!quiet) {
83-
logger.fail('Failed to update Socket packages')
84-
}
85-
process.exitCode = 1
86-
return
87-
}
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+
]
8867

89-
if (result.code !== 0) {
90-
if (!quiet) {
91-
logger.fail('Failed to update dependencies')
92-
}
93-
process.exitCode = 1
94-
} else {
95-
if (!quiet) {
96-
logger.success('Dependencies updated')
97-
logger.log('')
98-
}
99-
}
100-
} catch (e) {
101-
if (!quiet) {
102-
logger.fail(`Update failed: ${getErrorMessage(e)}`)
103-
}
104-
if (verbose) {
105-
logger.error(e)
106-
}
107-
process.exitCode = 1
68+
for (const [cmd, args] of steps) {
69+
if (!(await run(cmd, args))) {
70+
break
10871
}
10972
}
110-
111-
main().catch((error: unknown) => {
112-
const logger: Logger = getDefaultLogger()
113-
logger.error(error)
114-
process.exitCode = 1
115-
})

0 commit comments

Comments
 (0)