@cyanheads/pentest-mcp-server 0.1.5 → 0.1.7

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.
Files changed (68) hide show
  1. package/AGENTS.md +40 -27
  2. package/CLAUDE.md +40 -27
  3. package/Dockerfile +10 -6
  4. package/LICENSE +1 -1
  5. package/README.md +5 -4
  6. package/changelog/0.1.x/0.1.6.md +26 -0
  7. package/changelog/0.1.x/0.1.7.md +37 -0
  8. package/changelog/template.md +60 -19
  9. package/dist/data/encodings/index.d.ts.map +1 -1
  10. package/dist/data/encodings/index.js +9 -6
  11. package/dist/data/encodings/index.js.map +1 -1
  12. package/dist/data/payloads/index.d.ts +1 -1
  13. package/dist/data/payloads/index.d.ts.map +1 -1
  14. package/dist/data/payloads/index.js +5 -3
  15. package/dist/data/payloads/index.js.map +1 -1
  16. package/dist/data/payloads/ssti.d.ts.map +1 -1
  17. package/dist/data/payloads/ssti.js +3 -0
  18. package/dist/data/payloads/ssti.js.map +1 -1
  19. package/dist/data/payloads/xss.d.ts.map +1 -1
  20. package/dist/data/payloads/xss.js +5 -1
  21. package/dist/data/payloads/xss.js.map +1 -1
  22. package/dist/data/waf-bypass/index.d.ts.map +1 -1
  23. package/dist/data/waf-bypass/index.js +3 -1
  24. package/dist/data/waf-bypass/index.js.map +1 -1
  25. package/dist/mcp-server/tools/definitions/pentest-analyze-response.tool.d.ts +8 -8
  26. package/dist/mcp-server/tools/definitions/pentest-analyze-response.tool.d.ts.map +1 -1
  27. package/dist/mcp-server/tools/definitions/pentest-analyze-response.tool.js +12 -12
  28. package/dist/mcp-server/tools/definitions/pentest-analyze-response.tool.js.map +1 -1
  29. package/dist/mcp-server/tools/definitions/pentest-encode.tool.d.ts +5 -5
  30. package/dist/mcp-server/tools/definitions/pentest-encode.tool.d.ts.map +1 -1
  31. package/dist/mcp-server/tools/definitions/pentest-encode.tool.js +12 -7
  32. package/dist/mcp-server/tools/definitions/pentest-encode.tool.js.map +1 -1
  33. package/dist/mcp-server/tools/definitions/pentest-generate-payloads.tool.d.ts +30 -36
  34. package/dist/mcp-server/tools/definitions/pentest-generate-payloads.tool.d.ts.map +1 -1
  35. package/dist/mcp-server/tools/definitions/pentest-generate-payloads.tool.js +5 -18
  36. package/dist/mcp-server/tools/definitions/pentest-generate-payloads.tool.js.map +1 -1
  37. package/dist/mcp-server/tools/definitions/pentest-guide.tool.d.ts +10 -16
  38. package/dist/mcp-server/tools/definitions/pentest-guide.tool.d.ts.map +1 -1
  39. package/dist/mcp-server/tools/definitions/pentest-guide.tool.js +16 -22
  40. package/dist/mcp-server/tools/definitions/pentest-guide.tool.js.map +1 -1
  41. package/dist/mcp-server/tools/definitions/pentest-lookup-group.tool.js +4 -6
  42. package/dist/mcp-server/tools/definitions/pentest-lookup-group.tool.js.map +1 -1
  43. package/dist/mcp-server/tools/definitions/pentest-lookup-technique.tool.d.ts.map +1 -1
  44. package/dist/mcp-server/tools/definitions/pentest-lookup-technique.tool.js +6 -8
  45. package/dist/mcp-server/tools/definitions/pentest-lookup-technique.tool.js.map +1 -1
  46. package/dist/mcp-server/tools/definitions/pentest-map-techniques.tool.d.ts +13 -9
  47. package/dist/mcp-server/tools/definitions/pentest-map-techniques.tool.d.ts.map +1 -1
  48. package/dist/mcp-server/tools/definitions/pentest-map-techniques.tool.js +19 -7
  49. package/dist/mcp-server/tools/definitions/pentest-map-techniques.tool.js.map +1 -1
  50. package/dist/mcp-server/tools/index.d.ts +174 -180
  51. package/dist/mcp-server/tools/index.d.ts.map +1 -1
  52. package/dist/services/attack/attack-service.d.ts.map +1 -1
  53. package/dist/services/attack/attack-service.js +18 -21
  54. package/dist/services/attack/attack-service.js.map +1 -1
  55. package/dist/services/encoding/encoding-service.d.ts.map +1 -1
  56. package/dist/services/methodology/methodology-service.d.ts +249 -1
  57. package/dist/services/methodology/methodology-service.d.ts.map +1 -1
  58. package/dist/services/methodology/methodology-service.js +7 -17
  59. package/dist/services/methodology/methodology-service.js.map +1 -1
  60. package/dist/services/payload/payload-service.d.ts +2 -2
  61. package/dist/services/payload/payload-service.d.ts.map +1 -1
  62. package/dist/services/payload/payload-service.js +1 -3
  63. package/dist/services/payload/payload-service.js.map +1 -1
  64. package/dist/services/response-analysis/response-analysis-service.d.ts.map +1 -1
  65. package/dist/services/response-analysis/response-analysis-service.js +1 -1
  66. package/dist/services/response-analysis/response-analysis-service.js.map +1 -1
  67. package/package.json +18 -18
  68. package/server.json +3 -3
