@socketsecurity/mcp 0.0.20 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@socketsecurity/mcp",
3
- "version": "0.0.20",
3
+ "version": "0.2.1",
4
4
  "description": "Socket MCP server for scanning dependencies",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/SocketDev/socket-mcp#readme",
@@ -18,7 +18,7 @@
18
18
  "files": [
19
19
  "CHANGELOG.md",
20
20
  "dist/index.cjs",
21
- "hooks/socket-gate",
21
+ "dist/socket-gate",
22
22
  "package.json"
23
23
  ],
24
24
  "type": "module",
@@ -28,81 +28,266 @@
28
28
  "provenance": true
29
29
  },
30
30
  "devDependencies": {
31
- "@anthropic-ai/mcpb": "^1.2.0",
32
- "@modelcontextprotocol/sdk": "1.26.0",
33
- "@redwoodjs/agent-ci": "0.16.2",
34
- "@sinclair/typebox": "0.34.49",
35
- "@socketregistry/packageurl-js": "1.4.2",
36
- "@socketregistry/packageurl-js-stable": "npm:@socketregistry/packageurl-js@1.4.2",
37
- "@socketsecurity/lib": "6.0.8",
38
- "@socketsecurity/lib-stable": "npm:@socketsecurity/lib@6.0.8",
39
- "@socketsecurity/sdk": "4.0.1",
40
- "@socketsecurity/sdk-stable": "npm:@socketsecurity/sdk@4.0.1",
41
- "@types/node": "^24.13.1",
31
+ "@anthropic-ai/mcpb": "1.2.0",
32
+ "@bcoe/v8-coverage": "1.0.2",
33
+ "@mdn/browser-compat-data": "8.1.0",
34
+ "@modelcontextprotocol/client": "2.0.0",
35
+ "@modelcontextprotocol/core": "2.0.0",
36
+ "@modelcontextprotocol/node": "2.0.0",
37
+ "@modelcontextprotocol/server": "2.0.0",
38
+ "@playwright/mcp": "0.0.80",
39
+ "@shadscan/cli": "0.17.0",
40
+ "@sinclair/typebox": "0.34.52",
41
+ "@socketregistry/packageurl-js": "1.5.2",
42
+ "@socketregistry/packageurl-js-stable": "npm:@socketregistry/packageurl-js@1.5.2",
43
+ "@socketsecurity/lib": "7.0.1",
44
+ "@socketsecurity/lib-stable": "npm:@socketsecurity/lib@7.0.1",
45
+ "@socketsecurity/sdk": "4.1.4",
46
+ "@socketsecurity/sdk-stable": "npm:@socketsecurity/sdk@4.1.4",
47
+ "@types/mdast": "4.0.4",
48
+ "@types/node": "26.5.1",
49
+ "@types/semver": "7.8.0",
42
50
  "@types/shell-quote": "1.7.5",
43
51
  "@typescript/native-preview": "7.0.0-dev.20260510.1",
44
- "@vitest/coverage-v8": "4.1.8",
45
- "nock": "14.0.15",
46
- "npm-run-all2": "^8.0.4",
47
- "oxfmt": "0.48.0",
48
- "oxlint": "1.63.0",
49
- "regjsparser": "0.13.1",
50
- "rolldown": "1.1.0",
51
- "shell-quote": "1.8.4",
52
- "taze": "19.14.1",
53
- "untracked": "1.6.1",
54
- "vitest": "4.1.8"
52
+ "@ultrathink/acorn.rs.wasm": "0.1.1",
53
+ "@vitest/coverage-v8": "5.0.0",
54
+ "@vitest/ui": "5.0.0",
55
+ "@vitiate/core": "0.3.1",
56
+ "ast-v8-to-istanbul": "1.0.6",
57
+ "ata-validator": "1.25.0",
58
+ "c8": "12.0.0",
59
+ "chrome-devtools-mcp": "1.9.0",
60
+ "fast-check": "4.9.0",
61
+ "magic-string": "1.2.3",
62
+ "markdownlint-cli2": "0.23.2",
63
+ "mcp-tada": "0.4.0",
64
+ "mdast-util-from-markdown": "2.0.3",
65
+ "mdast-util-gfm": "3.1.0",
66
+ "mdast-util-to-markdown": "2.1.2",
67
+ "micromark": "4.0.2",
68
+ "micromark-extension-gfm": "3.0.0",
69
+ "neosanitize": "0.3.0",
70
+ "nock": "14.0.17",
71
+ "npm-high-impact": "1.13.0",
72
+ "oxfmt": "0.67.0",
73
+ "oxlint": "1.82.0",
74
+ "oxlint-tsgolint": "7.0.2001",
75
+ "parse5": "8.0.1",
76
+ "playwright-core": "1.63.0",
77
+ "portless": "0.15.6",
78
+ "regjsparser": "0.13.2",
79
+ "rolldown": "1.2.8",
80
+ "run-local-ci": "0.18.1",
81
+ "semver": "7.8.5",
82
+ "shell-quote": "1.10.0",
83
+ "svgo": "4.1.0",
84
+ "taze": "21.1.0",
85
+ "typebox": "1.3.30",
86
+ "typescript": "7.1.0-dev.20260909.1",
87
+ "untracked": "1.6.5",
88
+ "vitest": "5.0.0",
89
+ "vitiate": "0.3.1",
90
+ "yaml": "2.9.0"
91
+ },
92
+ "devEngines": {
93
+ "packageManager": [
94
+ {
95
+ "name": "npm",
96
+ "version": "^11.19.0 || >=12.0.2",
97
+ "onFail": "error"
98
+ },
99
+ {
100
+ "name": "pnpm",
101
+ "version": "^11.25.0 || >=12.3.4",
102
+ "onFail": "error"
103
+ }
104
+ ]
55
105
  },
