edgeaegis 2026.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +10 -0
- package/README.md +215 -0
- package/config.yml +29 -0
- package/origin/cloudflare/app/config.yml +2 -0
- package/origin/cloudflare/app/index.js +4 -0
- package/origin/edgeone/app/config.yml +2 -0
- package/origin/edgeone/app/index.js +4 -0
- package/origin/esa/app/config.yml +2 -0
- package/origin/esa/app/index.js +4 -0
- package/origin/netlify/app/config.yml +2 -0
- package/origin/netlify/app/index.js +4 -0
- package/origin/vercel/app/config.yml +2 -0
- package/origin/vercel/app/index.js +4 -0
- package/package.json +123 -0
- package/respond/403.html +2 -0
- package/respond/403.json +1 -0
- package/rules/allowlist.yml +2 -0
- package/rules/emergency.conf +4 -0
- package/rules/local.conf +2 -0
- package/rules/seclang-bundle.json +360 -0
- package/rules/seclang-upstreams.json +14 -0
- package/rules/setup.conf +15 -0
- package/secret/credentials.example.yml +7 -0
- package/secret/relay-server.example.yml +10 -0
- package/tools/.build/scr/adapters/cloudflare/runtime.d.ts +18 -0
- package/tools/.build/scr/adapters/cloudflare/runtime.js +16 -0
- package/tools/.build/scr/adapters/cloudflare/runtime.js.map +1 -0
- package/tools/.build/scr/adapters/cloudflare/worker.js +25 -0
- package/tools/.build/scr/adapters/edgeone/worker.js +7 -0
- package/tools/.build/scr/adapters/esa/worker.js +5 -0
- package/tools/.build/scr/adapters/forward.d.ts +4 -0
- package/tools/.build/scr/adapters/forward.js +25 -0
- package/tools/.build/scr/adapters/forward.js.map +1 -0
- package/tools/.build/scr/adapters/handler.js +39 -0
- package/tools/.build/scr/adapters/ingress.d.ts +10 -0
- package/tools/.build/scr/adapters/ingress.js +62 -0
- package/tools/.build/scr/adapters/ingress.js.map +1 -0
- package/tools/.build/scr/adapters/netlify/worker.js +7 -0
- package/tools/.build/scr/adapters/runtime.d.ts +33 -0
- package/tools/.build/scr/adapters/runtime.js +50 -0
- package/tools/.build/scr/adapters/runtime.js.map +1 -0
- package/tools/.build/scr/adapters/vercel/worker.js +7 -0
- package/tools/.build/scr/cli/config.d.ts +2 -0
- package/tools/.build/scr/cli/config.js +23 -0
- package/tools/.build/scr/cli/config.js.map +1 -0
- package/tools/.build/scr/cli/deployment.d.ts +15 -0
- package/tools/.build/scr/cli/deployment.js +108 -0
- package/tools/.build/scr/cli/deployment.js.map +1 -0
- package/tools/.build/scr/cli/init.d.ts +2 -0
- package/tools/.build/scr/cli/init.js +62 -0
- package/tools/.build/scr/cli/init.js.map +1 -0
- package/tools/.build/scr/cli/main.d.ts +2 -0
- package/tools/.build/scr/cli/main.js +99 -0
- package/tools/.build/scr/cli/main.js.map +1 -0
- package/tools/.build/scr/cli/relay-serve.d.ts +1 -0
- package/tools/.build/scr/cli/relay-serve.js +48 -0
- package/tools/.build/scr/cli/relay-serve.js.map +1 -0
- package/tools/.build/scr/cli/responses.d.ts +2 -0
- package/tools/.build/scr/cli/responses.js +31 -0
- package/tools/.build/scr/cli/responses.js.map +1 -0
- package/tools/.build/scr/cli/serve.d.ts +1 -0
- package/tools/.build/scr/cli/serve.js +132 -0
- package/tools/.build/scr/cli/serve.js.map +1 -0
- package/tools/.build/scr/config.d.ts +39 -0
- package/tools/.build/scr/config.js +89 -0
- package/tools/.build/scr/config.js.map +1 -0
- package/tools/.build/scr/core/types.d.ts +27 -0
- package/tools/.build/scr/core/types.js +34 -0
- package/tools/.build/scr/core/types.js.map +1 -0
- package/tools/.build/scr/core/version.d.ts +1 -0
- package/tools/.build/scr/core/version.js +3 -0
- package/tools/.build/scr/core/version.js.map +1 -0
- package/tools/.build/scr/http-boundary/index.d.ts +19 -0
- package/tools/.build/scr/http-boundary/index.js +173 -0
- package/tools/.build/scr/http-boundary/index.js.map +1 -0
- package/tools/.build/scr/http-boundary/json.d.ts +2 -0
- package/tools/.build/scr/http-boundary/json.js +90 -0
- package/tools/.build/scr/http-boundary/json.js.map +1 -0
- package/tools/.build/scr/origin/relay.d.ts +20 -0
- package/tools/.build/scr/origin/relay.js +221 -0
- package/tools/.build/scr/origin/relay.js.map +1 -0
- package/tools/.build/scr/origin/replay-node.d.ts +8 -0
- package/tools/.build/scr/origin/replay-node.js +36 -0
- package/tools/.build/scr/origin/replay-node.js.map +1 -0
- package/tools/.build/scr/origin/types.d.ts +18 -0
- package/tools/.build/scr/origin/types.js +2 -0
- package/tools/.build/scr/origin/types.js.map +1 -0
- package/tools/.build/scr/runtime/allowlist.d.ts +21 -0
- package/tools/.build/scr/runtime/allowlist.js +84 -0
- package/tools/.build/scr/runtime/allowlist.js.map +1 -0
- package/tools/.build/scr/runtime/browser-cache.d.ts +7 -0
- package/tools/.build/scr/runtime/browser-cache.js +50 -0
- package/tools/.build/scr/runtime/browser-cache.js.map +1 -0
- package/tools/.build/scr/runtime/error-pages.d.ts +10 -0
- package/tools/.build/scr/runtime/error-pages.js +72 -0
- package/tools/.build/scr/runtime/error-pages.js.map +1 -0
- package/tools/.build/scr/runtime/index.d.ts +54 -0
- package/tools/.build/scr/runtime/index.js +355 -0
- package/tools/.build/scr/runtime/index.js.map +1 -0
- package/tools/.build/scr/runtime/node.d.ts +3 -0
- package/tools/.build/scr/runtime/node.js +97 -0
- package/tools/.build/scr/runtime/node.js.map +1 -0
- package/tools/.build/scr/runtime/proxy.d.ts +12 -0
- package/tools/.build/scr/runtime/proxy.js +177 -0
- package/tools/.build/scr/runtime/proxy.js.map +1 -0
- package/tools/.build/scr/runtime/stream.d.ts +8 -0
- package/tools/.build/scr/runtime/stream.js +72 -0
- package/tools/.build/scr/runtime/stream.js.map +1 -0
- package/tools/.build/scr/seclang/body.d.ts +13 -0
- package/tools/.build/scr/seclang/body.js +109 -0
- package/tools/.build/scr/seclang/body.js.map +1 -0
- package/tools/.build/scr/seclang/bytes.d.ts +5 -0
- package/tools/.build/scr/seclang/bytes.js +38 -0
- package/tools/.build/scr/seclang/bytes.js.map +1 -0
- package/tools/.build/scr/seclang/index.d.ts +166 -0
- package/tools/.build/scr/seclang/index.js +1519 -0
- package/tools/.build/scr/seclang/index.js.map +1 -0
- package/tools/.build/scr/seclang/json.d.ts +2 -0
- package/tools/.build/scr/seclang/json.js +87 -0
- package/tools/.build/scr/seclang/json.js.map +1 -0
- package/tools/.build/scr/seclang/libinjection.d.ts +9 -0
- package/tools/.build/scr/seclang/libinjection.js +59 -0
- package/tools/.build/scr/seclang/libinjection.js.map +1 -0
- package/tools/.build/scr/seclang/node.d.ts +22 -0
- package/tools/.build/scr/seclang/node.js +113 -0
- package/tools/.build/scr/seclang/node.js.map +1 -0
- package/tools/.build/scr/seclang/pcre2.d.ts +23 -0
- package/tools/.build/scr/seclang/pcre2.js +153 -0
- package/tools/.build/scr/seclang/pcre2.js.map +1 -0
- package/tools/.build/scr/seclang/sources.d.ts +6 -0
- package/tools/.build/scr/seclang/sources.js +59 -0
- package/tools/.build/scr/seclang/sources.js.map +1 -0
- package/tools/.build/scr/seclang/state.d.ts +17 -0
- package/tools/.build/scr/seclang/state.js +53 -0
- package/tools/.build/scr/seclang/state.js.map +1 -0
- package/tools/.build/scr/seclang/syntax.d.ts +9 -0
- package/tools/.build/scr/seclang/syntax.js +74 -0
- package/tools/.build/scr/seclang/syntax.js.map +1 -0
- package/tools/.build/scr/seclang/transforms.d.ts +3 -0
- package/tools/.build/scr/seclang/transforms.js +157 -0
- package/tools/.build/scr/seclang/transforms.js.map +1 -0
- package/tools/.build/scr/seclang/unicode.d.ts +2 -0
- package/tools/.build/scr/seclang/unicode.js +19 -0
- package/tools/.build/scr/seclang/unicode.js.map +1 -0
- package/tools/.build/scr/seclang/vendor/LIBINJECTION-LICENSE +33 -0
- package/tools/.build/scr/seclang/vendor/PCRE2-NOTICE +47 -0
- package/tools/.build/scr/seclang/vendor/PCRE2-WRAPPER-LICENSE +21 -0
- package/tools/.build/scr/seclang/vendor/UNICODE-DATA-NOTICE +6 -0
- package/tools/.build/scr/seclang/vendor/libinjection.json +7 -0
- package/tools/.build/scr/seclang/vendor/libinjection.wasm +0 -0
- package/tools/.build/scr/seclang/vendor/pcre2.json +7 -0
- package/tools/.build/scr/seclang/vendor/pcre2.wasm +0 -0
- package/tools/.build/scr/seclang/vendor/unicode.json +378 -0
- package/tools/build-platform.mjs +199 -0
- package/tools/platform-command.mjs +59 -0
- package/tools/sync-native-seclang.mjs +155 -0
- package/tools/verify-full-crs.mjs +118 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"sources": [
|
|
4
|
+
{
|
|
5
|
+
"id": "owasp-crs",
|
|
6
|
+
"repository": "coreruleset/coreruleset",
|
|
7
|
+
"ref": "v4.30.0",
|
|
8
|
+
"commit": "e03a4f6dabc7a30ebd8c52c97d28a154f590a48f",
|
|
9
|
+
"rulesDirectory": "rules",
|
|
10
|
+
"setup": "crs-setup.conf.example",
|
|
11
|
+
"license": "LICENSE"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
package/rules/setup.conf
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Operator-owned behavior policy, loaded before upstream rules. Updaters never replace it.
|
|
2
|
+
# Per engine/isolate: burst 120 requests, refill 20/s, ban 300s; 3 confirmed attacks/60s also ban.
|
|
3
|
+
# All values/actions and exemptions remain controlled by the operator.
|
|
4
|
+
SecAction "id:1000200,phase:1,pass,nolog,initcol:ip=%{REMOTE_ADDR}"
|
|
5
|
+
Include emergency.conf
|
|
6
|
+
SecRule IP:edgeshield_blocked "@eq 1" "id:1000201,phase:1,deny,status:403,log,msg:'Temporary behavior protection'"
|
|
7
|
+
SecAction "id:1000202,phase:1,pass,nolog,deprecatevar:ip.edgeshield_rate=20/1,setvar:ip.edgeshield_rate=+1,expirevar:ip.edgeshield_rate=60"
|
|
8
|
+
SecRule IP:edgeshield_rate "@gt 120" "id:1000203,phase:1,deny,status:403,log,msg:'Request burst protection',setvar:ip.edgeshield_blocked=1,expirevar:ip.edgeshield_blocked=300,setvar:!ip.edgeshield_attacks,setvar:!ip.edgeshield_rate"
|
|
9
|
+
# Count once, after disrupted requests too; chained effects are applied only on their own match.
|
|
10
|
+
SecAction "id:1000208,phase:5,pass,nolog,initcol:ip=%{REMOTE_ADDR}"
|
|
11
|
+
SecRule TX:blocking_inbound_anomaly_score "@ge %{tx.inbound_anomaly_score_threshold}" "id:1000206,phase:5,pass,nolog,setvar:tx.edgeshield_attack=1"
|
|
12
|
+
SecRule &IP:edgeshield_blocked "@eq 0" "id:1000204,phase:5,pass,nolog,chain"
|
|
13
|
+
SecRule TX:edgeshield_attack "@eq 1" "setvar:ip.edgeshield_attacks=+1,expirevar:ip.edgeshield_attacks=60"
|
|
14
|
+
SecRule &IP:edgeshield_blocked "@eq 0" "id:1000205,phase:5,pass,nolog,chain"
|
|
15
|
+
SecRule IP:edgeshield_attacks "@ge 3" "setvar:ip.edgeshield_blocked=1,expirevar:ip.edgeshield_blocked=300,setvar:!ip.edgeshield_attacks,setvar:!ip.edgeshield_rate"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Copy to credentials.yml, or run npm run secret:init to create independent random tokens.
|
|
2
|
+
# credentials.yml stays local and is excluded from Git and npm packages.
|
|
3
|
+
originToken: ""
|
|
4
|
+
# Optional client Bearer token for routes marked authenticated.
|
|
5
|
+
# clientToken: ""
|
|
6
|
+
# Required for signed transport metadata on Fetch-only cloud platforms.
|
|
7
|
+
# ingressKey: ""
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
publicHost: relay.example
|
|
2
|
+
listen:
|
|
3
|
+
host: 127.0.0.1
|
|
4
|
+
port: 8791
|
|
5
|
+
privateKeyFile: relay/private.jwk
|
|
6
|
+
authenticationKeyFile: relay/authentication.key
|
|
7
|
+
credentialsFile: credentials.yml
|
|
8
|
+
businessFile: ../origin/cloudflare/app/index.js
|
|
9
|
+
stateFile: ../tools/.state/relay-replay.db
|
|
10
|
+
maxBytes: 8388608
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type PortableOptions } from "../runtime.js";
|
|
2
|
+
export declare function createCloudflareRuntime(options: PortableOptions): Promise<{
|
|
3
|
+
engine: {
|
|
4
|
+
handle: (request: Request, context: import("../../core/types.js").TrustedContext, rawTarget?: string, hooks?: {
|
|
5
|
+
event?: (event: import("../../core/types.js").SecurityEvent) => void;
|
|
6
|
+
}) => Promise<Response>;
|
|
7
|
+
version: string;
|
|
8
|
+
dispose(): void;
|
|
9
|
+
stats: {
|
|
10
|
+
requests: number;
|
|
11
|
+
ruleEvaluations: number;
|
|
12
|
+
blocked: number;
|
|
13
|
+
originRequests: number;
|
|
14
|
+
eventFailures: number;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
handle(request: Request): Promise<Response>;
|
|
18
|
+
}>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createPortableRuntime } from "../runtime.js";
|
|
2
|
+
export async function createCloudflareRuntime(options) {
|
|
3
|
+
const runtime = await createPortableRuntime(options);
|
|
4
|
+
return {
|
|
5
|
+
...runtime,
|
|
6
|
+
handle(request) {
|
|
7
|
+
const protocol = request.cf?.httpProtocol;
|
|
8
|
+
const ip = request.headers.get("cf-connecting-ip");
|
|
9
|
+
return runtime.handle(request, {
|
|
10
|
+
...(protocol ? { httpProtocol: protocol } : {}),
|
|
11
|
+
...(ip ? { verifiedClientIp: ip } : {}),
|
|
12
|
+
});
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../../../scr/adapters/cloudflare/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAwB,MAAM,eAAe,CAAC;AAC5E,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,OAAwB;IACpE,MAAM,OAAO,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACrD,OAAO;QACL,GAAG,OAAO;QACV,MAAM,CAAC,OAAgB;YACrB,MAAM,QAAQ,GAAI,OAAwD,CAAC,EAAE,EAAE,YAAY,CAAC;YAC5F,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;YACnD,OAAO,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE;gBAC7B,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/C,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import deployment from "edgeaegis:deployment";
|
|
2
|
+
import originFetches from "edgeaegis:origin";
|
|
3
|
+
import { INJECTION, PCRE2 } from "edgeaegis:wasm";
|
|
4
|
+
import { createCloudflareRuntime } from "./runtime.js";
|
|
5
|
+
const runtime = await createCloudflareRuntime({
|
|
6
|
+
...deployment,
|
|
7
|
+
pcre2: PCRE2,
|
|
8
|
+
libinjection: INJECTION,
|
|
9
|
+
originFetches
|
|
10
|
+
});
|
|
11
|
+
var stdin_default = {
|
|
12
|
+
async fetch(request) {
|
|
13
|
+
try {
|
|
14
|
+
return runtime.handle(request);
|
|
15
|
+
} catch {
|
|
16
|
+
return Response.json(
|
|
17
|
+
{ error: "shield_unavailable" },
|
|
18
|
+
{ status: 503, headers: { "cache-control": "no-store" } }
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
export {
|
|
24
|
+
stdin_default as default
|
|
25
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Fetch } from "../core/types.js";
|
|
2
|
+
import { type IngressMetadata } from "./ingress.js";
|
|
3
|
+
/** Optional transport bridge for platforms whose Fetch API hides the wire protocol. */
|
|
4
|
+
export declare function createIngressForwarder(endpoint: string, secret: string, transport?: Fetch): (request: Request, metadata: IngressMetadata) => Promise<Response>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { signIngress } from "./ingress.js";
|
|
2
|
+
/** Optional transport bridge for platforms whose Fetch API hides the wire protocol. */
|
|
3
|
+
export function createIngressForwarder(endpoint, secret, transport = fetch) {
|
|
4
|
+
const destination = new URL(endpoint);
|
|
5
|
+
if (destination.protocol !== "https:" ||
|
|
6
|
+
destination.username ||
|
|
7
|
+
destination.password ||
|
|
8
|
+
destination.hash ||
|
|
9
|
+
destination.search ||
|
|
10
|
+
destination.pathname !== "/")
|
|
11
|
+
throw new Error("Ingress destination must be an exact HTTPS origin");
|
|
12
|
+
return async (request, metadata) => {
|
|
13
|
+
const original = new URL(request.url);
|
|
14
|
+
if (request.headers.get("host") !== original.host)
|
|
15
|
+
throw new Error("Ingress Host mismatch");
|
|
16
|
+
const target = new URL(original.pathname + original.search, destination);
|
|
17
|
+
const headers = new Headers(request.headers);
|
|
18
|
+
headers.set("host", target.host);
|
|
19
|
+
headers.delete("x-edgeshield-ingress");
|
|
20
|
+
const forwarded = new Request(target, new Request(request, { headers, redirect: "manual" }));
|
|
21
|
+
forwarded.headers.set("x-edgeshield-ingress", await signIngress(forwarded, { ...metadata, originalUrl: request.url }, secret));
|
|
22
|
+
return transport(forwarded);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=forward.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forward.js","sourceRoot":"","sources":["../../../../scr/adapters/forward.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,WAAW,EAAE,MAAM,cAAc,CAAC;AACjE,uFAAuF;AACvF,MAAM,UAAU,sBAAsB,CAAC,QAAgB,EAAE,MAAc,EAAE,SAAS,GAAU,KAAK;IAC/F,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtC,IACE,WAAW,CAAC,QAAQ,KAAK,QAAQ;QACjC,WAAW,CAAC,QAAQ;QACpB,WAAW,CAAC,QAAQ;QACpB,WAAW,CAAC,IAAI;QAChB,WAAW,CAAC,MAAM;QAClB,WAAW,CAAC,QAAQ,KAAK,GAAG;QAE5B,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,OAAO,KAAK,EAAE,OAAgB,EAAE,QAAyB,EAAE,EAAE;QAC3D,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC5F,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACjC,OAAO,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACvC,MAAM,SAAS,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QAC7F,SAAS,CAAC,OAAO,CAAC,GAAG,CACnB,sBAAsB,EACtB,MAAM,WAAW,CAAC,SAAS,EAAE,EAAE,GAAG,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAChF,CAAC;QACF,OAAO,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import deployment from "edgeaegis:deployment";
|
|
2
|
+
import originFetches from "edgeaegis:origin";
|
|
3
|
+
import { INJECTION, PCRE2 } from "edgeaegis:wasm";
|
|
4
|
+
import { verifyIngress } from "./ingress.js";
|
|
5
|
+
import { createPortableRuntime } from "./runtime.js";
|
|
6
|
+
let pending;
|
|
7
|
+
async function handleCloudRequest(request) {
|
|
8
|
+
try {
|
|
9
|
+
if (!deployment.credentials.ingressKey)
|
|
10
|
+
throw new Error("Configure a trusted ingress signing key");
|
|
11
|
+
const metadata = await verifyIngress(request, deployment.credentials.ingressKey);
|
|
12
|
+
pending ??= createPortableRuntime({
|
|
13
|
+
...deployment,
|
|
14
|
+
pcre2: PCRE2,
|
|
15
|
+
libinjection: INJECTION,
|
|
16
|
+
originFetches
|
|
17
|
+
});
|
|
18
|
+
const headers = new Headers(request.headers);
|
|
19
|
+
headers.delete("x-edgeshield-ingress");
|
|
20
|
+
if (metadata.originalUrl) headers.set("host", new URL(metadata.originalUrl).host);
|
|
21
|
+
const inspected = new Request(
|
|
22
|
+
metadata.originalUrl ?? request.url,
|
|
23
|
+
new Request(request, { headers })
|
|
24
|
+
);
|
|
25
|
+
return (await pending).handle(
|
|
26
|
+
inspected,
|
|
27
|
+
{ httpProtocol: metadata.httpProtocol, verifiedClientIp: metadata.verifiedClientIp },
|
|
28
|
+
metadata.rawTarget
|
|
29
|
+
);
|
|
30
|
+
} catch {
|
|
31
|
+
return Response.json(
|
|
32
|
+
{ error: "trusted_ingress_required" },
|
|
33
|
+
{ status: 503, headers: { "cache-control": "no-store" } }
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
handleCloudRequest
|
|
39
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TrustedContext } from "../core/types.js";
|
|
2
|
+
export type IngressMetadata = Omit<TrustedContext, "tenant" | "identity"> & {
|
|
3
|
+
originalUrl?: string;
|
|
4
|
+
rawTarget?: string;
|
|
5
|
+
};
|
|
6
|
+
type Metadata = IngressMetadata;
|
|
7
|
+
/** Sign at the trusted transport before forwarding to a Fetch-only cloud function. */
|
|
8
|
+
export declare function signIngress(request: Request, metadata: Metadata, secret: string, at?: number): Promise<string>;
|
|
9
|
+
export declare function verifyIngress(request: Request, secret: string, now?: number): Promise<Metadata>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import ipaddr from "ipaddr.js";
|
|
2
|
+
import { stableJson } from "../core/types.js";
|
|
3
|
+
const encoder = new TextEncoder();
|
|
4
|
+
async function key(secret) {
|
|
5
|
+
if (!/^[\w-]{43,256}$/.test(secret))
|
|
6
|
+
throw new Error("Invalid ingress signing key");
|
|
7
|
+
return crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"]);
|
|
8
|
+
}
|
|
9
|
+
function message(request, metadata, at) {
|
|
10
|
+
return encoder.encode(stableJson({ method: request.method, url: request.url, metadata, at }));
|
|
11
|
+
}
|
|
12
|
+
function validate(value) {
|
|
13
|
+
if (!/^HTTP\/(?:1\.0|1\.1|2(?:\.0)?|3(?:\.0)?)$/.test(value.httpProtocol ?? "") ||
|
|
14
|
+
!value.verifiedClientIp ||
|
|
15
|
+
!ipaddr.isValid(value.verifiedClientIp) ||
|
|
16
|
+
Object.keys(value).some((k) => !["httpProtocol", "verifiedClientIp", "originalUrl", "rawTarget"].includes(k)))
|
|
17
|
+
throw new Error("Actual ingress protocol and IP are required");
|
|
18
|
+
if (value.originalUrl !== undefined) {
|
|
19
|
+
const url = new URL(value.originalUrl);
|
|
20
|
+
if (url.protocol !== "https:" ||
|
|
21
|
+
url.username ||
|
|
22
|
+
url.password ||
|
|
23
|
+
url.hash ||
|
|
24
|
+
value.originalUrl.length > 16384)
|
|
25
|
+
throw new Error("Invalid original URL");
|
|
26
|
+
}
|
|
27
|
+
if (value.rawTarget !== undefined &&
|
|
28
|
+
(typeof value.rawTarget !== "string" ||
|
|
29
|
+
value.rawTarget.length > 16384 ||
|
|
30
|
+
!value.rawTarget.startsWith("/") ||
|
|
31
|
+
/[\r\n]/.test(value.rawTarget)))
|
|
32
|
+
throw new Error("Invalid original request target");
|
|
33
|
+
}
|
|
34
|
+
/** Sign at the trusted transport before forwarding to a Fetch-only cloud function. */
|
|
35
|
+
export async function signIngress(request, metadata, secret, at = Date.now()) {
|
|
36
|
+
validate(metadata);
|
|
37
|
+
const signature = new Uint8Array(await crypto.subtle.sign("HMAC", await key(secret), message(request, metadata, at)));
|
|
38
|
+
return JSON.stringify({
|
|
39
|
+
metadata,
|
|
40
|
+
at,
|
|
41
|
+
signature: Array.from(signature, (b) => b.toString(16).padStart(2, "0")).join(""),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
export async function verifyIngress(request, secret, now = Date.now()) {
|
|
45
|
+
const header = request.headers.get("x-edgeshield-ingress");
|
|
46
|
+
if (!header || header.length > 2048)
|
|
47
|
+
throw new Error("Signed ingress metadata is required");
|
|
48
|
+
const packet = JSON.parse(header);
|
|
49
|
+
if (!packet ||
|
|
50
|
+
Object.keys(packet).some((k) => !["metadata", "at", "signature"].includes(k)) ||
|
|
51
|
+
!Number.isSafeInteger(packet.at) ||
|
|
52
|
+
Math.abs(now - packet.at) > 30000 ||
|
|
53
|
+
typeof packet.signature !== "string" ||
|
|
54
|
+
!/^[a-f\d]{64}$/.test(packet.signature))
|
|
55
|
+
throw new Error("Invalid ingress metadata");
|
|
56
|
+
validate(packet.metadata);
|
|
57
|
+
const signature = Uint8Array.from(packet.signature.match(/../g), (p) => Number.parseInt(p, 16));
|
|
58
|
+
if (!(await crypto.subtle.verify("HMAC", await key(secret), signature, message(request, packet.metadata, packet.at))))
|
|
59
|
+
throw new Error("Ingress signature mismatch");
|
|
60
|
+
return packet.metadata;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=ingress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingress.js","sourceRoot":"","sources":["../../../../scr/adapters/ingress.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { DeploymentConfig, ProjectCredentials } from "../config.js";
|
|
2
|
+
import { type Fetch, type TrustedContext } from "../core/types.js";
|
|
3
|
+
import type { ErrorPages } from "../runtime/error-pages.js";
|
|
4
|
+
import { type AllowlistEntry } from "../runtime/index.js";
|
|
5
|
+
export interface PortableOptions {
|
|
6
|
+
config: DeploymentConfig;
|
|
7
|
+
credentials: ProjectCredentials;
|
|
8
|
+
source: string;
|
|
9
|
+
files: Record<string, string>;
|
|
10
|
+
pcre2: WebAssembly.Module | BufferSource;
|
|
11
|
+
libinjection: WebAssembly.Module | BufferSource;
|
|
12
|
+
allowlist: AllowlistEntry[];
|
|
13
|
+
errorPages?: ErrorPages;
|
|
14
|
+
originFetches: Record<string, Fetch>;
|
|
15
|
+
}
|
|
16
|
+
/** Every deployment uses this compiler, byte parser, transaction and response inspector. */
|
|
17
|
+
export declare function createPortableRuntime(options: PortableOptions): Promise<{
|
|
18
|
+
engine: {
|
|
19
|
+
handle: (request: Request, context: TrustedContext, rawTarget?: string, hooks?: {
|
|
20
|
+
event?: (event: import("../core/types.js").SecurityEvent) => void;
|
|
21
|
+
}) => Promise<Response>;
|
|
22
|
+
version: string;
|
|
23
|
+
dispose(): void;
|
|
24
|
+
stats: {
|
|
25
|
+
requests: number;
|
|
26
|
+
ruleEvaluations: number;
|
|
27
|
+
blocked: number;
|
|
28
|
+
originRequests: number;
|
|
29
|
+
eventFailures: number;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
handle(request: Request, transport: Omit<TrustedContext, "tenant">, rawTarget?: string): Promise<Response>;
|
|
33
|
+
}>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { digest } from "../core/types.js";
|
|
2
|
+
import { createShield } from "../runtime/index.js";
|
|
3
|
+
import { createInjectionDetectors, createPCRE2Backend } from "../seclang/index.js";
|
|
4
|
+
/** Every deployment uses this compiler, byte parser, transaction and response inspector. */
|
|
5
|
+
export async function createPortableRuntime(options) {
|
|
6
|
+
const engine = await createShield({
|
|
7
|
+
tenant: options.config.tenant,
|
|
8
|
+
service: options.config.service,
|
|
9
|
+
publicHosts: options.config.publicHosts,
|
|
10
|
+
routes: options.config.routes,
|
|
11
|
+
secLang: options.source,
|
|
12
|
+
secLangOptions: {
|
|
13
|
+
files: options.files,
|
|
14
|
+
regexBackend: await createPCRE2Backend(options.pcre2),
|
|
15
|
+
detectors: await createInjectionDetectors(options.libinjection),
|
|
16
|
+
},
|
|
17
|
+
allowlist: options.allowlist,
|
|
18
|
+
...(options.errorPages ? { errorPages: options.errorPages } : {}),
|
|
19
|
+
...(options.config.limits ? { limits: options.config.limits } : {}),
|
|
20
|
+
...(options.config.maxConcurrentRequests
|
|
21
|
+
? { maxConcurrentRequests: options.config.maxConcurrentRequests }
|
|
22
|
+
: {}),
|
|
23
|
+
origins: Object.fromEntries(Object.entries(options.config.origins).map(([name, origin]) => {
|
|
24
|
+
const fetch = options.originFetches[name];
|
|
25
|
+
if (!fetch)
|
|
26
|
+
throw new Error("Missing business transport");
|
|
27
|
+
return [name, { ...origin, originToken: options.credentials.originToken, fetch }];
|
|
28
|
+
})),
|
|
29
|
+
});
|
|
30
|
+
const clientHash = options.credentials.clientToken
|
|
31
|
+
? await digest(options.credentials.clientToken)
|
|
32
|
+
: undefined;
|
|
33
|
+
return {
|
|
34
|
+
engine,
|
|
35
|
+
async handle(request, transport, rawTarget) {
|
|
36
|
+
// Headers are never promoted to identity by the shared engine.
|
|
37
|
+
const token = request.headers.get("authorization");
|
|
38
|
+
const identity = clientHash && token?.startsWith("Bearer ") && (await digest(token.slice(7))) === clientHash
|
|
39
|
+
? "configured-api-client"
|
|
40
|
+
: undefined;
|
|
41
|
+
const context = {
|
|
42
|
+
...transport,
|
|
43
|
+
tenant: options.config.tenant,
|
|
44
|
+
...(identity ? { identity } : {}),
|
|
45
|
+
};
|
|
46
|
+
return engine.handle(request, context, rawTarget);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../../scr/adapters/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { open } from "node:fs/promises";
|
|
2
|
+
import { parseConfigText, validateCredentials } from "../config.js";
|
|
3
|
+
export async function readData(path, limit = 8_000_000) {
|
|
4
|
+
const file = await open(path, "r");
|
|
5
|
+
try {
|
|
6
|
+
const chunks = [];
|
|
7
|
+
let length = 0;
|
|
8
|
+
for await (const chunk of file.createReadStream({ autoClose: false, highWaterMark: 65536 })) {
|
|
9
|
+
length += chunk.length;
|
|
10
|
+
if (length > limit)
|
|
11
|
+
throw new Error("Configuration input exceeds byte limit");
|
|
12
|
+
chunks.push(chunk);
|
|
13
|
+
}
|
|
14
|
+
return parseConfigText(Buffer.concat(chunks, length).toString("utf8"));
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
await file.close();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function readCredentials(path) {
|
|
21
|
+
return validateCredentials(await readData(path, 16384));
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../scr/cli/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACpE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY,EAAE,KAAK,GAAG,SAAS;IAC5D,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,IAAI,CAAC,gBAAgB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;YAC5F,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC;YACvB,IAAI,MAAM,GAAG,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC9E,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QACD,OAAO,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACzE,CAAC;YAAS,CAAC;QACT,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;AACH,CAAC;AACD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY;IAChD,OAAO,mBAAmB,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type DeploymentConfig } from "../config.js";
|
|
2
|
+
import type { BusinessFunction, OriginSettings, PlatformName } from "../origin/types.js";
|
|
3
|
+
export declare function readDeployment(path: string, platform?: PlatformName): Promise<{
|
|
4
|
+
config: DeploymentConfig;
|
|
5
|
+
businesses: Record<string, {
|
|
6
|
+
settings: OriginSettings;
|
|
7
|
+
originFile: string;
|
|
8
|
+
businessFile: string;
|
|
9
|
+
}>;
|
|
10
|
+
originRoot: string;
|
|
11
|
+
platformRoot: string;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function readBusinessFunction(path: string): Promise<BusinessFunction>;
|
|
14
|
+
export { readCredentials, readData } from "./config.js";
|
|
15
|
+
export { readResponsePages } from "./responses.js";
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { realpath } from "node:fs/promises";
|
|
2
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { validateDeploymentConfig } from "../config.js";
|
|
5
|
+
import { readData } from "./config.js";
|
|
6
|
+
export async function readDeployment(path, platform) {
|
|
7
|
+
const directory = dirname(resolve(path));
|
|
8
|
+
const input = (await readData(path));
|
|
9
|
+
platform ??= input.platform;
|
|
10
|
+
if (!["cloudflare", "vercel", "netlify", "esa", "edgeone"].includes(platform))
|
|
11
|
+
throw new Error("Select a cloud platform for the business origins");
|
|
12
|
+
if (typeof input.originDirectory !== "string" || Object.hasOwn(input, "origins"))
|
|
13
|
+
throw new Error("Business origins must be configured in origin/<platform>/<business>/config.yml");
|
|
14
|
+
if (!Array.isArray(input.routes) || !input.routes.length)
|
|
15
|
+
throw new Error("At least one business route is required");
|
|
16
|
+
const names = [
|
|
17
|
+
...new Set(input.routes.map((route) => {
|
|
18
|
+
if (!route ||
|
|
19
|
+
typeof route.business !== "string" ||
|
|
20
|
+
!/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(route.business))
|
|
21
|
+
throw new Error("Every route must select a valid business name");
|
|
22
|
+
return route.business;
|
|
23
|
+
})),
|
|
24
|
+
];
|
|
25
|
+
const originRoot = await realpath(resolve(directory, input.originDirectory));
|
|
26
|
+
const platformRoot = await realpath(resolve(originRoot, platform));
|
|
27
|
+
if (relative(originRoot, platformRoot) !== platform)
|
|
28
|
+
throw new Error("Cross-platform origin path is forbidden");
|
|
29
|
+
const businesses = Object.create(null);
|
|
30
|
+
const origins = Object.create(null);
|
|
31
|
+
for (const name of names) {
|
|
32
|
+
const businessRoot = await realpath(resolve(platformRoot, name));
|
|
33
|
+
if (relative(platformRoot, businessRoot) !== name)
|
|
34
|
+
throw new Error("Business directory must remain within its platform");
|
|
35
|
+
const originFile = await realpath(resolve(businessRoot, "config.yml"));
|
|
36
|
+
if (dirname(originFile) !== businessRoot)
|
|
37
|
+
throw new Error("Origin config must remain in its business directory");
|
|
38
|
+
const settings = (await readData(originFile, 65536));
|
|
39
|
+
if (!settings ||
|
|
40
|
+
settings.platform !== platform ||
|
|
41
|
+
!["function", "relay", "proxy"].includes(settings.mode))
|
|
42
|
+
throw new Error("Origin platform or mode mismatch");
|
|
43
|
+
const keys = settings.mode === "proxy"
|
|
44
|
+
? ["platform", "mode", "url"]
|
|
45
|
+
: settings.mode === "relay"
|
|
46
|
+
? ["platform", "mode", "endpoint", "publicKeyFile", "authenticationKeyFile"]
|
|
47
|
+
: ["platform", "mode"];
|
|
48
|
+
if (Object.keys(settings).some((key) => !keys.includes(key)))
|
|
49
|
+
throw new Error("Unknown business origin option");
|
|
50
|
+
let origin = `https://${name.toLowerCase()}.origin.internal`;
|
|
51
|
+
let businessFile = resolve(businessRoot, "index.js");
|
|
52
|
+
if (settings.mode === "function") {
|
|
53
|
+
businessFile = await realpath(businessFile);
|
|
54
|
+
if (dirname(businessFile) !== businessRoot)
|
|
55
|
+
throw new Error("Business entry must remain in its business directory");
|
|
56
|
+
}
|
|
57
|
+
if (settings.mode === "proxy") {
|
|
58
|
+
const url = new URL(settings.url);
|
|
59
|
+
if (url.protocol !== "https:" ||
|
|
60
|
+
url.username ||
|
|
61
|
+
url.password ||
|
|
62
|
+
url.pathname !== "/" ||
|
|
63
|
+
url.search ||
|
|
64
|
+
url.hash)
|
|
65
|
+
throw new Error("Proxy origin must be an exact HTTPS origin (IP, hostname or CNAME)");
|
|
66
|
+
origin = url.origin;
|
|
67
|
+
}
|
|
68
|
+
if (settings.mode === "relay") {
|
|
69
|
+
const endpoint = new URL(settings.endpoint);
|
|
70
|
+
if (endpoint.protocol !== "https:" || endpoint.username || endpoint.password || endpoint.hash)
|
|
71
|
+
throw new Error("Relay endpoint must use HTTPS without credentials or fragments");
|
|
72
|
+
const secretRoot = await realpath(resolve(directory, "secret"));
|
|
73
|
+
for (const field of ["publicKeyFile", "authenticationKeyFile"]) {
|
|
74
|
+
if (typeof settings[field] !== "string")
|
|
75
|
+
throw new Error("Relay key file is required");
|
|
76
|
+
const file = await realpath(resolve(businessRoot, settings[field]));
|
|
77
|
+
const rel = relative(secretRoot, file);
|
|
78
|
+
if (rel.startsWith("..") || isAbsolute(rel))
|
|
79
|
+
throw new Error("Relay keys must reside in the project secret directory");
|
|
80
|
+
settings[field] = file;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
businesses[name] = { settings, originFile, businessFile };
|
|
84
|
+
origins[name] = { origin, allowedOrigins: [origin] };
|
|
85
|
+
}
|
|
86
|
+
const config = validateDeploymentConfig({ ...input, platform, origins });
|
|
87
|
+
config.secLangFile = resolve(directory, config.secLangFile);
|
|
88
|
+
if (config.localRulesFile)
|
|
89
|
+
config.localRulesFile = resolve(directory, config.localRulesFile);
|
|
90
|
+
if (config.setupRulesFile)
|
|
91
|
+
config.setupRulesFile = resolve(directory, config.setupRulesFile);
|
|
92
|
+
config.secretFile = resolve(directory, config.secretFile);
|
|
93
|
+
if (config.allowlistFile)
|
|
94
|
+
config.allowlistFile = resolve(directory, config.allowlistFile);
|
|
95
|
+
if (config.responses)
|
|
96
|
+
config.responses.directory = resolve(directory, config.responses.directory);
|
|
97
|
+
return { config, businesses, originRoot, platformRoot };
|
|
98
|
+
}
|
|
99
|
+
export async function readBusinessFunction(path) {
|
|
100
|
+
const module = await import(pathToFileURL(path).href);
|
|
101
|
+
const business = module.default;
|
|
102
|
+
if (!business || typeof business.fetch !== "function")
|
|
103
|
+
throw new Error("Business origin must export a fetch function");
|
|
104
|
+
return business;
|
|
105
|
+
}
|
|
106
|
+
export { readCredentials, readData } from "./config.js";
|
|
107
|
+
export { readResponsePages } from "./responses.js";
|
|
108
|
+
//# sourceMappingURL=deployment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployment.js","sourceRoot":"","sources":["../../../../scr/cli/deployment.ts"],"names":[],"mappings":"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"}
|