@zintrust/core 0.9.5 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.js +3 -3
- package/src/proxy/CloudflareProxyShared.d.ts +4 -17
- package/src/proxy/CloudflareProxyShared.d.ts.map +1 -1
- package/src/proxy/CloudflareProxyShared.js +2 -38
- package/src/proxy/WorkerSigning.d.ts +33 -0
- package/src/proxy/WorkerSigning.d.ts.map +1 -0
- package/src/proxy/WorkerSigning.js +77 -0
- package/src/proxy.d.ts +1 -2
- package/src/proxy.d.ts.map +1 -1
- package/src/proxy.js +1 -2
- package/src/runtime/WorkerAdapterImports.d.ts +2 -2
- package/src/runtime/WorkerAdapterImports.js +1 -1
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @zintrust/core v0.9.
|
|
2
|
+
* @zintrust/core v0.9.6
|
|
3
3
|
*
|
|
4
4
|
* ZinTrust Framework - Production-Grade TypeScript Backend
|
|
5
5
|
* Built for performance, type safety, and exceptional developer experience
|
|
6
6
|
*
|
|
7
7
|
* Build Information:
|
|
8
|
-
* Built: 2026-04-23T13:
|
|
8
|
+
* Built: 2026-04-23T13:47:55.338Z
|
|
9
9
|
* Node: >=20.0.0
|
|
10
10
|
* License: MIT
|
|
11
11
|
*
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Available at runtime for debugging and health checks
|
|
22
22
|
*/
|
|
23
23
|
export const ZINTRUST_VERSION = '0.1.41';
|
|
24
|
-
export const ZINTRUST_BUILD_DATE = '2026-04-23T13:
|
|
24
|
+
export const ZINTRUST_BUILD_DATE = '2026-04-23T13:47:55.277Z'; // Replaced during build
|
|
25
25
|
export { Application } from './boot/Application.js';
|
|
26
26
|
export { AwsSigV4 } from './common/index.js';
|
|
27
27
|
export { SignedRequest } from './security/SignedRequest.js';
|
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export type ProxyNonceNamespace = {
|
|
5
|
-
get: (key: string) => Promise<string | null>;
|
|
6
|
-
put: (key: string, value: string, options?: ProxyNoncePutOptions) => Promise<void>;
|
|
7
|
-
};
|
|
8
|
-
type SignedRequestOptions = Readonly<{
|
|
9
|
-
secretEnvVar: string;
|
|
10
|
-
missingSecretStatus: number;
|
|
11
|
-
missingSecretMessage: string;
|
|
12
|
-
defaultSigningWindowMs: number;
|
|
13
|
-
}>;
|
|
14
|
-
type ReadAndVerifyJsonOptions = SignedRequestOptions & Readonly<{
|
|
1
|
+
import type { WorkerSigningOptions } from './WorkerSigning';
|
|
2
|
+
export type { ProxyNonceNamespace, ProxyNoncePutOptions, WorkerSigningOptions, } from './WorkerSigning';
|
|
3
|
+
type ReadAndVerifyJsonOptions = WorkerSigningOptions & Readonly<{
|
|
15
4
|
defaultMaxBodyBytes: number;
|
|
16
5
|
}>;
|
|
17
6
|
type ProxyRequestEnv = object;
|
|
@@ -34,8 +23,7 @@ export declare const parseOptionalJson: (text: string) => {
|
|
|
34
23
|
ok: false;
|
|
35
24
|
response: Response;
|
|
36
25
|
};
|
|
37
|
-
export declare const
|
|
38
|
-
export declare const verifySignedRequest: (request: Request, env: ProxyRequestEnv, bodyBytes: Uint8Array, options: SignedRequestOptions) => Promise<Response | {
|
|
26
|
+
export declare const verifySignedRequest: (request: Request, env: ProxyRequestEnv, bodyBytes: Uint8Array, options: WorkerSigningOptions) => Promise<Response | {
|
|
39
27
|
ok: true;
|
|
40
28
|
}>;
|
|
41
29
|
export declare const readAndVerifyJson: (request: Request, env: ProxyRequestEnv, options: ReadAndVerifyJsonOptions) => Promise<{
|
|
@@ -46,5 +34,4 @@ export declare const readAndVerifyJson: (request: Request, env: ProxyRequestEnv,
|
|
|
46
34
|
ok: false;
|
|
47
35
|
response: Response;
|
|
48
36
|
}>;
|
|
49
|
-
export {};
|
|
50
37
|
//# sourceMappingURL=CloudflareProxyShared.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CloudflareProxyShared.d.ts","sourceRoot":"","sources":["../../../src/proxy/CloudflareProxyShared.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CloudflareProxyShared.d.ts","sourceRoot":"","sources":["../../../src/proxy/CloudflareProxyShared.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAGjE,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAE9B,KAAK,wBAAwB,GAAG,oBAAoB,GAClD,QAAQ,CAAC;IACP,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CAAC,CAAC;AAEL,KAAK,eAAe,GAAG,MAAM,CAAC;AAE9B,eAAO,MAAM,IAAI,GAAI,QAAQ,MAAM,EAAE,MAAM,OAAO,KAAG,QAQpD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,QAG/E,CAAC;AAMF,eAAO,MAAM,SAAS,GAAI,KAAK,eAAe,EAAE,MAAM,MAAM,EAAE,UAAU,MAAM,KAAG,MAKhF,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,IAI9D,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,SAAS,OAAO,EAChB,UAAU,MAAM,KACf,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAY3F,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,MAAM,MAAM,KACX;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAezF,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,OAAO,EAChB,KAAK,eAAe,EACpB,WAAW,UAAU,EACrB,SAAS,oBAAoB,KAC5B,OAAO,CAAC,QAAQ,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,CAQjC,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,OAAO,EAChB,KAAK,eAAe,EACpB,SAAS,wBAAwB,KAChC,OAAO,CACN;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,UAAU,CAAA;CAAE,GAC5E;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAapC,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isString } from '../helper/index.js';
|
|
2
2
|
import { ErrorHandler } from './ErrorHandler.js';
|
|
3
3
|
import { RequestValidator } from './RequestValidator.js';
|
|
4
|
-
import {
|
|
4
|
+
import { WorkerSigning } from './WorkerSigning.js';
|
|
5
5
|
export const json = (status, body) => {
|
|
6
6
|
return new Response(JSON.stringify(body), {
|
|
7
7
|
status,
|
|
@@ -59,44 +59,8 @@ export const parseOptionalJson = (text) => {
|
|
|
59
59
|
}
|
|
60
60
|
return { ok: true, payload: parsed.value };
|
|
61
61
|
};
|
|
62
|
-
const loadSigningSecret = (env, secretEnvVar) => {
|
|
63
|
-
const directValue = getEnvValue(env, secretEnvVar);
|
|
64
|
-
const direct = isString(directValue) ? directValue.trim() : '';
|
|
65
|
-
if (direct !== '')
|
|
66
|
-
return direct;
|
|
67
|
-
const fallbackValue = getEnvValue(env, 'APP_KEY');
|
|
68
|
-
const fallback = isString(fallbackValue) ? fallbackValue.trim() : '';
|
|
69
|
-
if (fallback !== '')
|
|
70
|
-
return fallback;
|
|
71
|
-
return null;
|
|
72
|
-
};
|
|
73
|
-
export const verifyNonceKv = async (kv, keyId, nonce, ttlMs) => {
|
|
74
|
-
const ttlSeconds = Math.max(1, Math.ceil(ttlMs / 1000));
|
|
75
|
-
const storageKey = `nonce:${keyId}:${nonce}`;
|
|
76
|
-
const existing = await kv.get(storageKey);
|
|
77
|
-
if (existing !== null)
|
|
78
|
-
return false;
|
|
79
|
-
await kv.put(storageKey, '1', { expirationTtl: ttlSeconds });
|
|
80
|
-
return true;
|
|
81
|
-
};
|
|
82
62
|
export const verifySignedRequest = async (request, env, bodyBytes, options) => {
|
|
83
|
-
const
|
|
84
|
-
if (secret === null) {
|
|
85
|
-
return toErrorResponse(options.missingSecretStatus, 'CONFIG_ERROR', options.missingSecretMessage);
|
|
86
|
-
}
|
|
87
|
-
const windowMs = getEnvInt(env, 'ZT_PROXY_SIGNING_WINDOW_MS', options.defaultSigningWindowMs);
|
|
88
|
-
const nonceStore = getEnvValue(env, 'ZT_NONCES');
|
|
89
|
-
const verifyResult = await SigningService.verifyWithKeyProvider({
|
|
90
|
-
method: request.method,
|
|
91
|
-
url: request.url,
|
|
92
|
-
body: bodyBytes,
|
|
93
|
-
headers: request.headers,
|
|
94
|
-
windowMs,
|
|
95
|
-
getSecretForKeyId: (_keyId) => secret,
|
|
96
|
-
verifyNonce: nonceStore === undefined || nonceStore === null
|
|
97
|
-
? undefined
|
|
98
|
-
: async (keyId, nonce, ttlMs) => verifyNonceKv(nonceStore, keyId, nonce, ttlMs),
|
|
99
|
-
});
|
|
63
|
+
const verifyResult = await WorkerSigning.verifySignedRequest(request, env, bodyBytes, options);
|
|
100
64
|
if (!verifyResult.ok) {
|
|
101
65
|
return toErrorResponse(verifyResult.status, verifyResult.code, verifyResult.message);
|
|
102
66
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type ProxyNoncePutOptions = {
|
|
2
|
+
expirationTtl?: number;
|
|
3
|
+
};
|
|
4
|
+
export type ProxyNonceNamespace = {
|
|
5
|
+
get: (key: string) => Promise<string | null>;
|
|
6
|
+
put: (key: string, value: string, options?: ProxyNoncePutOptions) => Promise<void>;
|
|
7
|
+
};
|
|
8
|
+
export type WorkerSigningOptions = Readonly<{
|
|
9
|
+
secretEnvVar: string;
|
|
10
|
+
missingSecretStatus: number;
|
|
11
|
+
missingSecretMessage: string;
|
|
12
|
+
defaultSigningWindowMs: number;
|
|
13
|
+
}>;
|
|
14
|
+
type WorkerRequestEnv = object;
|
|
15
|
+
type WorkerSigningResult = {
|
|
16
|
+
ok: true;
|
|
17
|
+
} | {
|
|
18
|
+
ok: false;
|
|
19
|
+
status: number;
|
|
20
|
+
code: string;
|
|
21
|
+
message: string;
|
|
22
|
+
};
|
|
23
|
+
export declare const WorkerSigning: Readonly<{
|
|
24
|
+
verifyNonceKv: (kv: ProxyNonceNamespace, keyId: string, nonce: string, ttlMs: number) => Promise<boolean>;
|
|
25
|
+
verifySignedRequest: (request: Request, env: WorkerRequestEnv, bodyBytes: Uint8Array, options: WorkerSigningOptions) => Promise<WorkerSigningResult | {
|
|
26
|
+
ok: false;
|
|
27
|
+
status: number;
|
|
28
|
+
code: "CONFIG_ERROR";
|
|
29
|
+
message: string;
|
|
30
|
+
}>;
|
|
31
|
+
}>;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=WorkerSigning.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WorkerSigning.d.ts","sourceRoot":"","sources":["../../../src/proxy/WorkerSigning.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,oBAAoB,GAAG;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACpF,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sBAAsB,EAAE,MAAM,CAAC;CAChC,CAAC,CAAC;AAEH,KAAK,gBAAgB,GAAG,MAAM,CAAC;AAE/B,KAAK,mBAAmB,GACpB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAkGjE,eAAO,MAAM,aAAa;wBAlDpB,mBAAmB,SAChB,MAAM,SACN,MAAM,SACN,MAAM,KACZ,OAAO,CAAC,OAAO,CAAC;mCAUR,OAAO,OACX,gBAAgB,aACV,UAAU,WACZ,oBAAoB,KAC5B,OAAO,CACR,mBAAmB,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,cAAc,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAC3F;EAiCC,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { isString } from '../helper/index.js';
|
|
2
|
+
import { SignedRequest } from '../security/SignedRequest.js';
|
|
3
|
+
const getEnvValue = (env, name) => {
|
|
4
|
+
return env[name];
|
|
5
|
+
};
|
|
6
|
+
const getEnvInt = (env, name, fallback) => {
|
|
7
|
+
const raw = getEnvValue(env, name);
|
|
8
|
+
if (!isString(raw))
|
|
9
|
+
return fallback;
|
|
10
|
+
const parsed = Number.parseInt(raw, 10);
|
|
11
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
12
|
+
};
|
|
13
|
+
const loadSigningSecret = (env, secretEnvVar) => {
|
|
14
|
+
const directValue = getEnvValue(env, secretEnvVar);
|
|
15
|
+
const direct = isString(directValue) ? directValue.trim() : '';
|
|
16
|
+
if (direct !== '')
|
|
17
|
+
return direct;
|
|
18
|
+
const fallbackValue = getEnvValue(env, 'APP_KEY');
|
|
19
|
+
const fallback = isString(fallbackValue) ? fallbackValue.trim() : '';
|
|
20
|
+
if (fallback !== '')
|
|
21
|
+
return fallback;
|
|
22
|
+
return null;
|
|
23
|
+
};
|
|
24
|
+
const mapVerifyResult = (result) => {
|
|
25
|
+
if (result.ok)
|
|
26
|
+
return { ok: true };
|
|
27
|
+
if (result.code === 'MISSING_HEADER' || result.code === 'INVALID_TIMESTAMP') {
|
|
28
|
+
return { ok: false, status: 401, code: result.code, message: result.message };
|
|
29
|
+
}
|
|
30
|
+
if (result.code === 'EXPIRED') {
|
|
31
|
+
return { ok: false, status: 401, code: result.code, message: result.message };
|
|
32
|
+
}
|
|
33
|
+
if (result.code === 'UNKNOWN_KEY') {
|
|
34
|
+
return { ok: false, status: 403, code: result.code, message: result.message };
|
|
35
|
+
}
|
|
36
|
+
if (result.code === 'REPLAYED') {
|
|
37
|
+
return { ok: false, status: 409, code: result.code, message: result.message };
|
|
38
|
+
}
|
|
39
|
+
return { ok: false, status: 403, code: result.code, message: result.message };
|
|
40
|
+
};
|
|
41
|
+
const verifyNonceKv = async (kv, keyId, nonce, ttlMs) => {
|
|
42
|
+
const ttlSeconds = Math.max(1, Math.ceil(ttlMs / 1000));
|
|
43
|
+
const storageKey = `nonce:${keyId}:${nonce}`;
|
|
44
|
+
const existing = await kv.get(storageKey);
|
|
45
|
+
if (existing !== null)
|
|
46
|
+
return false;
|
|
47
|
+
await kv.put(storageKey, '1', { expirationTtl: ttlSeconds });
|
|
48
|
+
return true;
|
|
49
|
+
};
|
|
50
|
+
const verifySignedRequest = async (request, env, bodyBytes, options) => {
|
|
51
|
+
const secret = loadSigningSecret(env, options.secretEnvVar);
|
|
52
|
+
if (secret === null) {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
status: options.missingSecretStatus,
|
|
56
|
+
code: 'CONFIG_ERROR',
|
|
57
|
+
message: options.missingSecretMessage,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const windowMs = getEnvInt(env, 'ZT_PROXY_SIGNING_WINDOW_MS', options.defaultSigningWindowMs);
|
|
61
|
+
const nonceStore = getEnvValue(env, 'ZT_NONCES');
|
|
62
|
+
return mapVerifyResult(await SignedRequest.verify({
|
|
63
|
+
method: request.method,
|
|
64
|
+
url: request.url,
|
|
65
|
+
body: bodyBytes,
|
|
66
|
+
headers: request.headers,
|
|
67
|
+
windowMs,
|
|
68
|
+
getSecretForKeyId: (_keyId) => secret,
|
|
69
|
+
verifyNonce: nonceStore === undefined || nonceStore === null
|
|
70
|
+
? undefined
|
|
71
|
+
: async (keyId, nonce, ttlMs) => verifyNonceKv(nonceStore, keyId, nonce, ttlMs),
|
|
72
|
+
}));
|
|
73
|
+
};
|
|
74
|
+
export const WorkerSigning = Object.freeze({
|
|
75
|
+
verifyNonceKv,
|
|
76
|
+
verifySignedRequest,
|
|
77
|
+
});
|
package/src/proxy.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
export { RemoteSignedJson } from './common/RemoteSignedJson';
|
|
2
1
|
export { ZintrustD1Proxy } from './proxy/d1/ZintrustD1Proxy';
|
|
3
2
|
export { ErrorHandler } from './proxy/ErrorHandler';
|
|
4
3
|
export { ZintrustKvProxy } from './proxy/kv/ZintrustKvProxy';
|
|
5
4
|
export { RequestValidator } from './proxy/RequestValidator';
|
|
6
|
-
export {
|
|
5
|
+
export { WorkerSigning } from './proxy/WorkerSigning';
|
|
7
6
|
//# sourceMappingURL=proxy.d.ts.map
|
package/src/proxy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
|
package/src/proxy.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export { RemoteSignedJson } from './common/RemoteSignedJson.js';
|
|
2
1
|
export { ZintrustD1Proxy } from './proxy/d1/ZintrustD1Proxy.js';
|
|
3
2
|
export { ErrorHandler } from './proxy/ErrorHandler.js';
|
|
4
3
|
export { ZintrustKvProxy } from './proxy/kv/ZintrustKvProxy.js';
|
|
5
4
|
export { RequestValidator } from './proxy/RequestValidator.js';
|
|
6
|
-
export {
|
|
5
|
+
export { WorkerSigning } from './proxy/WorkerSigning.js';
|
|
@@ -22,7 +22,7 @@ const tryImportOptional = async () => {
|
|
|
22
22
|
await tryImportProjectRuntime();
|
|
23
23
|
await import('./WorkerProjectPlugins.js');
|
|
24
24
|
};
|
|
25
|
-
const ready = tryImportOptional();
|
|
25
|
+
const ready = await tryImportOptional();
|
|
26
26
|
export const WorkerAdapterImports = Object.freeze({
|
|
27
27
|
loaded: true,
|
|
28
28
|
ready,
|