56
106
  "engines": {
57
- "node": ">=22.0.0",
58
- "npm": ">=11.16.0"
107
+ "node": ">=24",
108
+ "npm": "^11.19.0 || >=12.0.2",
109
+ "pnpm": "^11.25.0 || >=12.3.4"
59
110
  },
60
111
  "allowScripts": {
61
112
  "cpu-features": false,
62
113
  "esbuild": true,
63
114
  "protobufjs": false,
115
+ "puppeteer": false,
64
116
  "ssh2": false
65
117
  },
66
- "npm-run-all2": {
67
- "nodeRun": true
68
- },
69
118
  "scripts": {
119
+ "npm:publish": "node scripts/fleet/npm/publish-npm.mts",
70
120
  "test": "node scripts/fleet/test.mts",
71
- "test:e2e": "vitest run --config .config/repo/vitest.config.mts test/e2e/",
121
+ "test:e2e": "node scripts/fleet/test.mts test/repo/e2e",
122
+ "test:fuzz": "node scripts/repo/fuzz.mts",
72
123
  "cover": "node scripts/fleet/cover.mts",
73
124
  "test:tsc": "tsgo --noEmit -p tsconfig.json",
74
125
  "lint": "node scripts/fleet/lint.mts",
75
126
  "lint:all": "node scripts/fleet/lint.mts --all",
76
- "format": "oxfmt -c .config/fleet/oxfmtrc.json --write .",
77
- "format:check": "oxfmt -c .config/fleet/oxfmtrc.json --check .",
127
+ "format": "node scripts/fleet/format.mts",
128
+ "format:check": "node scripts/fleet/format.mts --check",
78
129
  "check": "node scripts/fleet/check.mts",
79
130
  "check:all": "node scripts/fleet/check.mts --all",
80
131
  "fix": "node scripts/fleet/fix.mts",
81
132
  "fix:all": "node scripts/fleet/fix.mts --all",
82
133
  "update": "node scripts/fleet/update.mts",
83
- "build": "npm run clean && run-s build:*",
84
- "build:bundle": "node scripts/build.mts",
134
+ "build": "pnpm run clean && pnpm run build:bundle && pnpm run build:permissions",
135
+ "build:bundle": "node scripts/repo/build.mts",
85
136
  "build:permissions": "chmod +x ./dist/index.cjs",
86
- "build-mcpb": "run-s build build-mcpb:*",
87
- "build-mcpb:versions_match": "node scripts/check-versions.ts",
137
+ "build-mcpb": "pnpm run build && pnpm run build-mcpb:versions_match && pnpm run build-mcpb:validate && pnpm run build-mcpb:ensure-deps && pnpm run build-mcpb:mcpb-pack",
138
+ "build-mcpb:versions_match": "node scripts/repo/check-versions.mts",
88
139
  "build-mcpb:validate": "mcpb validate ./",
89
140
  "build-mcpb:ensure-deps": "npm install --production --ignore-scripts",
90
141
  "build-mcpb:mcpb-pack": "mcpb pack ./",
91
- "clean": "bash ./scripts/clean.sh",
92
- "debug-stdio": "node ./mock-client/debug-client.ts",
93
- "debug-sdk": "node ./mock-client/stdio-client.ts",
94
- "debug-http": "node ./mock-client/http-client.ts",
95
- "server-stdio": "SOCKET_API_TOKEN=${SOCKET_API_TOKEN:-${SOCKET_API_KEY}} node ./index.ts",
96
- "server-stdio:debug": "SOCKET_DEBUG=1 SOCKET_API_TOKEN=${SOCKET_API_TOKEN:-${SOCKET_API_KEY}} node ./index.ts",
97
- "server-http": "MCP_HTTP_MODE=true SOCKET_API_TOKEN=${SOCKET_API_TOKEN:-${SOCKET_API_KEY}} node ./index.ts",
98
- "server-http:debug": "SOCKET_DEBUG=1 MCP_HTTP_MODE=true SOCKET_API_TOKEN=${SOCKET_API_TOKEN:-${SOCKET_API_KEY}} node ./index.ts",
99
- "check:paths": "node scripts/fleet/check/paths-are-canonical.mts",
142
+ "clean": "node scripts/fleet/clean.mts",
143
+ "debug-stdio": "node ./mock-client/debug-client.mts",
144
+ "debug-sdk": "node ./mock-client/stdio-client.mts",
145
+ "debug-http": "node ./mock-client/http-client.mts",
146
+ "server-stdio": "pnpm run build:bundle && SOCKET_API_TOKEN=${SOCKET_API_TOKEN:-${SOCKET_API_KEY}} node ./dist/index.cjs",
147
+ "server-stdio:debug": "pnpm run build:bundle && SOCKET_DEBUG=1 SOCKET_API_TOKEN=${SOCKET_API_TOKEN:-${SOCKET_API_KEY}} node ./dist/index.cjs",
148
+ "server-http": "pnpm run build:bundle && MCP_HTTP_MODE=true SOCKET_API_TOKEN=${SOCKET_API_TOKEN:-${SOCKET_API_KEY}} node ./dist/index.cjs",
149
+ "server-http:debug": "pnpm run build:bundle && SOCKET_DEBUG=1 MCP_HTTP_MODE=true SOCKET_API_TOKEN=${SOCKET_API_TOKEN:-${SOCKET_API_KEY}} node ./dist/index.cjs",
150
+ "check:paths": "node scripts/fleet/check/paths-are-valid.mts",
100
151
  "security": "node scripts/fleet/security.mts",
101
152
  "setup-security-tools": "node .claude/hooks/fleet/setup-security-tools/install.mts",
102
153
  "lockstep": "node scripts/fleet/lockstep.mts",
103
154
  "lockstep:emit-schema": "node scripts/fleet/lockstep-emit-schema.mts",
104
- "ci:local": "node scripts/fleet/agent-ci-skip-locks.mts run --all --quiet --pause-on-failure --github-token",
155
+ "lockstep:emit-mirror-globs": "node scripts/fleet/lockstep-emit-mirror-globs.mts",
156
+ "ci:local": "node scripts/fleet/ci/local/run.mts run --all --quiet --pause-on-failure --github-token",
105
157
  "weekly-update": "node scripts/fleet/weekly-update.mts",
106
- "weekly-update:ci": "node scripts/fleet/weekly-update-workflow.mts run"
158
+ "doctor:auth": "node scripts/fleet/check/setup-is-prompt-less.mts",
159
+ "sync-oxlint-rules": "node scripts/fleet/sync-oxlint-rules.mts",
160
+ "sync-package-manager-pins": "node scripts/fleet/sync-package-manager-pins.mts",
161
+ "doctor": "node scripts/fleet/doctor.mts",
162
+ "setup:brew": "node scripts/fleet/setup/brew.mts",
163
+ "setup:go": "node scripts/fleet/setup/go.mts",
164
+ "setup:python": "node scripts/fleet/setup/python.mts",
165
+ "setup:refero": "node scripts/fleet/setup/refero.mts",
166
+ "setup:rust": "node scripts/fleet/setup/rust.mts",
167
+ "type": "node scripts/fleet/type.mts",
168
+ "socket-wheelhouse:emit-schema": "node scripts/fleet/socket-wheelhouse-emit-schema.mts",
169
+ "mcp:reset": "node scripts/fleet/mcp/reset.mts",
170
+ "setup:sfw-ca": "node scripts/fleet/setup/sfw-ca.mts",
171
+ "weekly-update:ci": "gh workflow run cron-weekly-update.yml",
172
+ "prune:backups": "node scripts/fleet/backup-branches.mts prune",
173
+ "strip:ai-tags": "node scripts/fleet/strip-ai-tags.mts",
174
+ "npm:approve": "node scripts/fleet/npm/approve.mts",
175
+ "npm:auth": "node scripts/fleet/npm-auth.mts",
176
+ "npm:dispatch": "node scripts/fleet/registry-infra/remote-npm-publish.mts --publish",
177
+ "npm:staged": "node scripts/fleet/npm/staged.mts",
178
+ "hide-comments": "node scripts/fleet/hide-comments.mts",
179
+ "ruleset:bypass": "node scripts/fleet/grant-ruleset-bypass.mts",
180
+ "soak:bypass": "node scripts/fleet/soak-bypass.mts",
181
+ "npm:trust": "node scripts/fleet/registry-infra/npm/settings/trust.mts",
182
+ "prune:branch-backups": "node scripts/fleet/backup-branches.mts prune",
183
+ "preflight": "node scripts/repo/bootstrap/fleet.mjs --quiet && node scripts/fleet/preflight.mts",
184
+ "npm:auth:browser": "node scripts/fleet/npm-auth-browser.mts",
185
+ "npm:auth:cli": "node scripts/fleet/npm-auth-cli.mts",
186
+ "prune:gha-caches": "node scripts/fleet/prune-actions-caches.mts",
187
+ "check:fleet": "node scripts/fleet/check.mts",
188
+ "fix:js": "node scripts/fleet/fix.mts --js",
189
+ "fix:repo": "node scripts/fleet/fix.mts --repo",
190
+ "fmt:js": "node scripts/fleet/format.mts",
191
+ "lint:js": "node scripts/fleet/lint.mts",
192
+ "gh:auth": "node scripts/fleet/gh-auth.mts",
193
+ "sync-global-hooks": "node scripts/fleet/sync-global-hooks.mts",
194
+ "backup": "node scripts/fleet/backup-work.mts",
195
+ "offload:model": "node scripts/fleet/offload-model.mts",
196
+ "setup:offload-providers": "node scripts/fleet/setup/offload-providers.mts",
197
+ "build:hook-bundle": "node scripts/fleet/build-hook-bundle.mts",
198
+ "build:hook-snapshot": "node scripts/fleet/build-hook-snapshot.mts",
199
+ "build:snapshot-launcher": "node scripts/fleet/build-snapshot-launcher.mts",
200
+ "gen:glyph": "node scripts/fleet/gen/glyph.mts",
201
+ "mcp:schema:update": "mcp-tada introspect --command node --arg index.mts --env SOCKET_API_TOKEN=YOUR_API_TOKEN --env MCP_HTTP_MODE=false --timeout 5000 --json --out test/integration/server-schema.golden.json && pnpm run format test/integration/server-schema.golden.json",
202
+ "mcp:schema:check": "mcp-tada check --command node --arg index.mts --env SOCKET_API_TOKEN=YOUR_API_TOKEN --env MCP_HTTP_MODE=false --timeout 5000 --against test/integration/server-schema.golden.json",
203
+ "mcp:introspect": "node scripts/fleet/mcp/schemas.mts introspect",
204
+ "mcp:check": "node scripts/fleet/mcp/schemas.mts check",
205
+ "cover:types": "node scripts/fleet/cover/types/run.mts",
206
+ "audit:consumer-source": "node scripts/fleet/audit/consumer-source.mts",
207
+ "ai-balancer": "node scripts/fleet/ai/balancer/launch.mts",
208
+ "ai-balancer:auth": "node scripts/fleet/ai/balancer/login.mts",
209
+ "ai-balancer:refresh": "node scripts/fleet/ai/balancer/service.mts refresh",
210
+ "ai-balancer:service": "node scripts/fleet/ai/balancer/service.mts",
211
+ "ai:odai:cache": "node scripts/fleet/ai/odai/run.mts cache",
212
+ "ai:odai:diagnose": "node scripts/fleet/ai/odai/run.mts diagnose",
213
+ "ai:read": "node scripts/fleet/ai/bounded-reader.mts",
214
+ "spend-usage": "node scripts/fleet/spend-usage.mts",
215
+ "update:chrome": "node scripts/fleet/update/chrome.mts",
216
+ "build:oxlint-bundle": "node scripts/fleet/build-oxlint-bundle.mts",
217
+ "check:test-budget-headroom": "node scripts/fleet/check/test-budget-headroom.mts",
218
+ "check:workspace-installation": "node scripts/fleet/check/workspace-installation.mts",
219
+ "depot:auth": "node scripts/fleet/registry-infra/depot/browser-auth.mts",
220
+ "fleet:update": "node scripts/repo/bootstrap/fleet.mjs",
221
+ "integrity-refresh": "node scripts/fleet/external-tools/refresh.mts",
222
+ "setup:1password": "node scripts/fleet/setup/one-password.mts",
223
+ "setup:ci:local": "node scripts/fleet/ci/local/setup.mts",
224
+ "setup:ci:remote": "node scripts/fleet/ci/remote/setup.mts",
225
+ "setup:ai-clients": "node scripts/fleet/setup/ai-clients.mts",
226
+ "setup:pgbot": "node scripts/fleet/setup/pgbot.mts",
227
+ "mcp:connect": "node scripts/fleet/mcp/connect.mts",
228
+ "mcp:pgbot": "node scripts/fleet/mcp/pgbot/run.mts",
229
+ "mcp:schemas:check": "node scripts/fleet/mcp/schemas.mts check",
230
+ "mcp:schemas:save": "node scripts/fleet/mcp/schemas.mts save",
231
+ "mcp:status": "node scripts/fleet/mcp/connect.mts --status",
232
+ "npm:inventory": "node scripts/fleet/registry-infra/npm/account-inventory-read.mts",
233
+ "soak:check": "node scripts/fleet/check/soak-time-is-consistent.mts",
234
+ "sweep:worktrees": "node scripts/fleet/worktree-sweep.mts",
235
+ "sweep:worktrees:visit": "node scripts/fleet/worktree/sweep/run.mts",
236
+ "test:profile": "node scripts/fleet/test/profile.mts",
237
+ "update:pnpm": "node scripts/fleet/update/pnpm.mts",
238
+ "release:hint": "node scripts/fleet/release/hint.mts",
239
+ "slack:auth": "node scripts/fleet/mcp/connect.mts slack --client claude --reauthenticate",
240
+ "slack:token": "node scripts/fleet/setup/slack-token.mts",
241
+ "lockstep:assist": "node scripts/fleet/lockstep/assist/run.mts",
242
+ "mcp:1password": "node scripts/fleet/mcp/1password/run.mts",
243
+ "github:browser": "node scripts/fleet/browser/github-auth.mts",
244
+ "remote:github:release": "node scripts/fleet/release/github/remote.mts",
245
+ "worktree:create": "node scripts/fleet/worktree/create/run.mts",
246
+ "npm:trust:migrate": "node scripts/fleet/npm/trusted-publisher/run.mts",
247
+ "build:test": "node scripts/fleet/commands/run.mts build test",
248
+ "build:update": "node scripts/fleet/commands/run.mts build update",
249
+ "check:test": "node scripts/fleet/commands/run.mts check test",
250
+ "check:update": "node scripts/fleet/commands/run.mts check update",
251
+ "ci:test": "node scripts/fleet/commands/run.mts ci test",
252
+ "ci:update": "node scripts/fleet/commands/run.mts ci update",
253
+ "cover:test": "node scripts/fleet/commands/run.mts cover test",
254
+ "cover:update": "node scripts/fleet/commands/run.mts cover update",
255
+ "format:test": "node scripts/fleet/commands/run.mts format test",
256
+ "format:update": "node scripts/fleet/commands/run.mts format update",
257
+ "lint:test": "node scripts/fleet/commands/run.mts lint test",
258
+ "lint:update": "node scripts/fleet/commands/run.mts lint update",
259
+ "lockstep:test": "node scripts/fleet/commands/run.mts lockstep test",
260
+ "test:test": "node scripts/fleet/commands/run.mts test test",
261
+ "test:update": "node scripts/fleet/commands/run.mts test update",
262
+ "types:test": "node scripts/fleet/commands/run.mts types test",
263
+ "types:update": "node scripts/fleet/commands/run.mts types update",
264
+ "setup-all": "pnpm run setup:0-mise && pnpm run setup:1-token && pnpm run setup:2-security-tools && pnpm run setup:3-hook-snapshot && pnpm run setup:4-repo-steps",
265
+ "setup:0-mise": "node scripts/fleet/setup/mise.mts --bootstrap",
266
+ "setup:1-token": "node scripts/fleet/setup/token.mts",
267
+ "setup:2-security-tools": "node .claude/hooks/fleet/setup-security-tools/install.mts",
268
+ "setup:3-hook-snapshot": "node scripts/fleet/setup/hook-snapshot.mts",
269
+ "setup:4-repo-steps": "node scripts/fleet/setup/repo-steps.mts",
270
+ "setup:mise": "node scripts/fleet/setup/mise.mts",
271
+ "check:fix": "node scripts/fleet/check/fix.mts",
272
+ "ci:fix": "node scripts/fleet/ci/fix/run.mts",
273
+ "ci:gates": "node scripts/fleet/ci/gates/run.mts",
274
+ "cover:allowance": "node scripts/fleet/cover/allowance.mts",
275
+ "cover:balance": "node scripts/fleet/cover/balance.mts",
276
+ "cover:fix": "node scripts/fleet/cover/fix.mts",
277
+ "lint:fix": "node scripts/fleet/lint/fix.mts",
278
+ "lockstep:fix": "node scripts/fleet/commands/run.mts lockstep fix",
279
+ "lockstep:update": "node scripts/fleet/lockstep/update.mts",
280
+ "test:allowance": "node scripts/fleet/test/allowance.mts",
281
+ "test:balance": "node scripts/fleet/test/balance.mts",
282
+ "test:fix": "node scripts/fleet/test/fix.mts",
283
+ "types:fix": "node scripts/fleet/types/fix.mts",
284
+ "check:deps": "node scripts/fleet/check.mts --category=deps",
285
+ "check:hooks": "node scripts/fleet/check.mts --category=hooks",
286
+ "check:types": "node scripts/fleet/check.mts --category=types",
287
+ "setup:xcode-license": "node scripts/fleet/setup/xcode-license.mts",
288
+ "browser:restart": "node scripts/fleet/browser/session/run.mts restart",
289
+ "browser:start": "node scripts/fleet/browser/session/run.mts start",
290
+ "browser:status": "node scripts/fleet/browser/session/run.mts status",
291
+ "browser:stop": "node scripts/fleet/browser/session/run.mts stop"
107
292
  }