package/AGENTS.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** pentest-mcp-server
4
- **Version:** 0.1.5
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.10.6`
4
+ **Version:** 0.1.7
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.12.3`
6
6
  **Engines:** Bun ≥1.3.0, Node ≥24.0.0
7
- **MCP SDK:** `@modelcontextprotocol/sdk` ^1.29.0
7
+ **MCP SDK:** `@modelcontextprotocol/server` / `@modelcontextprotocol/client` ^2.0.0 (via framework)
8
8
  **Zod:** ^4.4.3
9
9
 
10
10
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
@@ -54,7 +54,13 @@ export const pentestLookupTechnique = tool('pentest_lookup_technique', {
54
54
  async handler(input, ctx) {
55
55
  const svc = getAttackService();
56
56
  const result = svc.lookup(input.query, input.include_subtechniques);
57
- if (!result) throw ctx.fail('no_match', `No technique matched: ${input.query}`);
57
+ if (!result) {
58
+ throw ctx.fail(
59
+ 'no_match',
60
+ `No technique matched: ${input.query}`,
61
+ ctx.recoveryFor('no_match'),
62
+ );
63
+ }
58
64
  ctx.log.info('Technique lookup', { query: input.query, id: result.technique_id });
59
65
  return result;
60
66
  },
@@ -93,12 +99,14 @@ Handlers receive a unified `ctx` object. Properties used by this server:
93
99
 
94
100
  | Property | Description |
95
101
  |:---------|:------------|
96
- | `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
102
+ | `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino and client-visible `notifications/message`. |
103
+ | `ctx.enrich` | Success-path context — this server uses `.truncated()` to disclose capped technique maps. Declared enrichment reaches both `structuredContent` and `content[]`. |
104
+ | `ctx.recoveryFor` | Resolves a declared error contract's recovery string into wire-ready `data.recovery.hint`. |
97
105
  | `ctx.signal` | `AbortSignal` for cancellation. |
98
106
  | `ctx.requestId` | Unique request ID. |
99
107
  | `ctx.tenantId` | Tenant ID from JWT or `'default'` for stdio. |
100
108
 
101
- This server is stateless — tools read from embedded data only. `ctx.state`, `ctx.elicit`, `ctx.sample`, and `ctx.progress` are not used.
109
+ This server is stateless — tools read from embedded data only. `ctx.state`, `ctx.requestInput`, `ctx.inputs`, and `ctx.content` are not used.
102
110
 
103
111
  ---
104
112
 
@@ -106,7 +114,7 @@ This server is stateless — tools read from embedded data only. `ctx.state`, `c
106
114
 
107
115
  Handlers throw — the framework catches, classifies, and formats.
108
116
 
109
- **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required descriptive metadata for the agent's next move ( 5 words, lint-validated); for the wire `data.recovery.hint` (mirrored into `content[]` text), pass explicitly at the throw site when dynamic context matters: `ctx.fail('reason', msg, { recovery: { hint: '...' } })`. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
117
+ **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) and is the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override with an explicit `{ recovery: { hint: '...' } }` when dynamic runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
110
118
 
111
119
  ```ts
112
120
  import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
@@ -118,7 +126,9 @@ errors: [
118
126
  ],
119
127
  async handler(input, ctx) {
120
128
  const item = await db.find(input.id);
121
- if (!item) throw ctx.fail('no_match', `No item ${input.id}`);
129
+ if (!item) {
130
+ throw ctx.fail('no_match', `No item ${input.id}`, ctx.recoveryFor('no_match'));
131
+ }
122
132
  return item;
123
133
  }
124
134
  ```
@@ -234,7 +244,7 @@ Available skills:
234
244
  | `api-auth` | Auth modes, scopes, JWT/OAuth |
235
245
  | `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
236
246
  | `api-config` | AppConfig, parseConfig, env vars |
237
- | `api-context` | Context interface, logger, state, progress |
247
+ | `api-context` | Context interface, logger, state, multi-round-trip input, enrichment, and content blocks |
238
248
  | `api-errors` | McpError, JsonRpcErrorCode, error patterns |
239
249
  | `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
240
250
  | `api-services` | LLM, Speech, Graph services |
@@ -251,31 +261,34 @@ When you complete a skill's checklist, check the boxes and add a completion time
251
261
 
252
262
  ## Commands
253
263
 
254
- **Runtime:** Scripts use `tsx`both `npm run <cmd>` and `bun run <cmd>` work. `bun` is slightly faster for script invocation but not required.
264
+ **Runtime:** Scripts use Bun's native TypeScript execution — `bun run <cmd>` is the standard invocation. `npm run <cmd>` also works (npm delegates to Bun).
255
265
 
256
266
  | Command | Purpose |
257
267
  |:--------|:--------|
258
- | `npm run build` | Compile TypeScript |
259
- | `npm run rebuild` | Clean + build |
260
- | `npm run clean` | Remove build artifacts |
261
- | `npm run devcheck` | Lint + format + typecheck + security + changelog sync |
268
+ | `bun run build` | Compile TypeScript |
269
+ | `bun run rebuild` | Clean + build |
270
+ | `bun run clean` | Remove build artifacts |
271
+ | `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
262
272
  | `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Use when `devcheck` flags a transitive advisory — Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
263
- | `npm run tree` | Generate directory structure doc |
264
- | `npm run format` | Auto-fix formatting (safe fixes only) |
265
- | `npm run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
266
- | `npm test` | Run tests |
267
- | `npm run start:stdio` | Production mode (stdio) |
268
- | `npm run start:http` | Production mode (HTTP) |
269
- | `npm run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
270
- | `npm run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
271
- | `npm run release:github` | Create GitHub Release from the current annotated tag |
272
- | `npm run bundle` | Build and pack as `.mcpb` for one-click Claude Desktop install |
273
+ | `bun run lint:mcp` | Run the MCP definition linter standalone |
274
+ | `bun run lint:packaging` | Run packaging-surface checks standalone |
275
+ | `bun run list-skills` | Print the skill registry |
276
+ | `bun run tree` | Generate directory structure doc |
277
+ | `bun run format` | Auto-fix formatting (safe fixes only) |
278
+ | `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
279
+ | `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
280
+ | `bun run start:stdio` | Production mode (stdio) |
281
+ | `bun run start:http` | Production mode (HTTP) |
282
+ | `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
283
+ | `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
284
+ | `bun run release:github` | Create GitHub Release from the current annotated tag |
285
+ | `bun run bundle` | Build and pack as `.mcpb` for one-click Claude Desktop install |
273
286
 
274
287
  ---
275
288
 
276
289
  ## Bundling
277
290
 
278
- `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
291
+ `bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
279
292
 
280
293
  **Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match.
281
294
 
@@ -285,7 +298,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
285
298
 
286
299
  ## Changelog
287
300
 
288
- Directory-based, grouped by minor series via the `.x` semver-wildcard convention. Source of truth: `changelog/<major.minor>.x/<version>.md` (e.g. `changelog/0.1.x/0.1.0.md`) — one file per release, shipped in the npm package. At release, author the per-version file with a concrete version and date, then run `npm run changelog:build` to regenerate the rollup. `changelog/template.md` is a **pristine format reference** — never edited or moved; read it for the frontmatter + section layout when scaffolding. `CHANGELOG.md` is a **navigation index** (header + link + summary per version), regenerated by `npm run changelog:build` — devcheck hard-fails on drift; never hand-edit it.
301
+ Directory-based, grouped by minor series via the `.x` semver-wildcard convention. Source of truth: `changelog/<major.minor>.x/<version>.md` (e.g. `changelog/0.1.x/0.1.0.md`) — one file per release, shipped in the npm package. At release, author the per-version file with a concrete version and date, then run `bun run changelog:build` to regenerate the rollup. `changelog/template.md` is a **pristine format reference** — never edited or moved; read it for the frontmatter + section layout when scaffolding. `CHANGELOG.md` is a **navigation index** (header + link + summary per version), regenerated by `bun run changelog:build` — devcheck hard-fails on drift; never hand-edit it.
289
302
 
290
303
  Each per-version file opens with YAML frontmatter:
291
304
 
@@ -339,4 +352,4 @@ import { getMyService } from '@/services/my-domain/my-service.js';
339
352
  - [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = package name; `interface.shortDescription` from `package.json` description
340
353
  - [ ] `.codex-plugin/mcp.json` updated — server name key matches `package.json` name; env vars added for any required API keys
341
354
  - [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; inline `mcpServers` entry with server name key, env vars for any required API keys
342
- - [ ] `npm run devcheck` passes
355
+ - [ ] `bun run devcheck` passes with zero warnings
package/CLAUDE.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** pentest-mcp-server
4
- **Version:** 0.1.5
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.10.6`
4
+ **Version:** 0.1.7
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.12.3`
6
6
  **Engines:** Bun ≥1.3.0, Node ≥24.0.0
7
- **MCP SDK:** `@modelcontextprotocol/sdk` ^1.29.0
7
+ **MCP SDK:** `@modelcontextprotocol/server` / `@modelcontextprotocol/client` ^2.0.0 (via framework)
8
8
  **Zod:** ^4.4.3
9
9
 
10
10
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
@@ -54,7 +54,13 @@ export const pentestLookupTechnique = tool('pentest_lookup_technique', {
54
54
  async handler(input, ctx) {
55
55
  const svc = getAttackService();
56
56
  const result = svc.lookup(input.query, input.include_subtechniques);
57
- if (!result) throw ctx.fail('no_match', `No technique matched: ${input.query}`);
57
+ if (!result) {
58
+ throw ctx.fail(
59
+ 'no_match',
60
+ `No technique matched: ${input.query}`,
61
+ ctx.recoveryFor('no_match'),
62
+ );
63
+ }
58
64
  ctx.log.info('Technique lookup', { query: input.query, id: result.technique_id });
59
65
  return result;
60
66
  },
@@ -93,12 +99,14 @@ Handlers receive a unified `ctx` object. Properties used by this server:
93
99
 
94
100
  | Property | Description |
95
101
  |:---------|:------------|
96
- | `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
102
+ | `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino and client-visible `notifications/message`. |
103
+ | `ctx.enrich` | Success-path context — this server uses `.truncated()` to disclose capped technique maps. Declared enrichment reaches both `structuredContent` and `content[]`. |
104
+ | `ctx.recoveryFor` | Resolves a declared error contract's recovery string into wire-ready `data.recovery.hint`. |
97
105
  | `ctx.signal` | `AbortSignal` for cancellation. |
98
106
  | `ctx.requestId` | Unique request ID. |
99
107
  | `ctx.tenantId` | Tenant ID from JWT or `'default'` for stdio. |
100
108
 
101
- This server is stateless — tools read from embedded data only. `ctx.state`, `ctx.elicit`, `ctx.sample`, and `ctx.progress` are not used.
109
+ This server is stateless — tools read from embedded data only. `ctx.state`, `ctx.requestInput`, `ctx.inputs`, and `ctx.content` are not used.
102
110
 
103
111
  ---
104
112
 
@@ -106,7 +114,7 @@ This server is stateless — tools read from embedded data only. `ctx.state`, `c
106
114
 
107
115
  Handlers throw — the framework catches, classifies, and formats.
108
116
 
109
- **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required descriptive metadata for the agent's next move ( 5 words, lint-validated); for the wire `data.recovery.hint` (mirrored into `content[]` text), pass explicitly at the throw site when dynamic context matters: `ctx.fail('reason', msg, { recovery: { hint: '...' } })`. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
117
+ **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) and is the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reason')` as the throw's data to put it on the wire (`data.recovery.hint`, mirrored into `content[]` text); override with an explicit `{ recovery: { hint: '...' } }` when dynamic runtime context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring.
110
118
 
111
119
  ```ts
112
120
  import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
@@ -118,7 +126,9 @@ errors: [
118
126
  ],
119
127
  async handler(input, ctx) {
120
128
  const item = await db.find(input.id);
121
- if (!item) throw ctx.fail('no_match', `No item ${input.id}`);
129
+ if (!item) {
130
+ throw ctx.fail('no_match', `No item ${input.id}`, ctx.recoveryFor('no_match'));
131
+ }
122
132
  return item;
123
133
  }
124
134
  ```
@@ -234,7 +244,7 @@ Available skills:
234
244
  | `api-auth` | Auth modes, scopes, JWT/OAuth |
235
245
  | `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
236
246
  | `api-config` | AppConfig, parseConfig, env vars |
237
- | `api-context` | Context interface, logger, state, progress |
247
+ | `api-context` | Context interface, logger, state, multi-round-trip input, enrichment, and content blocks |
238
248
  | `api-errors` | McpError, JsonRpcErrorCode, error patterns |
239
249
  | `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
240
250
  | `api-services` | LLM, Speech, Graph services |
@@ -251,31 +261,34 @@ When you complete a skill's checklist, check the boxes and add a completion time
251
261
 
252
262
  ## Commands
253
263
 
254
- **Runtime:** Scripts use `tsx`both `npm run <cmd>` and `bun run <cmd>` work. `bun` is slightly faster for script invocation but not required.
264
+ **Runtime:** Scripts use Bun's native TypeScript execution — `bun run <cmd>` is the standard invocation. `npm run <cmd>` also works (npm delegates to Bun).
255
265
 
256
266
  | Command | Purpose |
257
267
  |:--------|:--------|
258
- | `npm run build` | Compile TypeScript |
259
- | `npm run rebuild` | Clean + build |
260
- | `npm run clean` | Remove build artifacts |
261
- | `npm run devcheck` | Lint + format + typecheck + security + changelog sync |
268
+ | `bun run build` | Compile TypeScript |
269
+ | `bun run rebuild` | Clean + build |
270
+ | `bun run clean` | Remove build artifacts |
271
+ | `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
262
272
  | `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Use when `devcheck` flags a transitive advisory — Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
263
- | `npm run tree` | Generate directory structure doc |
264
- | `npm run format` | Auto-fix formatting (safe fixes only) |
265
- | `npm run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
266
- | `npm test` | Run tests |
267
- | `npm run start:stdio` | Production mode (stdio) |
268
- | `npm run start:http` | Production mode (HTTP) |
269
- | `npm run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
270
- | `npm run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
271
- | `npm run release:github` | Create GitHub Release from the current annotated tag |
272
- | `npm run bundle` | Build and pack as `.mcpb` for one-click Claude Desktop install |
273
+ | `bun run lint:mcp` | Run the MCP definition linter standalone |
274
+ | `bun run lint:packaging` | Run packaging-surface checks standalone |
275
+ | `bun run list-skills` | Print the skill registry |
276
+ | `bun run tree` | Generate directory structure doc |
277
+ | `bun run format` | Auto-fix formatting (safe fixes only) |
278
+ | `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
279
+ | `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
280
+ | `bun run start:stdio` | Production mode (stdio) |
281
+ | `bun run start:http` | Production mode (HTTP) |
282
+ | `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
283
+ | `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
284
+ | `bun run release:github` | Create GitHub Release from the current annotated tag |
285
+ | `bun run bundle` | Build and pack as `.mcpb` for one-click Claude Desktop install |
273
286
 
274
287
  ---
275
288
 
276
289
  ## Bundling
277
290
 
278
- `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
291
+ `bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
279
292
 
280
293
  **Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match.
281
294
 
@@ -285,7 +298,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
285
298
 
286
299
  ## Changelog
287
300
 
288
- Directory-based, grouped by minor series via the `.x` semver-wildcard convention. Source of truth: `changelog/<major.minor>.x/<version>.md` (e.g. `changelog/0.1.x/0.1.0.md`) — one file per release, shipped in the npm package. At release, author the per-version file with a concrete version and date, then run `npm run changelog:build` to regenerate the rollup. `changelog/template.md` is a **pristine format reference** — never edited or moved; read it for the frontmatter + section layout when scaffolding. `CHANGELOG.md` is a **navigation index** (header + link + summary per version), regenerated by `npm run changelog:build` — devcheck hard-fails on drift; never hand-edit it.
301
+ Directory-based, grouped by minor series via the `.x` semver-wildcard convention. Source of truth: `changelog/<major.minor>.x/<version>.md` (e.g. `changelog/0.1.x/0.1.0.md`) — one file per release, shipped in the npm package. At release, author the per-version file with a concrete version and date, then run `bun run changelog:build` to regenerate the rollup. `changelog/template.md` is a **pristine format reference** — never edited or moved; read it for the frontmatter + section layout when scaffolding. `CHANGELOG.md` is a **navigation index** (header + link + summary per version), regenerated by `bun run changelog:build` — devcheck hard-fails on drift; never hand-edit it.
289
302
 
290
303
  Each per-version file opens with YAML frontmatter:
291
304
 
@@ -339,4 +352,4 @@ import { getMyService } from '@/services/my-domain/my-service.js';
339
352
  - [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = package name; `interface.shortDescription` from `package.json` description
340
353
  - [ ] `.codex-plugin/mcp.json` updated — server name key matches `package.json` name; env vars added for any required API keys
341
354
  - [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; inline `mcpServers` entry with server name key, env vars for any required API keys
342
- - [ ] `npm run devcheck` passes
355
+ - [ ] `bun run devcheck` passes with zero warnings
package/Dockerfile CHANGED
@@ -4,7 +4,7 @@
4
4
  # This stage installs all dependencies (including dev), builds the TypeScript
5
5
  # source code into JavaScript, and prepares the production assets.
6
6
  # ==============================================================================
7
- FROM oven/bun:1.3 AS build
7
+ FROM oven/bun:1.4.0 AS build
8
8
 
9
9
  WORKDIR /usr/src/app
10
10
 
@@ -12,7 +12,8 @@ WORKDIR /usr/src/app
12
12
  COPY package.json bun.lock ./
13
13
 
14
14
  # Install all dependencies (including dev dependencies for building)
15
- RUN bun install --frozen-lockfile
15
+ RUN --mount=type=cache,target=/root/.bun/install/cache \
16
+ bun install --frozen-lockfile --ignore-scripts
16
17
 
17
18
  # Copy the rest of the source code
18
19
  COPY . .
@@ -31,7 +32,7 @@ RUN bun run build
31
32
  # application. It uses a slim base image and only includes production
32
33
  # dependencies and build artifacts.
33
34
  # ==============================================================================
34
- FROM oven/bun:1.3-slim AS production
35
+ FROM oven/bun:1.4.0-slim AS production
35
36
 
36
37
  WORKDIR /usr/src/app
37
38
 
@@ -52,14 +53,17 @@ COPY package.json bun.lock ./
52
53
 
53
54
  # Install only production dependencies, ignoring any lifecycle scripts (like 'prepare')
54
55
  # that are not needed in the final production image.
55
- RUN bun install --production --frozen-lockfile --ignore-scripts
56
+ RUN --mount=type=cache,target=/root/.bun/install/cache \
57
+ bun install --production --omit=peer --frozen-lockfile --ignore-scripts
56
58
 
57
59
  # Conditionally install OpenTelemetry optional peer dependencies (Tier 3).
58
60
  # These are not bundled by default to keep the base image lean. Enable at build time
59
61
  # with: docker build --build-arg OTEL_ENABLED=true
60
62
  ARG OTEL_ENABLED=true
61
- RUN if [ "$OTEL_ENABLED" = "true" ]; then \
62
- bun add @hono/otel \
63
+ RUN --mount=type=cache,target=/root/.bun/install/cache \
64
+ if [ "$OTEL_ENABLED" = "true" ]; then \
65
+ bun add --omit=dev --omit=peer --ignore-scripts \
66
+ @hono/otel \
63
67
  @opentelemetry/instrumentation-http \
64
68
  @opentelemetry/exporter-metrics-otlp-http \
65
69
  @opentelemetry/exporter-trace-otlp-http \
package/LICENSE CHANGED
@@ -186,7 +186,7 @@ Apache License
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright 2025 Casey Hand @cyanheads
189
+ Copyright 2026 Casey Hand @cyanheads
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-0.1.5-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/pentest-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/pentest-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/pentest-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^6.0.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-0.1.7-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/pentest-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/pentest-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/pentest-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.0%2B-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
11
 
12
12
  </div>
13
13
 
@@ -385,9 +385,10 @@ The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `
385
385
 
386
386
  See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
387
387
 
388
- - Handlers throw, framework catches no `try/catch` in tool logic
389
- - Use `ctx.log` for request-scoped logging, `ctx.state` for tenant-scoped storage
390
- - Register new tools via the barrel in `src/mcp-server/tools/definitions/index.ts`
388
+ - Handlers throw and the framework catches; catch only to translate an owned operational failure into a declared `ctx.fail` contract
389
+ - Use `ctx.log` for request-scoped logging and keep request handling stateless and deterministic
390
+ - Register new tools via the barrel in `src/mcp-server/tools/index.ts`
391
+ - External source boundary: validate raw data, normalize to a domain type, then return the output schema; never fabricate missing fields
391
392
  - `authorized_use_reminder` is a required output field on every tool that produces methodology or payload content — render it as the first line of every `content[]` response in `format()`
392
393
  - Every technique, finding, and payload object has required (non-optional) `detection` and `mitigation` fields — this is a schema contract, not documentation guidance
393
394
 
@@ -0,0 +1,26 @@
1
+ ---
2
+ summary: "Adopt @cyanheads/mcp-ts-core 0.10.9 — check-dependency-specifiers devcheck guard, plugin-manifest packaging checks, fresh-scaffold and worktree-deletion guards; re-sync six vendored scripts and fourteen skills; dependency refresh"
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.1.6 — 2026-06-20
8
+
9
+ ## Added
10
+
11
+ - **`scripts/check-dependency-specifiers.ts`** (vendored) — new `Dependency Specifiers` devcheck step (`--no-dep-specifiers`) that hard-fails on floating specifiers (`latest`, `*`, pre-release dist-tags `next`/`beta`/`canary`/`rc`) in `package.json`'s dependency sections and `bun.lock`'s `workspaces` map. Catches `bun update --latest` writing a literal `latest` dist-tag into the lock's workspace map, which clears every other gate then lets the next `bun install` re-resolve past the `package.json` range. Static and local — runs in the default and `--fast` passes. ([cyanheads/mcp-ts-core#246](https://github.com/cyanheads/mcp-ts-core/issues/246))
12
+ - **Plugin marketplace manifest checks in `scripts/lint-packaging.ts`** (vendored) — check 10 validates `.claude-plugin/plugin.json` and `.codex-plugin/plugin.json`: non-empty descriptions, display fields (`name`, server key, `interface.displayName`) carrying the unscoped `pentest-mcp-server`, and the `npx -y` install arg carrying the full `@cyanheads/pentest-mcp-server`. Gated by the new `devcheck.config.json` `packaging.pluginManifests` flag (set to `true`); each manifest is skipped cleanly when absent. ([cyanheads/mcp-ts-core#240](https://github.com/cyanheads/mcp-ts-core/issues/240))
13
+
14
+ ## Changed
15
+
16
+ - **`@cyanheads/mcp-ts-core`** `^0.10.6 → ^0.10.9`.
17
+ - **`devcheck.config.json`** — added `packaging.pluginManifests: true`, enabling check 10 above.
18
+ - **Vendored devcheck scripts re-synced to mcp-ts-core 0.10.9** — `scripts/devcheck.ts` registers the `Dependency Specifiers` step and skips git-dependent checks (TODOs/FIXMEs, Tracked Secrets, Framework Antipatterns) via a shared `isGitRepo()` guard on a fresh scaffold; `scripts/build-changelog.ts` exits cleanly under `--check` when no `<major.minor>.x/` version files exist; `scripts/check-framework-antipatterns.ts` self-guards outside a git repo; `scripts/check-skill-versions.ts` skips a `SKILL.md` deleted from the worktree instead of crashing with ENOENT. ([cyanheads/mcp-ts-core#237](https://github.com/cyanheads/mcp-ts-core/issues/237), [#242](https://github.com/cyanheads/mcp-ts-core/issues/242), [#243](https://github.com/cyanheads/mcp-ts-core/issues/243), [#246](https://github.com/cyanheads/mcp-ts-core/issues/246))
19
+ - **Vendored skills re-synced to mcp-ts-core 0.10.9** — fourteen `skills/<name>/SKILL.md` files updated with body refreshes and `metadata.version` bumps: `add-tool`, `api-auth`, `api-canvas`, `api-config`, `api-context`, `api-errors`, `api-services`, `api-telemetry`, `field-test`, `git-wrapup`, `orchestrations`, `polish-docs-meta`, `report-issue-local`, `tool-defs-analysis`. Mirrored into `.claude/skills/`. ([cyanheads/mcp-ts-core#238](https://github.com/cyanheads/mcp-ts-core/issues/238))
20
+
21
+ ### Dependency bumps
22
+
23
+ - `@types/node` `^25.9.3 → ^26.0.0`
24
+ - `vitest` `^4.1.8 → ^4.1.9`
25
+ </content>
26
+ </invoke>
@@ -0,0 +1,37 @@
1
+ ---
2
+ summary: "Adopt mcp-ts-core 0.12.3 and MCP SDK v2 with strict tool contracts and wire-ready error and enrichment metadata"
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.1.7 — 2026-08-21
8
+
9
+ ## Added
10
+
11
+ - **Supply-chain controls** — installs apply a three-day release-age hold to third-party packages, exempt first-party `@cyanheads/mcp-ts-core`, and scan packages with `@socketsecurity/bun-security-scanner`.
12
+ - **Community health files** — added contribution, conduct, funding, and private vulnerability-reporting guidance.
13
+
14
+ ## Changed
15
+
16
+ - **`@cyanheads/mcp-ts-core` 0.12.3 / MCP SDK v2** — the server serves modern and legacy MCP revisions with strict root tool inputs, JSON Schema 2020-12, and an advertised error envelope.
17
+ - **TypeScript 7 test typechecking** — `tsconfig.json` now covers `src/` and `tests/`, while `tsconfig.build.json` keeps production emit scoped to `src/`; tool tests use contract-aware mock contexts.
18
+ - **Bun 1.4 packaging** — Docker stages pin Bun 1.4.0, cache installs, omit optional peers from production, and strip platform-specific native bindings from MCPB bundles.
19
+ - **Tool definitions** — descriptions and output-field guidance were tightened across all seven tools; vendored scripts, skills, agent protocol, and packaging metadata were synced to the 0.12.3 framework line.
20
+
21
+ ## Fixed
22
+
23
+ - **Declared errors** — `pentest_analyze_response`, `pentest_encode`, both ATT&CK lookups, and `pentest_map_techniques` now place their declared recovery hints on the wire; unreachable guide and payload error contracts were removed.
24
+ - **`pentest_map_techniques`** — empty profile arrays now raise `no_profile`, and every success reports `{ truncated, shown, cap }` enrichment rather than omitting the complete-result case.
25
+ - **`pentest_encode` text output** — truncated previews say the full value remains available in `structuredContent`.
26
+ - **Response analysis summary** — clean-result text preserves spacing before its follow-up sentence.
27
+
28
+ ## Dependencies
29
+
30
+ - `@cyanheads/mcp-ts-core` `^0.10.9` → `^0.12.3`
31
+ - `@biomejs/biome` `^2.5.0` → `^2.5.9`
32
+ - `@socketsecurity/bun-security-scanner` added at `^1.1.2`
33
+ - `@types/node` `^26.0.0` → `^26.2.0`
34
+ - `ignore` `^7.0.5` → `^7.0.6`
35
+ - `tsc-alias` `^1.8.17` → `^1.9.2`
36
+ - `typescript` `^6.0.3` → `^7.0.2`
37
+ - `vitest` `^4.1.9` → `^4.1.11`
@@ -4,10 +4,11 @@
4
4
  # to author a new release. Set that file's H1 to `# <version> — YYYY-MM-DD`
5
5
  # with a concrete date.
6
6
 
7
- # Required. One-line GitHub Release-style headline. 350 character cap.
8
- # Default short and scannable. Don't pad, don't stitch unrelated changes with
9
- # semicolons pick the headline. Quotes required: unquoted YAML treats `: `
10
- # inside the value as a key separator and fails GitHub's strict parser.
7
+ # Required. One-line GitHub Release-style headline. 350 character cap — a
8
+ # ceiling, not a target. Default short and scannable. Don't pad, don't stitch
9
+ # unrelated changes with commas/semicolons into an inventory pick the
10
+ # headline, like a tag's theme line. Quotes required: unquoted YAML treats
11
+ # `: ` inside the value as a key separator and fails GitHub's strict parser.
11
12
  summary: ""
12
13
 
13
14
  # Set `true` when consumers must change code to upgrade: API removals,
@@ -15,16 +16,19 @@ summary: ""
15
16
  # usage. Flagged as `Breaking` in the rollup.
16
17
  breaking: false
17
18
 
18
- # Set `true` if this release contains any security fix. Pairs with the
19
- # `## Security` section below. Flagged as `Security` in the rollup so
20
- # users can triage upgrade urgency at a glance.
19
+ # Set `true` ONLY for a security fix in THIS project's own source code — a
20
+ # vulnerability or hardening in code you ship. A dependency or transitive CVE
21
+ # bump is routine maintenance, NOT a security release: record it under
22
+ # `## Dependencies` (with the advisory ID) and leave this `false`. When true,
23
+ # pairs with the `## Security` section below and flags `Security` in the rollup.
21
24
  security: false
22
25
 
23
26
  # Optional free-form notes for maintenance agents processing this release.
24
27
  # Not rendered in CHANGELOG — consumed by agents running `maintenance` on
25
- # downstream servers. Use for adoption instructions that don't fit the
26
- # human-facing sections: new files to create, fields to populate, one-time
27
- # migration steps. Omit the field entirely when there's nothing to say.
28
+ # downstream servers. ADOPTION STEPS ONLY new files to create, fields to
29
+ # populate, one-time migration steps. Never a second rendering of the body:
30
+ # if a body bullet already says it, name the bullet's symbol instead of
31
+ # re-explaining. Omit the field entirely when there's nothing to say.
28
32
  # agent-notes: |
29
33
  # <instructions for downstream maintenance agents>
30
34
  ---
@@ -39,17 +43,54 @@ security: false
39
43
  each bullet with the symbol or concept name in **bold** so they can skip
40
44
  what's irrelevant and zoom in on what's not.
41
45
 
42
- Tone: terse, fact-dense, not verbose. Default to one sentence per bullet —
43
- name the symbol, state what changed, stop. Use a second sentence only when
44
- it carries weight. If a bullet feels long, it is.
45
-
46
- Cut: mechanism walkthroughs (those belong in JSDoc, CLAUDE.md/AGENTS.md, or the
47
- relevant skill), ceremonial framings ("This release introduces…",
48
- backwards-compat paragraphs), file-by-file test enumerations, internal
49
- implementation notes. Prefer code/symbol names over English re-explanations.
46
+ Tone: terse, fact-dense, not verbose. Bullet shape: **symbol** + what
47
+ changed + at most one consumer-facing caveat. One sentence by default, two
48
+ when the second carries weight a bullet past ~40 words or three sentences
49
+ is wrong. The depth lives one hop away: the linked issue carries the why,
50
+ the commit diff carries the how. The changelog names what changed and what
51
+ a consumer does about it; a reader who wants mechanism opens the link.
52
+
53
+ Model length on THIS guide, never on the previous entry — entries modeled
54
+ on entries compound.
55
+
56
+ Cut (each has shipped as a wall of text; these are the cruft):
57
+ - History/justification narration — how the bug worked, why the old
58
+ behavior was wrong. One short clause at most; the issue carries the story.
59
+ - Design-rationale defense — "chosen over Y because…", "guarding the
60
+ getter is not enough…". That is the author arguing with a reviewer;
61
+ reviewers read the PR, not the changelog.
62
+ - Defensive unchanged-clauses — "X is unchanged", "byte-identical to
63
+ <prev>". Keep one only where its absence would cause a real misread,
64
+ as a short parenthetical.
65
+ - Edge-case inventories — marker lists, not-flagged lists, escape tables.
66
+ Tests and the issue carry those.
67
+ - Mechanism walkthroughs (JSDoc, CLAUDE.md/AGENTS.md, or the relevant
68
+ skill own those), ceremonial framings ("This release introduces…"),
69
+ backwards-compat paragraphs, file-by-file test enumerations. Prefer
70
+ code/symbol names over English re-explanations.
71
+
72
+ Verified ≠ included: the every-claim-verified-from-the-diff rule bounds
73
+ the TRUTH of what you write, never the AMOUNT.
74
+
75
+ Example — same fact, right size:
76
+
77
+ TOO LONG: **`fetchWithTimeout`'s `timeoutMs` bounds the whole exchange**
78
+ (#341). `fetch` resolves once headers arrive and the deadline was
79
+ cleared as the helper returned, so a peer that answered promptly and
80
+ then stalled the stream held the request open indefinitely. A 2xx
81
+ carrying a body now comes back as a passthrough wrapper that disarms
82
+ the deadline when the body closes, errors, or is cancelled; …
83
+ [+90 more words of mechanism and edge cases]
84
+
85
+ RIGHT: **`fetchWithTimeout`'s `timeoutMs` now bounds the whole
86
+ exchange, not just the headers** (#341). A stalled body aborts with
87
+ the same `Timeout` error; the returned `Response` is a wrapper, so
88
+ identity assertions (`toBe(response)`) no longer hold.
50
89
 
51
90
  Narrative intro: skip by default. Add one short sentence only when the
52
- release theme genuinely needs framing the bullets can't carry.
91
+ release theme genuinely needs framing the bullets can't carry. When many
92
+ bullets share one upgrade consequence, state it ONCE — intro line or
93
+ agent-notes — never per bullet.
53
94
 
54
95
  Sections: Keep a Changelog order — Added, Changed, Deprecated, Removed,
55
96
  Fixed, Security. Include only sections with entries; delete the rest
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/data/encodings/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,gCAAgC;AAChC,MAAM,MAAM,YAAY,GACpB,KAAK,GACL,YAAY,GACZ,aAAa,GACb,SAAS,GACT,KAAK,GACL,QAAQ,GACR,WAAW,GACX,WAAW,GACX,YAAY,GACZ,eAAe,CAAC;AAEpB,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,wBAAwB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAItD;AAED,8CAA8C;AAC9C,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAanD;AAED,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAW/C;AAED,uBAAuB;AACvB,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWpD;AAED,oDAAoD;AACpD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIrD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUxD;AAED,gEAAgE;AAChE,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAmB3D,CAAC;AAEF,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,MAAM,CAuB3E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,YAAY,EAAE,GACpB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAAE,CAQ5C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CAUpE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CAkClE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/data/encodings/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,gCAAgC;AAChC,MAAM,MAAM,YAAY,GACpB,KAAK,GACL,YAAY,GACZ,aAAa,GACb,SAAS,GACT,KAAK,GACL,QAAQ,GACR,WAAW,GACX,WAAW,GACX,YAAY,GACZ,eAAe,CAAC;AAEpB,wCAAwC;AACxC,MAAM,WAAW,YAAY;IAC3B,wBAAwB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;CAChB;AAMD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAItD;AAED,8CAA8C;AAC9C,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAanD;AAED,gEAAgE;AAChE,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAW/C;AAED,uBAAuB;AACvB,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWpD;AAED,oDAAoD;AACpD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIrD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUxD;AAED,gEAAgE;AAChE,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAmB3D,CAAC;AAEF,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,GAAG,MAAM,CAuB3E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,YAAY,EAAE,GACpB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,YAAY,EAAE,CAAA;CAAE,CAQ5C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CAUpE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CAkClE"}
@@ -2,6 +2,9 @@
2
2
  * @fileoverview Pure TypeScript encoding/transformation functions for payload bypass research.
3
3
  * @module data/encodings/index
4
4
  */
5
+ function codePointOf(character) {
6
+ return character.codePointAt(0);
7
+ }
5
8
  /**
6
9
  * URL percent-encoding of special characters.
7
10
  * Encodes all characters that are not unreserved URI characters.
@@ -18,14 +21,14 @@ export function encodeDoubleUrl(input) {
18
21
  /** HTML entity encoding — numeric decimal form for every character. */
19
22
  export function encodeHtmlEntity(input) {
20
23
  return Array.from(input)
21
- .map((ch) => `&#${ch.codePointAt(0)};`)
24
+ .map((ch) => `&#${codePointOf(ch)};`)
22
25
  .join('');
23
26
  }
24
27
  /** Unicode escape sequences (\uXXXX form). */
25
28
  export function encodeUnicode(input) {
26
29
  return Array.from(input)
27
30
  .map((ch) => {
28
- const cp = ch.codePointAt(0);
31
+ const cp = codePointOf(ch);
29
32
  if (cp > 0xffff) {
30
33
  // Surrogate pair for code points above BMP
31
34
  const hi = Math.floor((cp - 0x10000) / 0x400) + 0xd800;
@@ -40,7 +43,7 @@ export function encodeUnicode(input) {
40
43
  export function encodeHex(input) {
41
44
  return Array.from(input)
42
45
  .map((ch) => {
43
- const cp = ch.codePointAt(0);
46
+ const cp = codePointOf(ch);
44
47
  if (cp > 0xff) {
45
48
  // Fall back to Unicode escape for multi-byte characters
46
49
  return `\\u${cp.toString(16).padStart(4, '0')}`;
@@ -57,7 +60,7 @@ export function encodeBase64(input) {
57
60
  export function encodeJsEscape(input) {
58
61
  return Array.from(input)
59
62
  .map((ch) => {
60
- const cp = ch.codePointAt(0);
63
+ const cp = codePointOf(ch);
61
64
  // Use octal for printable ASCII
62
65
  if (cp >= 0x20 && cp <= 0x7e) {
63
66
  return `\\${cp.toString(8)}`;
@@ -83,8 +86,8 @@ export function encodeMixedCase(input) {
83
86
  export function encodeCommentBreak(input) {
84
87
  const chars = Array.from(input);
85
88
  const result = [];
86
- for (let i = 0; i < chars.length; i++) {
87
- result.push(chars[i]);
89
+ for (const [i, char] of chars.entries()) {
90
+ result.push(char);
88
91
  if ((i + 1) % 3 === 0 && i < chars.length - 1) {
89
92
  result.push('/**/');
90
93
  }