@patchstack/connect 0.4.2 → 0.5.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.
- package/AGENT-INSTALL.md +21 -4
- package/README.md +10 -2
- package/dist/cli.js +277 -56
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +221 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +218 -15
- package/dist/index.js.map +1 -1
- package/dist/protect/templates/astro-middleware.ts +22 -1
- package/dist/protect/templates/express-guard.cjs +54 -4
- package/dist/protect/templates/express-guard.js +54 -4
- package/dist/protect/templates/express-guard.ts +43 -5
- package/dist/protect/templates/fastify-plugin.cjs +41 -3
- package/dist/protect/templates/fastify-plugin.js +41 -3
- package/dist/protect/templates/fastify-plugin.ts +30 -2
- package/dist/protect/templates/generic-guard.cjs +58 -5
- package/dist/protect/templates/generic-guard.js +58 -5
- package/dist/protect/templates/generic-guard.ts +47 -4
- package/dist/protect/templates/guard.ts +42 -8
- package/dist/protect/templates/next-middleware.ts +22 -1
- package/dist/protect/templates/nuxt-middleware.ts +22 -1
- package/dist/protect/templates/sveltekit-hooks.ts +22 -1
- package/dist/protect.cjs +341 -94
- package/dist/protect.cjs.map +1 -1
- package/dist/protect.edge.js +56 -24
- package/dist/protect.edge.js.map +1 -1
- package/dist/protect.js +57 -25
- package/dist/protect.js.map +1 -1
- package/dist/{refresh-manifest-JNCTIAM5.js → refresh-manifest-LDS35UKV.js} +234 -33
- package/dist/refresh-manifest-LDS35UKV.js.map +1 -0
- package/package.json +3 -3
- package/dist/refresh-manifest-JNCTIAM5.js.map +0 -1
|
@@ -74,6 +74,26 @@ async function buildProtection() {
|
|
|
74
74
|
);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
|
|
78
|
+
// A protection that could not be built must not become an app that cannot answer. Each seam below asks
|
|
79
|
+
// for one, steps aside when it cannot have one, and leaves the app to carry on unscreened.
|
|
80
|
+
// `getProtection` clears its slot on a failed build, so the next request builds again — one bad start
|
|
81
|
+
// does not switch protection off for the life of the process.
|
|
82
|
+
//
|
|
83
|
+
// Only the FIRST failure is reported: enough to know the guard is not screening, without a line per
|
|
84
|
+
// request. A later failure is not reported, including one with a different cause.
|
|
85
|
+
let psUnavailable = false;
|
|
86
|
+
function psStepAside(err: unknown) {
|
|
87
|
+
if (!psUnavailable) {
|
|
88
|
+
psUnavailable = true;
|
|
89
|
+
console.warn(
|
|
90
|
+
"[patchstack] protection is unavailable; traffic may pass through unscreened until a later attempt succeeds. Reported once per process. Cause: " +
|
|
91
|
+
(err instanceof Error ? err.message : String(err)),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
77
97
|
// Request-middleware path: the browser tunnels its direct Supabase calls here.
|
|
78
98
|
let _handle: ((request: Request) => Promise<Response>) | undefined;
|
|
79
99
|
export async function handleGuardRequest(request: Request): Promise<Response> {
|
|
@@ -91,7 +111,10 @@ export async function handleGuardRequest(request: Request): Promise<Response> {
|
|
|
91
111
|
let _inspect: ((data: unknown) => Promise<{ rule?: string; message: string } | null>) | undefined;
|
|
92
112
|
export async function inspectServerFn(data: unknown): Promise<{ rule?: string; message: string } | null> {
|
|
93
113
|
if (!_inspect) {
|
|
94
|
-
|
|
114
|
+
const protection = await getProtection().catch(psStepAside);
|
|
115
|
+
// Null is "allow" here, which is what the two paths below already answer when they cannot screen.
|
|
116
|
+
if (!protection) return null;
|
|
117
|
+
_inspect = createServerFnGuard({ protection });
|
|
95
118
|
}
|
|
96
119
|
return _inspect(data);
|
|
97
120
|
}
|
|
@@ -105,12 +128,17 @@ export async function inspectServerFn(data: unknown): Promise<{ rule?: string; m
|
|
|
105
128
|
// route or a method, and the engine can only apply that scope if it is given the request the response
|
|
106
129
|
// belongs to. Without it a scoped response rule is delivered, counted as protection, and matches nothing.
|
|
107
130
|
export async function screenResponse<T>(response: T, request?: Request): Promise<T> {
|
|
131
|
+
if (!(response instanceof Response)) return response;
|
|
132
|
+
const protection = await getProtection().catch(psStepAside);
|
|
133
|
+
if (!protection) return response;
|
|
108
134
|
try {
|
|
109
|
-
if (!(response instanceof Response)) return response;
|
|
110
|
-
const protection = await getProtection();
|
|
111
135
|
return (protection.screenResponse ? await protection.screenResponse(response, request) : response) as T;
|
|
112
|
-
} catch {
|
|
113
|
-
|
|
136
|
+
} catch (err) {
|
|
137
|
+
// Reported, then the response goes out as it is. A response that was never screened and a response
|
|
138
|
+
// that had nothing to redact look identical from here, so silence would make them one.
|
|
139
|
+
psStepAside(err);
|
|
140
|
+
|
|
141
|
+
return response;
|
|
114
142
|
}
|
|
115
143
|
}
|
|
116
144
|
|
|
@@ -120,10 +148,16 @@ export async function screenResponse<T>(response: T, request?: Request): Promise
|
|
|
120
148
|
// WAF false-positive surface, so it is opt-in. `request.clone()` so the app can still read the body.
|
|
121
149
|
let _reqGuard: ((request: Request) => Promise<Response | null>) | undefined;
|
|
122
150
|
export async function guardRequest(request: Request): Promise<Response | null> {
|
|
151
|
+
if (!_reqGuard) {
|
|
152
|
+
const protection = await getProtection().catch(psStepAside);
|
|
153
|
+
if (!protection) return null; // null is "allow" here
|
|
154
|
+
_reqGuard = protection.fetchGuard();
|
|
155
|
+
}
|
|
123
156
|
try {
|
|
124
|
-
if (!_reqGuard) _reqGuard = (await getProtection()).fetchGuard();
|
|
125
157
|
return await _reqGuard(request.clone());
|
|
126
|
-
} catch {
|
|
127
|
-
|
|
158
|
+
} catch (err) {
|
|
159
|
+
psStepAside(err);
|
|
160
|
+
|
|
161
|
+
return null;
|
|
128
162
|
}
|
|
129
163
|
}
|
|
@@ -42,9 +42,30 @@ async function buildProtection() {
|
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
|
|
46
|
+
// A protection that could not be built must not become an app that cannot answer. Each seam below asks
|
|
47
|
+
// for one, steps aside when it cannot have one, and leaves the app to carry on unscreened.
|
|
48
|
+
// `getProtection` clears its slot on a failed build, so the next request builds again — one bad start
|
|
49
|
+
// does not switch protection off for the life of the process.
|
|
50
|
+
//
|
|
51
|
+
// Only the FIRST failure is reported: enough to know the guard is not screening, without a line per
|
|
52
|
+
// request. A later failure is not reported, including one with a different cause.
|
|
53
|
+
let psUnavailable = false;
|
|
54
|
+
function psStepAside(err: unknown) {
|
|
55
|
+
if (!psUnavailable) {
|
|
56
|
+
psUnavailable = true;
|
|
57
|
+
console.warn(
|
|
58
|
+
"[patchstack] protection is unavailable; traffic may pass through unscreened until a later attempt succeeds. Reported once per process. Cause: " +
|
|
59
|
+
(err instanceof Error ? err.message : String(err)),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
45
65
|
// #region patchstack-next (managed by patchstack-connect protect — do not edit)
|
|
46
66
|
export async function middleware(request: Request) {
|
|
47
|
-
const protection = await getProtection();
|
|
67
|
+
const protection = await getProtection().catch(psStepAside);
|
|
68
|
+
if (!protection) return; // Next continues to the route
|
|
48
69
|
const blocked = await protection.fetchGuard()(request);
|
|
49
70
|
if (blocked) return blocked; // 403 — blocked before it reaches your route
|
|
50
71
|
// otherwise fall through (Next continues to the route)
|
|
@@ -42,9 +42,30 @@ async function buildProtection() {
|
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
|
|
46
|
+
// A protection that could not be built must not become an app that cannot answer. Each seam below asks
|
|
47
|
+
// for one, steps aside when it cannot have one, and leaves the app to carry on unscreened.
|
|
48
|
+
// `getProtection` clears its slot on a failed build, so the next request builds again — one bad start
|
|
49
|
+
// does not switch protection off for the life of the process.
|
|
50
|
+
//
|
|
51
|
+
// Only the FIRST failure is reported: enough to know the guard is not screening, without a line per
|
|
52
|
+
// request. A later failure is not reported, including one with a different cause.
|
|
53
|
+
let psUnavailable = false;
|
|
54
|
+
function psStepAside(err: unknown) {
|
|
55
|
+
if (!psUnavailable) {
|
|
56
|
+
psUnavailable = true;
|
|
57
|
+
console.warn(
|
|
58
|
+
"[patchstack] protection is unavailable; traffic may pass through unscreened until a later attempt succeeds. Reported once per process. Cause: " +
|
|
59
|
+
(err instanceof Error ? err.message : String(err)),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
45
65
|
// #region patchstack-nuxt (managed by patchstack-connect protect — do not edit)
|
|
46
66
|
export default defineEventHandler(async (event) => {
|
|
47
|
-
const protection = await getProtection();
|
|
67
|
+
const protection = await getProtection().catch(psStepAside);
|
|
68
|
+
if (!protection) return; // Nitro continues to the route
|
|
48
69
|
const method = event.method ?? "GET";
|
|
49
70
|
// readRawBody caches on the event, so your route handlers can still read the body.
|
|
50
71
|
const body = method !== "GET" && method !== "HEAD" ? await readRawBody(event) : undefined;
|
|
@@ -42,9 +42,30 @@ async function buildProtection() {
|
|
|
42
42
|
);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
|
|
46
|
+
// A protection that could not be built must not become an app that cannot answer. Each seam below asks
|
|
47
|
+
// for one, steps aside when it cannot have one, and leaves the app to carry on unscreened.
|
|
48
|
+
// `getProtection` clears its slot on a failed build, so the next request builds again — one bad start
|
|
49
|
+
// does not switch protection off for the life of the process.
|
|
50
|
+
//
|
|
51
|
+
// Only the FIRST failure is reported: enough to know the guard is not screening, without a line per
|
|
52
|
+
// request. A later failure is not reported, including one with a different cause.
|
|
53
|
+
let psUnavailable = false;
|
|
54
|
+
function psStepAside(err: unknown) {
|
|
55
|
+
if (!psUnavailable) {
|
|
56
|
+
psUnavailable = true;
|
|
57
|
+
console.warn(
|
|
58
|
+
"[patchstack] protection is unavailable; traffic may pass through unscreened until a later attempt succeeds. Reported once per process. Cause: " +
|
|
59
|
+
(err instanceof Error ? err.message : String(err)),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
45
65
|
// #region patchstack-sveltekit (managed by patchstack-connect protect — do not edit)
|
|
46
66
|
export const handle: Handle = async ({ event, resolve }) => {
|
|
47
|
-
const protection = await getProtection();
|
|
67
|
+
const protection = await getProtection().catch(psStepAside);
|
|
68
|
+
if (!protection) return resolve(event);
|
|
48
69
|
const blocked = await protection.fetchGuard()(event.request);
|
|
49
70
|
if (blocked) return blocked; // 403 — blocked before it reaches your route
|
|
50
71
|
// Response rules can be scoped to a route or a method, and the engine can only apply that scope if it is
|