108
293
  }
@@ -1,296 +0,0 @@
1
- /**
2
- * Socket-gate — Claude Code PreToolUse hook (source for the bundled
3
- * socket-gate.cjs).
4
- *
5
- * Intercepts package install commands across npm, PyPI, Cargo, RubyGems, Go,
6
- * and NuGet, and checks the target package against the public Socket MCP
7
- * server. Blocks installs when the supply chain score is below 20 (known
8
- * malware, typosquats, high-risk supply chain signals).
9
- *
10
- * No API key, no CLI, no registration beyond copying the bundled directory.
11
- *
12
- * Setup (see README.md):
13
- *
14
- * 1. Copy the whole hooks/socket-gate/ directory to ~/.claude/hooks/ (from a
15
- * published @socketsecurity/mcp install, or build it here with `pnpm run
16
- * build`).
17
- * 2. Point a PreToolUse Bash hook at ~/.claude/hooks/socket-gate/socket-gate.cjs
18
- * in ~/.claude/settings.json.
19
- *
20
- * This source imports @socketsecurity/lib-stable; rolldown inlines it into the
21
- * bundled socket-gate.cjs so the copied-out hook stays self-contained (a Claude
22
- * Code hook has no package.json and no node_modules — it can only run what is
23
- * physically present in the file).
24
- *
25
- * Fails open on network, parse, and timeout errors so a Socket outage does not
26
- * block legitimate work.
27
- */
28
-
29
- import { readFileSync } from 'node:fs'
30
- import { argv } from 'node:process'
31
- import { fileURLToPath } from 'node:url'
32
-
33
- import { errorMessage } from '@socketsecurity/lib-stable/errors'
34
-
35
- const MCP_URL = 'https://mcp.socket.dev/'
36
- const SUPPLY_CHAIN_THRESHOLD = 20
37
- const REQUEST_TIMEOUT_MS = 10_000
38
-
39
- export type Ecosystem = 'npm' | 'pypi' | 'cargo' | 'gem' | 'golang' | 'nuget'
40
-
41
- export interface HookInput {
42
- session_id: string
43
- tool_name: string
44
- tool_input: Record<string, unknown> | string
45
- }
46
-
47
- // Each pattern matches "<tool> <install-subcommand> <pkg>" and captures the
48
- // first non-flag argument ([^\s-] rejects a leading dash, [^\s]* takes the
49
- // rest) as the package spec to scan. The (?:…) groups list a tool's install
50
- // aliases (npm add|i|install, cargo add|install, go get|install) without
51
- // capturing them.
52
- const INSTALL_PATTERNS: Array<{ ecosystem: Ecosystem; pattern: RegExp }> = [
53
- { ecosystem: 'npm', pattern: /\bnpm\s+(?:add|i|install)\s+([^\s-][^\s]*)/i }, // socket-lint: allow uncommented-regex
54
- { ecosystem: 'npm', pattern: /\byarn\s+add\s+([^\s-][^\s]*)/i },
55
- { ecosystem: 'npm', pattern: /\bpnpm\s+add\s+([^\s-][^\s]*)/i },
56
- { ecosystem: 'npm', pattern: /\bbun\s+add\s+([^\s-][^\s]*)/i },
57
- {
58
- ecosystem: 'pypi',
59
- pattern: /(?:\bpython3?\s+-m\s+)?\bpip3?\s+install\s+([^\s-][^\s]*)/i, // socket-lint: allow uncommented-regex
60
- },
61
- { ecosystem: 'pypi', pattern: /\buv\s+add\s+([^\s-][^\s]*)/i },
62
- { ecosystem: 'pypi', pattern: /\buv\s+pip\s+install\s+([^\s-][^\s]*)/i },
63
- { ecosystem: 'pypi', pattern: /\bpoetry\s+add\s+([^\s-][^\s]*)/i },
64
- { ecosystem: 'pypi', pattern: /\bpipenv\s+install\s+([^\s-][^\s]*)/i },
65
- {
66
- ecosystem: 'cargo',
67
- pattern: /\bcargo\s+(?:add|install)\s+([^\s-][^\s]*)/i, // socket-lint: allow uncommented-regex
68
- },
69
- { ecosystem: 'gem', pattern: /\bgem\s+install\s+([^\s-][^\s]*)/i },
70
- { ecosystem: 'gem', pattern: /\bbundle\s+add\s+([^\s-][^\s]*)/i },
71
- { ecosystem: 'golang', pattern: /\bgo\s+(?:get|install)\s+([^\s-][^\s]*)/i }, // socket-lint: allow uncommented-regex
72
- { ecosystem: 'nuget', pattern: /\bdotnet\s+add\s+package\s+([^\s-][^\s]*)/i },
73
- { ecosystem: 'nuget', pattern: /\bnuget\s+install\s+([^\s-][^\s]*)/i },
74
- ]
75
-
76
- export async function checkPackage(
77
- ecosystem: Ecosystem,
78
- packageName: string,
79
- fetchImpl: typeof fetch = fetch,
80
- ): Promise<{ decision: 'allow' | 'deny'; reason: string }> {
81
- const signal = AbortSignal.timeout(REQUEST_TIMEOUT_MS)
82
- const commonHeaders = {
83
- 'Content-Type': 'application/json',
84
- Accept: 'application/json, text/event-stream',
85
- }
86
-
87
- const initRes = await fetchImpl(MCP_URL, {
88
- method: 'POST',
89
- headers: commonHeaders,
90
- body: JSON.stringify({
91
- jsonrpc: '2.0',
92
- id: 1,
93
- method: 'initialize',
94
- params: {
95
- protocolVersion: '2024-11-05',
96
- capabilities: {},
97
- clientInfo: { name: 'socket-gate', version: '1.0' },
98
- },
99
- }),
100
- signal,
101
- })
102
-
103
- if (!initRes.ok) {
104
- throw new Error(`Socket MCP initialize returned ${initRes.status}`)
105
- }
106
-
107
- const sessionId = initRes.headers.get('mcp-session-id')
108
- if (!sessionId) {
109
- throw new Error('Socket MCP did not return a session id')
110
- }
111
- await initRes.text()
112
-
113
- const callRes = await fetchImpl(MCP_URL, {
114
- method: 'POST',
115
- headers: { ...commonHeaders, 'Mcp-Session-Id': sessionId },
116
- body: JSON.stringify({
117
- jsonrpc: '2.0',
118
- id: 2,
119
- method: 'tools/call',
120
- params: {
121
- name: 'depscore',
122
- arguments: {
123
- packages: [{ ecosystem, depname: packageName }],
124
- },
125
- },
126
- }),
127
- signal,
128
- })
129
-
130
- if (!callRes.ok) {
131
- throw new Error(`Socket MCP depscore returned ${callRes.status}`)
132
- }
133
-
134
- const payload = (await callRes.json()) as {
135
- result?:
136
- | {
137
- content?: Array<{ type: string; text: string }> | undefined
138
- isError?: boolean | undefined
139
- }
140
- | undefined
141
- }
142
-
143
- if (payload.result?.isError) {
144
- throw new Error(
145
- 'Socket MCP reported a tool error (package likely not found)',
146
- )
147
- }
148
-
149
- const text = payload.result?.content?.[0]?.text ?? ''
150
- const score = parseSupplyChainScore(text)
151
-
152
- if (score === undefined) {
153
- throw new Error('Could not parse supplyChain score from MCP response')
154
- }
155
-
156
- if (score < SUPPLY_CHAIN_THRESHOLD) {
157
- return {
158
- decision: 'deny',
159
- reason: `Socket blocked "${packageName}" (${ecosystem}): supply chain score is ${score} (threshold ${SUPPLY_CHAIN_THRESHOLD}).\n\nReview: https://socket.dev/${ecosystem}/package/${packageName}`,
160
- }
161
- }
162
-
163
- return { decision: 'allow', reason: '' }
164
- }
165
-
166
- export function extractPackage(
167
- command: string,
168
- ): { ecosystem: Ecosystem; name: string } | undefined {
169
- for (const { ecosystem, pattern } of INSTALL_PATTERNS) {
170
- const match = command.match(pattern)
171
- if (match?.[1]) {
172
- const name = stripVersion(match[1], ecosystem)
173
- if (!name) {
174
- continue
175
- }
176
- return { ecosystem, name }
177
- }
178
- }
179
- return undefined
180
- }
181
-
182
- // stdout is the Claude Code hook IPC channel — the harness parses this exact
183
- // JSON as the permission decision, so these must be raw writes (a logger would
184
- // add formatting/timestamps and break the protocol).
185
- export function outputAllow(): void {
186
- const payload = JSON.stringify({
187
- hookSpecificOutput: {
188
- hookEventName: 'PreToolUse',
189
- permissionDecision: 'allow',
190
- },
191
- })
192
- process.stdout.write(payload) // socket-lint: allow (process-stdio: stdout is the hook decision protocol; raw write keeps the bundled hook free of logger indirection)
193
- }
194
-
195
- export function outputDeny(reason: string): void {
196
- const payload = JSON.stringify({
197
- hookSpecificOutput: {
198
- hookEventName: 'PreToolUse',
199
- permissionDecision: 'deny',
200
- permissionDecisionReason: reason,
201
- },
202
- })
203
- process.stdout.write(payload) // socket-lint: allow (process-stdio: stdout is the hook decision protocol; raw write keeps the bundled hook free of logger indirection)
204
- }
205
-
206
- export function parseSupplyChainScore(text: string): number | undefined {
207
- // "supplyChain:" then optional spaces, capturing an integer with an
208
- // optional ".<fraction>" decimal part (e.g. "supplyChain: 0.82").
209
- const match = text.match(/supplyChain:\s*(\d+(?:\.\d+)?)/i)
210
- return match ? Number(match[1]) : undefined
211
- }
212
-
213
- export function stripVersion(pkg: string, ecosystem: Ecosystem): string {
214
- switch (ecosystem) {
215
- case 'npm':
216
- return pkg.replace(/@[\d^~].*/u, '').replace(/@latest$/u, '')
217
- case 'pypi':
218
- return pkg.split(/[=<>!~[]/)[0] ?? pkg
219
- case 'cargo':
220
- case 'golang':
221
- case 'nuget':
222
- return pkg.replace(/@.*$/u, '')
223
- default:
224
- return pkg
225
- }
226
- }
227
-
228
- async function main(): Promise<void> {
229
- let raw: string
230
- try {
231
- raw = readFileSync(0, 'utf-8')
232
- } catch {
233
- outputAllow()
234
- return
235
- }
236
-
237
- if (!raw.trim()) {
238
- outputAllow()
239
- return
240
- }
241
-
242
- let input: HookInput
243
- try {
244
- input = JSON.parse(raw)
245
- } catch {
246
- outputAllow()
247
- return
248
- }
249
-
250
- if (input.tool_name !== 'Bash') {
251
- outputAllow()
252
- return
253
- }
254
-
255
- const command =
256
- typeof input.tool_input === 'string'
257
- ? input.tool_input
258
- : (input.tool_input?.['command'] as string) || ''
259
-
260
- if (!command) {
261
- outputAllow()
262
- return
263
- }
264
-
265
- const target = extractPackage(command)
266
- if (!target) {
267
- outputAllow()
268
- return
269
- }
270
-
271
- try {
272
- const result = await checkPackage(target.ecosystem, target.name)
273
- if (result.decision === 'deny') {
274
- outputDeny(result.reason)
275
- } else {
276
- outputAllow()
277
- }
278
- } catch (e) {
279
- // Deliberate fail-OPEN: a Socket outage / network error / parse failure
280
- // must not block legitimate installs (this hook is an advisory guardrail,
281
- // not a hard gate — see the file header). Surface the error on stderr so
282
- // the failure is observable; stdout stays the allow/deny IPC channel.
283
- const errLine = `socket-gate: check failed for ${target.ecosystem}/${target.name}, failing open: ${errorMessage(e)}\n`
284
- process.stderr.write(errLine) // socket-lint: allow (process-stdio: stderr feedback for the harness; raw write keeps the bundled hook free of logger indirection)
285
- outputAllow()
286
- }
287
- }
288
-
289
- const isMainModule =
290
- argv[1] !== undefined && fileURLToPath(import.meta.url) === argv[1]
291
-
292
- if (isMainModule) {
293
- main().catch(() => {
294
- outputAllow()
295
- })
296
- }