@kairosinternational/watchman-nextjs 0.1.0

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.
@@ -0,0 +1,4 @@
1
+ import type { WatchmanMiddlewareConfig } from './types.js';
2
+ export declare const DEFAULT_MIDDLEWARE_CONFIG: Required<Omit<WatchmanMiddlewareConfig, 'projectRoot' | 'onFinding' | 'onColdStartComplete'>>;
3
+ export declare function resolveConfig(user?: WatchmanMiddlewareConfig): WatchmanMiddlewareConfig & typeof DEFAULT_MIDDLEWARE_CONFIG;
4
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAC9C,IAAI,CAAC,wBAAwB,EAAE,aAAa,GAAG,WAAW,GAAG,qBAAqB,CAAC,CAUpF,CAAC;AAEF,wBAAgB,aAAa,CAC3B,IAAI,GAAE,wBAA6B,GAClC,wBAAwB,GAAG,OAAO,yBAAyB,CAM7D"}
package/dist/config.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_MIDDLEWARE_CONFIG = void 0;
4
+ exports.resolveConfig = resolveConfig;
5
+ exports.DEFAULT_MIDDLEWARE_CONFIG = {
6
+ blockAt: 'critical',
7
+ warnAt: 'high',
8
+ scanBody: true,
9
+ scanHeaders: true,
10
+ scanUrl: true,
11
+ coldStartScan: true,
12
+ skipPaths: ['/_next', '/favicon.ico', '/static'],
13
+ quiet: false,
14
+ };
15
+ function resolveConfig(user = {}) {
16
+ return {
17
+ ...exports.DEFAULT_MIDDLEWARE_CONFIG,
18
+ projectRoot: user.projectRoot ?? process.cwd(),
19
+ ...user,
20
+ };
21
+ }
22
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;AAeA,sCAQC;AArBY,QAAA,yBAAyB,GAElC;IACF,OAAO,EAAE,UAAU;IACnB,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,IAAI;IACd,WAAW,EAAE,IAAI;IACjB,OAAO,EAAE,IAAI;IACb,aAAa,EAAE,IAAI;IACnB,SAAS,EAAE,CAAC,QAAQ,EAAE,cAAc,EAAE,SAAS,CAAC;IAChD,KAAK,EAAE,KAAK;CACb,CAAC;AAEF,SAAgB,aAAa,CAC3B,OAAiC,EAAE;IAEnC,OAAO;QACL,GAAG,iCAAyB;QAC5B,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE;QAC9C,GAAG,IAAI;KACR,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { withWatchman, scanRequest, scanString } from './middleware.js';
2
+ export { withWatchmanRoute } from './route-handler.js';
3
+ export { DEFAULT_MIDDLEWARE_CONFIG, resolveConfig } from './config.js';
4
+ export type { WatchmanMiddlewareConfig, RequestFinding, ScanOutcome, ScanTarget, Severity, Finding, WatchmanReport, } from './types.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACvE,YAAY,EACV,wBAAwB,EACxB,cAAc,EACd,WAAW,EACX,UAAU,EACV,QAAQ,EACR,OAAO,EACP,cAAc,GACf,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveConfig = exports.DEFAULT_MIDDLEWARE_CONFIG = exports.withWatchmanRoute = exports.scanString = exports.scanRequest = exports.withWatchman = void 0;
4
+ var middleware_js_1 = require("./middleware.js");
5
+ Object.defineProperty(exports, "withWatchman", { enumerable: true, get: function () { return middleware_js_1.withWatchman; } });
6
+ Object.defineProperty(exports, "scanRequest", { enumerable: true, get: function () { return middleware_js_1.scanRequest; } });
7
+ Object.defineProperty(exports, "scanString", { enumerable: true, get: function () { return middleware_js_1.scanString; } });
8
+ var route_handler_js_1 = require("./route-handler.js");
9
+ Object.defineProperty(exports, "withWatchmanRoute", { enumerable: true, get: function () { return route_handler_js_1.withWatchmanRoute; } });
10
+ var config_js_1 = require("./config.js");
11
+ Object.defineProperty(exports, "DEFAULT_MIDDLEWARE_CONFIG", { enumerable: true, get: function () { return config_js_1.DEFAULT_MIDDLEWARE_CONFIG; } });
12
+ Object.defineProperty(exports, "resolveConfig", { enumerable: true, get: function () { return config_js_1.resolveConfig; } });
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iDAAwE;AAA/D,6GAAA,YAAY,OAAA;AAAE,4GAAA,WAAW,OAAA;AAAE,2GAAA,UAAU,OAAA;AAC9C,uDAAuD;AAA9C,qHAAA,iBAAiB,OAAA;AAC1B,yCAAuE;AAA9D,sHAAA,yBAAyB,OAAA;AAAE,0GAAA,aAAa,OAAA"}
@@ -0,0 +1,25 @@
1
+ import { type NextRequest, type NextMiddleware } from 'next/server';
2
+ import { DEFAULT_MIDDLEWARE_CONFIG } from './config.js';
3
+ import type { WatchmanMiddlewareConfig, RequestFinding, ScanOutcome, ScanTarget } from './types.js';
4
+ /**
5
+ * Scans a single string against injection + secret patterns.
6
+ * Returns an array of findings. Caller annotates target.
7
+ */
8
+ export declare function scanString(input: string, target: ScanTarget): RequestFinding[];
9
+ /**
10
+ * Scans a NextRequest (URL, headers, body) against AI-tool-integrity
11
+ * and secrets-exposure patterns.
12
+ */
13
+ export declare function scanRequest(request: NextRequest, config: WatchmanMiddlewareConfig & typeof DEFAULT_MIDDLEWARE_CONFIG): Promise<ScanOutcome>;
14
+ /**
15
+ * Creates a Next.js middleware function that scans every request
16
+ * for prompt injection and secret exposure, and runs a cold-start
17
+ * file scan for dependency and runtime issues.
18
+ *
19
+ * Usage in middleware.ts:
20
+ * import { withWatchman } from '@kairosinternational/watchman-nextjs'
21
+ * export default withWatchman()
22
+ * export const config = { matcher: ['/((?!_next|static).*)'] }
23
+ */
24
+ export declare function withWatchman(userConfig?: WatchmanMiddlewareConfig): NextMiddleware;
25
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAUlF,OAAO,EAAiB,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,KAAK,EACV,wBAAwB,EACxB,cAAc,EACd,WAAW,EACX,UAAU,EACX,MAAM,YAAY,CAAC;AAsBpB;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,cAAc,EAAE,CAiC9E;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,wBAAwB,GAAG,OAAO,yBAAyB,GAClE,OAAO,CAAC,WAAW,CAAC,CAsCtB;AAuDD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,UAAU,GAAE,wBAA6B,GACxC,cAAc,CAgEhB"}
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scanString = scanString;
4
+ exports.scanRequest = scanRequest;
5
+ exports.withWatchman = withWatchman;
6
+ const node_crypto_1 = require("node:crypto");
7
+ const server_1 = require("next/server");
8
+ const watchman_1 = require("@kairosinternational/watchman");
9
+ const config_js_1 = require("./config.js");
10
+ const SEVERITY_ORDER = {
11
+ critical: 4,
12
+ high: 3,
13
+ medium: 2,
14
+ low: 1,
15
+ info: 0,
16
+ };
17
+ function severityAtLeast(a, threshold) {
18
+ return SEVERITY_ORDER[a] >= SEVERITY_ORDER[threshold];
19
+ }
20
+ function maxSeverity(findings) {
21
+ if (findings.length === 0)
22
+ return null;
23
+ return findings.reduce((max, f) => (SEVERITY_ORDER[f.severity] > SEVERITY_ORDER[max] ? f.severity : max), 'info');
24
+ }
25
+ /**
26
+ * Scans a single string against injection + secret patterns.
27
+ * Returns an array of findings. Caller annotates target.
28
+ */
29
+ function scanString(input, target) {
30
+ const findings = [];
31
+ if (!input)
32
+ return findings;
33
+ for (const sig of watchman_1.INJECTION_SIGNATURES) {
34
+ if (sig.pattern.test(input)) {
35
+ findings.push({
36
+ id: (0, node_crypto_1.randomUUID)(),
37
+ target,
38
+ rule: 'prompt-injection',
39
+ severity: sig.severity,
40
+ label: sig.label,
41
+ message: `Prompt injection signature in ${target}: ${sig.label}`,
42
+ evidence: input.slice(0, 200),
43
+ });
44
+ }
45
+ }
46
+ for (const sig of watchman_1.SECRET_SIGNATURES) {
47
+ if (sig.pattern.test(input)) {
48
+ findings.push({
49
+ id: (0, node_crypto_1.randomUUID)(),
50
+ target,
51
+ rule: 'known-patterns',
52
+ severity: 'critical',
53
+ label: sig.label,
54
+ message: `${sig.description} detected in ${target}`,
55
+ evidence: '[REDACTED]',
56
+ });
57
+ }
58
+ }
59
+ return findings;
60
+ }
61
+ /**
62
+ * Scans a NextRequest (URL, headers, body) against AI-tool-integrity
63
+ * and secrets-exposure patterns.
64
+ */
65
+ async function scanRequest(request, config) {
66
+ const findings = [];
67
+ if (config.scanUrl) {
68
+ let decoded = request.nextUrl.toString();
69
+ try {
70
+ decoded = decodeURIComponent(decoded);
71
+ }
72
+ catch {
73
+ // malformed -- scan as-is
74
+ }
75
+ findings.push(...scanString(decoded, 'url'));
76
+ }
77
+ if (config.scanHeaders) {
78
+ for (const [key, value] of request.headers.entries()) {
79
+ if (key.toLowerCase() === 'cookie')
80
+ continue;
81
+ findings.push(...scanString(`${key}: ${value}`, 'headers'));
82
+ }
83
+ }
84
+ if (config.scanBody && ['POST', 'PUT', 'PATCH'].includes(request.method)) {
85
+ try {
86
+ const cloned = request.clone();
87
+ const body = await cloned.text();
88
+ if (body) {
89
+ findings.push(...scanString(body, 'body'));
90
+ }
91
+ }
92
+ catch {
93
+ // Body unreadable -- skip
94
+ }
95
+ }
96
+ const maxSev = maxSeverity(findings);
97
+ const shouldBlock = maxSev !== null && severityAtLeast(maxSev, config.blockAt);
98
+ const shouldWarn = maxSev !== null && !shouldBlock && severityAtLeast(maxSev, config.warnAt);
99
+ return { findings, maxSeverity: maxSev, shouldBlock, shouldWarn };
100
+ }
101
+ let coldStartPromise = null;
102
+ function runColdStartScan(config) {
103
+ if (coldStartPromise)
104
+ return coldStartPromise;
105
+ coldStartPromise = (async () => {
106
+ if (!config.coldStartScan)
107
+ return null;
108
+ try {
109
+ const engine = new watchman_1.WatchmanEngine({
110
+ projectRoot: config.projectRoot ?? process.cwd(),
111
+ scanners: {
112
+ 'dependency-integrity': { enabled: true },
113
+ 'runtime-monitor': { enabled: true },
114
+ },
115
+ exclude: ['node_modules', '.git', 'dist', 'build', '.next'],
116
+ failOn: 'high',
117
+ });
118
+ engine.register(watchman_1.dependencyIntegrityScanner);
119
+ engine.register(watchman_1.runtimeMonitorScanner);
120
+ const report = await engine.scan();
121
+ if (config.onColdStartComplete) {
122
+ await config.onColdStartComplete(report);
123
+ }
124
+ if (!config.quiet) {
125
+ const critical = report.results
126
+ .flatMap((r) => r.findings)
127
+ .filter((f) => f.severity === 'critical').length;
128
+ if (critical > 0) {
129
+ console.warn(`[watchman] Cold-start scan: ${critical} critical findings in ${report.totalFindings} total`);
130
+ }
131
+ }
132
+ return report;
133
+ }
134
+ catch (err) {
135
+ if (!config.quiet) {
136
+ console.error('[watchman] Cold-start scan failed:', err);
137
+ }
138
+ return null;
139
+ }
140
+ })();
141
+ return coldStartPromise;
142
+ }
143
+ /**
144
+ * Creates a Next.js middleware function that scans every request
145
+ * for prompt injection and secret exposure, and runs a cold-start
146
+ * file scan for dependency and runtime issues.
147
+ *
148
+ * Usage in middleware.ts:
149
+ * import { withWatchman } from '@kairosinternational/watchman-nextjs'
150
+ * export default withWatchman()
151
+ * export const config = { matcher: ['/((?!_next|static).*)'] }
152
+ */
153
+ function withWatchman(userConfig = {}) {
154
+ const config = (0, config_js_1.resolveConfig)(userConfig);
155
+ // Kick off cold-start scan (non-blocking)
156
+ void runColdStartScan(config);
157
+ return async function watchmanMiddleware(request) {
158
+ const pathname = request.nextUrl.pathname;
159
+ if (config.skipPaths.some((p) => pathname.startsWith(p))) {
160
+ return server_1.NextResponse.next();
161
+ }
162
+ const outcome = await scanRequest(request, config);
163
+ if (config.onFinding) {
164
+ for (const finding of outcome.findings) {
165
+ await config.onFinding(finding);
166
+ }
167
+ }
168
+ if (outcome.shouldBlock) {
169
+ if (!config.quiet) {
170
+ console.warn(`[watchman] BLOCKED ${request.method} ${pathname} -- ${outcome.maxSeverity}: ${outcome.findings.length} findings`);
171
+ }
172
+ return server_1.NextResponse.json({
173
+ error: 'Request blocked by Watchman security scan',
174
+ severity: outcome.maxSeverity,
175
+ findings: outcome.findings.map((f) => ({
176
+ id: f.id,
177
+ target: f.target,
178
+ rule: f.rule,
179
+ label: f.label,
180
+ severity: f.severity,
181
+ })),
182
+ }, { status: 403 });
183
+ }
184
+ const response = server_1.NextResponse.next();
185
+ if (outcome.shouldWarn) {
186
+ // Top finding is the highest-severity one that triggered the warn
187
+ const topFinding = outcome.findings.find((f) => f.severity === outcome.maxSeverity);
188
+ if (topFinding) {
189
+ response.headers.set('X-Watchman-Alert', topFinding.id);
190
+ response.headers.set('X-Watchman-Severity', topFinding.severity);
191
+ response.headers.set('X-Watchman-Findings', String(outcome.findings.length));
192
+ }
193
+ if (!config.quiet) {
194
+ console.warn(`[watchman] WARN ${request.method} ${pathname} -- ${outcome.maxSeverity}: ${outcome.findings.length} findings`);
195
+ }
196
+ }
197
+ return response;
198
+ };
199
+ }
200
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { NextRequest } from 'next/server';
2
+ import type { WatchmanMiddlewareConfig } from './types.js';
3
+ type RouteHandler = (request: NextRequest, context?: {
4
+ params: Record<string, string | string[]>;
5
+ }) => Promise<Response> | Response;
6
+ /**
7
+ * Higher-order wrapper for Next.js App Router route handlers.
8
+ * Runs Watchman scanning on the incoming request before invoking
9
+ * the underlying handler. Use this for per-route scanning when
10
+ * you don't want global middleware.
11
+ *
12
+ * Usage:
13
+ * export const POST = withWatchmanRoute(async (req) => { ... });
14
+ */
15
+ export declare function withWatchmanRoute<H extends RouteHandler>(handler: H, userConfig?: WatchmanMiddlewareConfig): H;
16
+ export {};
17
+ //# sourceMappingURL=route-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-handler.d.ts","sourceRoot":"","sources":["../src/route-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,OAAO,KAAK,EACV,wBAAwB,EAIzB,MAAM,YAAY,CAAC;AAEpB,KAAK,YAAY,GAAG,CAClB,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAA;CAAE,KACpD,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;AA+DlC;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,YAAY,EACtD,OAAO,EAAE,CAAC,EACV,UAAU,GAAE,wBAA6B,GACxC,CAAC,CAmDH"}
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withWatchmanRoute = withWatchmanRoute;
4
+ const server_1 = require("next/server");
5
+ const config_js_1 = require("./config.js");
6
+ const middleware_js_1 = require("./middleware.js");
7
+ const SEVERITY_ORDER = {
8
+ critical: 4,
9
+ high: 3,
10
+ medium: 2,
11
+ low: 1,
12
+ info: 0,
13
+ };
14
+ function severityAtLeast(a, threshold) {
15
+ return SEVERITY_ORDER[a] >= SEVERITY_ORDER[threshold];
16
+ }
17
+ function maxSeverity(findings) {
18
+ if (findings.length === 0)
19
+ return null;
20
+ return findings.reduce((max, f) => (SEVERITY_ORDER[f.severity] > SEVERITY_ORDER[max] ? f.severity : max), 'info');
21
+ }
22
+ async function scanHandlerRequest(request, config) {
23
+ const findings = [];
24
+ if (config.scanUrl) {
25
+ let decoded = request.url;
26
+ try {
27
+ decoded = decodeURIComponent(decoded);
28
+ }
29
+ catch {
30
+ // malformed -- scan as-is
31
+ }
32
+ findings.push(...(0, middleware_js_1.scanString)(decoded, 'url'));
33
+ }
34
+ if (config.scanHeaders) {
35
+ for (const [key, value] of request.headers.entries()) {
36
+ if (key.toLowerCase() === 'cookie')
37
+ continue;
38
+ findings.push(...(0, middleware_js_1.scanString)(`${key}: ${value}`, 'headers'));
39
+ }
40
+ }
41
+ if (config.scanBody && ['POST', 'PUT', 'PATCH'].includes(request.method)) {
42
+ try {
43
+ const cloned = request.clone();
44
+ const body = await cloned.text();
45
+ if (body)
46
+ findings.push(...(0, middleware_js_1.scanString)(body, 'body'));
47
+ }
48
+ catch {
49
+ // unreadable body
50
+ }
51
+ }
52
+ const maxSev = maxSeverity(findings);
53
+ const shouldBlock = maxSev !== null && severityAtLeast(maxSev, config.blockAt);
54
+ const shouldWarn = maxSev !== null && !shouldBlock && severityAtLeast(maxSev, config.warnAt);
55
+ return { findings, maxSeverity: maxSev, shouldBlock, shouldWarn };
56
+ }
57
+ /**
58
+ * Higher-order wrapper for Next.js App Router route handlers.
59
+ * Runs Watchman scanning on the incoming request before invoking
60
+ * the underlying handler. Use this for per-route scanning when
61
+ * you don't want global middleware.
62
+ *
63
+ * Usage:
64
+ * export const POST = withWatchmanRoute(async (req) => { ... });
65
+ */
66
+ function withWatchmanRoute(handler, userConfig = {}) {
67
+ const config = (0, config_js_1.resolveConfig)(userConfig);
68
+ const wrapped = async (request, context) => {
69
+ const outcome = await scanHandlerRequest(request, config);
70
+ if (config.onFinding) {
71
+ for (const finding of outcome.findings) {
72
+ await config.onFinding(finding);
73
+ }
74
+ }
75
+ if (outcome.shouldBlock) {
76
+ if (!config.quiet) {
77
+ console.warn(`[watchman] BLOCKED route handler ${request.method} ${new URL(request.url).pathname} -- ${outcome.maxSeverity}`);
78
+ }
79
+ return server_1.NextResponse.json({
80
+ error: 'Request blocked by Watchman security scan',
81
+ severity: outcome.maxSeverity,
82
+ findings: outcome.findings.map((f) => ({
83
+ id: f.id,
84
+ target: f.target,
85
+ rule: f.rule,
86
+ label: f.label,
87
+ severity: f.severity,
88
+ })),
89
+ }, { status: 403 });
90
+ }
91
+ const response = await handler(request, context);
92
+ if (outcome.shouldWarn) {
93
+ const topFinding = outcome.findings.find((f) => f.severity === outcome.maxSeverity);
94
+ if (topFinding) {
95
+ response.headers.set('X-Watchman-Alert', topFinding.id);
96
+ response.headers.set('X-Watchman-Severity', topFinding.severity);
97
+ response.headers.set('X-Watchman-Findings', String(outcome.findings.length));
98
+ }
99
+ }
100
+ return response;
101
+ };
102
+ return wrapped;
103
+ }
104
+ //# sourceMappingURL=route-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-handler.js","sourceRoot":"","sources":["../src/route-handler.ts"],"names":[],"mappings":";;AAsFA,8CAsDC;AA3ID,wCAA2C;AAC3C,2CAA4C;AAC5C,mDAA6C;AAa7C,MAAM,cAAc,GAA6B;IAC/C,QAAQ,EAAE,CAAC;IACX,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,GAAG,EAAE,CAAC;IACN,IAAI,EAAE,CAAC;CACR,CAAC;AAEF,SAAS,eAAe,CAAC,CAAW,EAAE,SAAmB;IACvD,OAAO,cAAc,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,SAAS,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,WAAW,CAAC,QAA0B;IAC7C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,OAAO,QAAQ,CAAC,MAAM,CACpB,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,EACjF,MAAM,CACP,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,OAAoB,EACpB,MAAwC;IAExC,MAAM,QAAQ,GAAqB,EAAE,CAAC;IAEtC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,IAAI,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC;QAC1B,IAAI,CAAC;YACH,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,0BAA0B;QAC5B,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAA,0BAAU,EAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YACrD,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,QAAQ;gBAAE,SAAS;YAC7C,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAA,0BAAU,EAAC,GAAG,GAAG,KAAK,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACzE,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACjC,IAAI,IAAI;gBAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAA,0BAAU,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QACvD,CAAC;QAAC,MAAM,CAAC;YACP,kBAAkB;QACpB,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,WAAW,GAAG,MAAM,KAAK,IAAI,IAAI,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC/E,MAAM,UAAU,GACd,MAAM,KAAK,IAAI,IAAI,CAAC,WAAW,IAAI,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAE5E,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;AACpE,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,iBAAiB,CAC/B,OAAU,EACV,aAAuC,EAAE;IAEzC,MAAM,MAAM,GAAG,IAAA,yBAAa,EAAC,UAAU,CAAC,CAAC;IAEzC,MAAM,OAAO,GAAiB,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;QACvD,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAE1D,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACvC,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QAED,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBAClB,OAAO,CAAC,IAAI,CACV,oCAAoC,OAAO,CAAC,MAAM,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,OAAO,OAAO,CAAC,WAAW,EAAE,CAChH,CAAC;YACJ,CAAC;YACD,OAAO,qBAAY,CAAC,IAAI,CACtB;gBACE,KAAK,EAAE,2CAA2C;gBAClD,QAAQ,EAAE,OAAO,CAAC,WAAW;gBAC7B,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACrC,EAAE,EAAE,CAAC,CAAC,EAAE;oBACR,MAAM,EAAE,CAAC,CAAC,MAAM;oBAChB,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,KAAK,EAAE,CAAC,CAAC,KAAK;oBACd,QAAQ,EAAE,CAAC,CAAC,QAAQ;iBACrB,CAAC,CAAC;aACJ,EACD,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAEjD,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CACtC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,WAAW,CAC1C,CAAC;YACF,IAAI,UAAU,EAAE,CAAC;gBACf,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC;gBACxD,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;gBACjE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;YAC/E,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,OAAY,CAAC;AACtB,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { Severity, WatchmanReport, Finding } from '@kairosinternational/watchman';
2
+ export type ScanTarget = 'url' | 'headers' | 'body' | 'response';
3
+ export interface RequestFinding {
4
+ id: string;
5
+ target: ScanTarget;
6
+ rule: string;
7
+ severity: Severity;
8
+ label: string;
9
+ message: string;
10
+ evidence: string;
11
+ }
12
+ export interface WatchmanMiddlewareConfig {
13
+ /** Project root for cold-start file scanning. Defaults to process.cwd() */
14
+ projectRoot?: string;
15
+ /** Severity at or above which to block the request. Default: 'critical' */
16
+ blockAt?: Severity;
17
+ /** Severity at or above which to attach a warning header. Default: 'high' */
18
+ warnAt?: Severity;
19
+ /** Scan request body for secrets and injection. Default: true */
20
+ scanBody?: boolean;
21
+ /** Scan request headers. Default: true */
22
+ scanHeaders?: boolean;
23
+ /** Scan request URL and query params. Default: true */
24
+ scanUrl?: boolean;
25
+ /** Run cold-start scan (dependency-integrity + runtime-monitor) at init. Default: true */
26
+ coldStartScan?: boolean;
27
+ /** Paths to skip entirely. Default: ['/_next', '/favicon.ico'] */
28
+ skipPaths?: string[];
29
+ /** Called on every finding, useful for telemetry */
30
+ onFinding?: (finding: RequestFinding) => void | Promise<void>;
31
+ /** Called once when cold-start scan completes */
32
+ onColdStartComplete?: (report: WatchmanReport) => void | Promise<void>;
33
+ /** Quiet mode -- suppress console logging. Default: false */
34
+ quiet?: boolean;
35
+ }
36
+ export interface ScanOutcome {
37
+ findings: RequestFinding[];
38
+ maxSeverity: Severity | null;
39
+ shouldBlock: boolean;
40
+ shouldWarn: boolean;
41
+ }
42
+ export type { Finding, WatchmanReport, Severity };
43
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAEvF,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,QAAQ,CAAC;IAEnB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,QAAQ,CAAC;IAElB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,0FAA0F;IAC1F,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB,oDAAoD;IACpD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9D,iDAAiD;IACjD,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvE,6DAA6D;IAC7D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,WAAW,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@kairosinternational/watchman-nextjs",
3
+ "version": "0.1.0",
4
+ "description": "Next.js 14 App Router adapter for @kairosinternational/watchman",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsc --watch",
16
+ "test": "vitest run",
17
+ "test:watch": "vitest",
18
+ "typecheck": "tsc --noEmit"
19
+ },
20
+ "keywords": ["nextjs", "security", "middleware", "watchman"],
21
+ "author": "Kai'Ros International Technologies",
22
+ "license": "UNLICENSED",
23
+ "engines": {
24
+ "node": ">=18"
25
+ },
26
+ "dependencies": {
27
+ "@kairosinternational/watchman": "0.1.0"
28
+ },
29
+ "peerDependencies": {
30
+ "next": "^14.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "typescript": "^5.7.0",
34
+ "vitest": "^3.0.0",
35
+ "@types/node": "^22.0.0",
36
+ "next": "^14.2.0"
37
+ }
38
+ }
package/src/config.ts ADDED
@@ -0,0 +1,24 @@
1
+ import type { WatchmanMiddlewareConfig } from './types.js';
2
+
3
+ export const DEFAULT_MIDDLEWARE_CONFIG: Required<
4
+ Omit<WatchmanMiddlewareConfig, 'projectRoot' | 'onFinding' | 'onColdStartComplete'>
5
+ > = {
6
+ blockAt: 'critical',
7
+ warnAt: 'high',
8
+ scanBody: true,
9
+ scanHeaders: true,
10
+ scanUrl: true,
11
+ coldStartScan: true,
12
+ skipPaths: ['/_next', '/favicon.ico', '/static'],
13
+ quiet: false,
14
+ };
15
+
16
+ export function resolveConfig(
17
+ user: WatchmanMiddlewareConfig = {},
18
+ ): WatchmanMiddlewareConfig & typeof DEFAULT_MIDDLEWARE_CONFIG {
19
+ return {
20
+ ...DEFAULT_MIDDLEWARE_CONFIG,
21
+ projectRoot: user.projectRoot ?? process.cwd(),
22
+ ...user,
23
+ };
24
+ }
package/src/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ export { withWatchman, scanRequest, scanString } from './middleware.js';
2
+ export { withWatchmanRoute } from './route-handler.js';
3
+ export { DEFAULT_MIDDLEWARE_CONFIG, resolveConfig } from './config.js';
4
+ export type {
5
+ WatchmanMiddlewareConfig,
6
+ RequestFinding,
7
+ ScanOutcome,
8
+ ScanTarget,
9
+ Severity,
10
+ Finding,
11
+ WatchmanReport,
12
+ } from './types.js';