@doclo/core 0.1.5
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 +21 -0
- package/README.md +34 -0
- package/dist/index.d.ts +931 -0
- package/dist/index.js +2293 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/validation-utils.d.ts +1 -0
- package/dist/internal/validation-utils.js +650 -0
- package/dist/internal/validation-utils.js.map +1 -0
- package/dist/observability/index.d.ts +933 -0
- package/dist/observability/index.js +630 -0
- package/dist/observability/index.js.map +1 -0
- package/dist/pdf-utils.d.ts +123 -0
- package/dist/pdf-utils.js +106 -0
- package/dist/pdf-utils.js.map +1 -0
- package/dist/runtime/base64.d.ts +100 -0
- package/dist/runtime/base64.js +52 -0
- package/dist/runtime/base64.js.map +1 -0
- package/dist/runtime/crypto.d.ts +56 -0
- package/dist/runtime/crypto.js +35 -0
- package/dist/runtime/crypto.js.map +1 -0
- package/dist/runtime/env.d.ts +130 -0
- package/dist/runtime/env.js +76 -0
- package/dist/runtime/env.js.map +1 -0
- package/dist/security/index.d.ts +236 -0
- package/dist/security/index.js +260 -0
- package/dist/security/index.js.map +1 -0
- package/dist/validation-CzOz6fwq.d.ts +1126 -0
- package/dist/validation.d.ts +1 -0
- package/dist/validation.js +445 -0
- package/dist/validation.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/runtime/crypto.ts"],"sourcesContent":["/**\n * Universal Crypto Adapter\n *\n * Provides crypto-secure random byte generation for both Node.js and Edge Runtime.\n * Uses Web Crypto API (available in both environments) for maximum compatibility.\n *\n * @module @doclo/core/runtime/crypto\n */\n\n/**\n * Generate crypto-secure random bytes\n *\n * Uses Web Crypto API which is available in:\n * - Node.js 18+ (globalThis.crypto)\n * - Browsers (window.crypto)\n * - Cloudflare Workers (crypto)\n * - Vercel Edge Functions (crypto)\n *\n * @param length - Number of random bytes to generate\n * @returns Uint8Array containing random bytes\n */\nexport function getRandomBytes(length: number): Uint8Array {\n const bytes = new Uint8Array(length);\n\n // Use Web Crypto API (available in all supported runtimes)\n // Node.js 18+, Edge Runtime, and browsers all support globalThis.crypto\n if (typeof globalThis !== 'undefined' && globalThis.crypto?.getRandomValues) {\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n }\n\n throw new Error(\n 'Web Crypto API not available. This SDK requires:\\n' +\n '- Node.js 18.0.0 or later (has globalThis.crypto)\\n' +\n '- Edge Runtime (Vercel, Cloudflare)\\n' +\n '- Modern browsers'\n );\n}\n\n/**\n * Convert Uint8Array to lowercase hex string\n *\n * @param bytes - Byte array to convert\n * @returns Hex string representation\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Generate random hex string of specified byte length\n *\n * @param byteLength - Number of random bytes (hex string will be 2x this length)\n * @returns Lowercase hex string\n *\n * @example\n * ```typescript\n * const traceId = randomHex(16); // 32 hex characters\n * const spanId = randomHex(8); // 16 hex characters\n * ```\n */\nexport function randomHex(byteLength: number): string {\n const bytes = getRandomBytes(byteLength);\n return bytesToHex(bytes);\n}\n\n/**\n * Generate a UUID v4 string\n *\n * Format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\n *\n * @returns UUID v4 string\n *\n * @example\n * ```typescript\n * const id = randomUUID(); // \"550e8400-e29b-41d4-a716-446655440000\"\n * ```\n */\nexport function randomUUID(): string {\n // Try native crypto.randomUUID() first (Node 19+, all Edge runtimes)\n if (typeof globalThis !== 'undefined' && globalThis.crypto?.randomUUID) {\n return globalThis.crypto.randomUUID();\n }\n\n // Fallback: Manual UUID v4 generation\n const bytes = getRandomBytes(16);\n\n // Set version (4) and variant bits for UUID v4\n bytes[6] = (bytes[6] & 0x0f) | 0x40; // Version 4\n bytes[8] = (bytes[8] & 0x3f) | 0x80; // Variant 10\n\n // Format as UUID string\n const hex = bytesToHex(bytes);\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;\n}\n"],"mappings":";AAqBO,SAAS,eAAe,QAA4B;AACzD,QAAM,QAAQ,IAAI,WAAW,MAAM;AAInC,MAAI,OAAO,eAAe,eAAe,WAAW,QAAQ,iBAAiB;AAC3E,eAAW,OAAO,gBAAgB,KAAK;AACvC,WAAO;AAAA,EACT;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EAIF;AACF;AAQO,SAAS,WAAW,OAA2B;AACpD,SAAO,MAAM,KAAK,KAAK,EACpB,IAAI,OAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EACxC,KAAK,EAAE;AACZ;AAcO,SAAS,UAAU,YAA4B;AACpD,QAAM,QAAQ,eAAe,UAAU;AACvC,SAAO,WAAW,KAAK;AACzB;AAcO,SAAS,aAAqB;AAEnC,MAAI,OAAO,eAAe,eAAe,WAAW,QAAQ,YAAY;AACtE,WAAO,WAAW,OAAO,WAAW;AAAA,EACtC;AAGA,QAAM,QAAQ,eAAe,EAAE;AAG/B,QAAM,CAAC,IAAK,MAAM,CAAC,IAAI,KAAQ;AAC/B,QAAM,CAAC,IAAK,MAAM,CAAC,IAAI,KAAQ;AAG/B,QAAM,MAAM,WAAW,KAAK;AAC5B,SAAO,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC;AAC9G;","names":[]}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal Environment Configuration
|
|
3
|
+
*
|
|
4
|
+
* Provides environment variable access for both Node.js and Edge Runtime.
|
|
5
|
+
* Supports multiple patterns:
|
|
6
|
+
* - Node.js: process.env
|
|
7
|
+
* - Vercel Edge Functions: process.env (available)
|
|
8
|
+
* - Cloudflare Workers: env bindings (passed as config)
|
|
9
|
+
*
|
|
10
|
+
* @module @doclo/core/runtime/env
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Runtime environment configuration
|
|
14
|
+
*
|
|
15
|
+
* Can be set explicitly or will fall back to process.env when available.
|
|
16
|
+
*/
|
|
17
|
+
interface RuntimeEnv {
|
|
18
|
+
/**
|
|
19
|
+
* Node environment (development, production, test)
|
|
20
|
+
*/
|
|
21
|
+
NODE_ENV?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Debug flag for validation messages
|
|
24
|
+
*/
|
|
25
|
+
DEBUG_VALIDATION?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Skip flow validation (use with caution!)
|
|
28
|
+
*/
|
|
29
|
+
DOCLO_SKIP_VALIDATION?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Endpoint for security event logging
|
|
32
|
+
*/
|
|
33
|
+
SECURITY_LOG_ENDPOINT?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Additional environment variables
|
|
36
|
+
*/
|
|
37
|
+
[key: string]: string | undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Set global runtime environment configuration
|
|
41
|
+
*
|
|
42
|
+
* Use this to configure environment variables in Edge Runtime environments
|
|
43
|
+
* where process.env is not available (like Cloudflare Workers).
|
|
44
|
+
*
|
|
45
|
+
* @param env - Environment configuration object
|
|
46
|
+
*/
|
|
47
|
+
declare function setRuntimeEnv(env: RuntimeEnv): void;
|
|
48
|
+
/**
|
|
49
|
+
* Get global runtime environment configuration
|
|
50
|
+
*
|
|
51
|
+
* @returns Current runtime environment config or null
|
|
52
|
+
*/
|
|
53
|
+
declare function getRuntimeEnv(): RuntimeEnv | null;
|
|
54
|
+
/**
|
|
55
|
+
* Clear global runtime environment configuration (for testing)
|
|
56
|
+
*/
|
|
57
|
+
declare function clearRuntimeEnv(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Get environment variable value
|
|
60
|
+
*
|
|
61
|
+
* Priority:
|
|
62
|
+
* 1. Explicitly set runtime env (via setRuntimeEnv)
|
|
63
|
+
* 2. process.env (Node.js, Vercel Edge)
|
|
64
|
+
* 3. undefined
|
|
65
|
+
*
|
|
66
|
+
* @param key - Environment variable name
|
|
67
|
+
* @param defaultValue - Optional default value if not found
|
|
68
|
+
* @returns Environment variable value or undefined
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const nodeEnv = getEnv('NODE_ENV', 'development');
|
|
73
|
+
* const isProduction = nodeEnv === 'production';
|
|
74
|
+
*
|
|
75
|
+
* const skipValidation = getEnv('DOCLO_SKIP_VALIDATION') === 'true';
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
declare function getEnv(key: string, defaultValue?: string): string | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Check if running in production mode
|
|
81
|
+
*
|
|
82
|
+
* @returns True if NODE_ENV is 'production'
|
|
83
|
+
*/
|
|
84
|
+
declare function isProduction(): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Check if running in development mode
|
|
87
|
+
*
|
|
88
|
+
* @returns True if NODE_ENV is 'development'
|
|
89
|
+
*/
|
|
90
|
+
declare function isDevelopment(): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Check if running in test mode
|
|
93
|
+
*
|
|
94
|
+
* @returns True if NODE_ENV is 'test'
|
|
95
|
+
*/
|
|
96
|
+
declare function isTest(): boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Check if debug validation is enabled
|
|
99
|
+
*
|
|
100
|
+
* @returns True if DEBUG_VALIDATION is set to any truthy value
|
|
101
|
+
*/
|
|
102
|
+
declare function isDebugValidation(): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Check if validation should be skipped
|
|
105
|
+
*
|
|
106
|
+
* CAUTION: Only use in trusted environments!
|
|
107
|
+
*
|
|
108
|
+
* @returns True if DOCLO_SKIP_VALIDATION is 'true'
|
|
109
|
+
*/
|
|
110
|
+
declare function shouldSkipValidation(): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Detect current runtime environment
|
|
113
|
+
*
|
|
114
|
+
* @returns Runtime type: 'node', 'edge-vercel', 'workerd' (Cloudflare), 'browser', or 'unknown'
|
|
115
|
+
*/
|
|
116
|
+
declare function detectRuntime(): 'node' | 'edge-vercel' | 'workerd' | 'browser' | 'unknown';
|
|
117
|
+
/**
|
|
118
|
+
* Check if running in an Edge Runtime environment
|
|
119
|
+
*
|
|
120
|
+
* @returns True if running in Vercel Edge or Cloudflare Workers
|
|
121
|
+
*/
|
|
122
|
+
declare function isEdgeRuntime(): boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Check if running in Node.js
|
|
125
|
+
*
|
|
126
|
+
* @returns True if running in Node.js
|
|
127
|
+
*/
|
|
128
|
+
declare function isNodeRuntime(): boolean;
|
|
129
|
+
|
|
130
|
+
export { type RuntimeEnv, clearRuntimeEnv, detectRuntime, getEnv, getRuntimeEnv, isDebugValidation, isDevelopment, isEdgeRuntime, isNodeRuntime, isProduction, isTest, setRuntimeEnv, shouldSkipValidation };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// src/runtime/env.ts
|
|
2
|
+
var globalRuntimeEnv = null;
|
|
3
|
+
function setRuntimeEnv(env) {
|
|
4
|
+
globalRuntimeEnv = env;
|
|
5
|
+
}
|
|
6
|
+
function getRuntimeEnv() {
|
|
7
|
+
return globalRuntimeEnv;
|
|
8
|
+
}
|
|
9
|
+
function clearRuntimeEnv() {
|
|
10
|
+
globalRuntimeEnv = null;
|
|
11
|
+
}
|
|
12
|
+
function getEnv(key, defaultValue) {
|
|
13
|
+
if (globalRuntimeEnv && key in globalRuntimeEnv) {
|
|
14
|
+
return globalRuntimeEnv[key];
|
|
15
|
+
}
|
|
16
|
+
if (typeof process !== "undefined" && process.env) {
|
|
17
|
+
const value = process.env[key];
|
|
18
|
+
if (value !== void 0) {
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return defaultValue;
|
|
23
|
+
}
|
|
24
|
+
function isProduction() {
|
|
25
|
+
return getEnv("NODE_ENV") === "production";
|
|
26
|
+
}
|
|
27
|
+
function isDevelopment() {
|
|
28
|
+
return getEnv("NODE_ENV") === "development";
|
|
29
|
+
}
|
|
30
|
+
function isTest() {
|
|
31
|
+
return getEnv("NODE_ENV") === "test";
|
|
32
|
+
}
|
|
33
|
+
function isDebugValidation() {
|
|
34
|
+
const debug = getEnv("DEBUG_VALIDATION");
|
|
35
|
+
return Boolean(debug && debug !== "0" && debug !== "false");
|
|
36
|
+
}
|
|
37
|
+
function shouldSkipValidation() {
|
|
38
|
+
return getEnv("DOCLO_SKIP_VALIDATION") === "true";
|
|
39
|
+
}
|
|
40
|
+
function detectRuntime() {
|
|
41
|
+
if (typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers") {
|
|
42
|
+
return "workerd";
|
|
43
|
+
}
|
|
44
|
+
if (typeof globalThis !== "undefined" && "EdgeRuntime" in globalThis) {
|
|
45
|
+
return "edge-vercel";
|
|
46
|
+
}
|
|
47
|
+
if (typeof process !== "undefined" && process.versions?.node) {
|
|
48
|
+
return "node";
|
|
49
|
+
}
|
|
50
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
51
|
+
return "browser";
|
|
52
|
+
}
|
|
53
|
+
return "unknown";
|
|
54
|
+
}
|
|
55
|
+
function isEdgeRuntime() {
|
|
56
|
+
const runtime = detectRuntime();
|
|
57
|
+
return runtime === "edge-vercel" || runtime === "workerd";
|
|
58
|
+
}
|
|
59
|
+
function isNodeRuntime() {
|
|
60
|
+
return detectRuntime() === "node";
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
clearRuntimeEnv,
|
|
64
|
+
detectRuntime,
|
|
65
|
+
getEnv,
|
|
66
|
+
getRuntimeEnv,
|
|
67
|
+
isDebugValidation,
|
|
68
|
+
isDevelopment,
|
|
69
|
+
isEdgeRuntime,
|
|
70
|
+
isNodeRuntime,
|
|
71
|
+
isProduction,
|
|
72
|
+
isTest,
|
|
73
|
+
setRuntimeEnv,
|
|
74
|
+
shouldSkipValidation
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/runtime/env.ts"],"sourcesContent":["/**\n * Universal Environment Configuration\n *\n * Provides environment variable access for both Node.js and Edge Runtime.\n * Supports multiple patterns:\n * - Node.js: process.env\n * - Vercel Edge Functions: process.env (available)\n * - Cloudflare Workers: env bindings (passed as config)\n *\n * @module @doclo/core/runtime/env\n */\n\n/**\n * Runtime environment configuration\n *\n * Can be set explicitly or will fall back to process.env when available.\n */\nexport interface RuntimeEnv {\n /**\n * Node environment (development, production, test)\n */\n NODE_ENV?: string;\n\n /**\n * Debug flag for validation messages\n */\n DEBUG_VALIDATION?: string;\n\n /**\n * Skip flow validation (use with caution!)\n */\n DOCLO_SKIP_VALIDATION?: string;\n\n /**\n * Endpoint for security event logging\n */\n SECURITY_LOG_ENDPOINT?: string;\n\n /**\n * Additional environment variables\n */\n [key: string]: string | undefined;\n}\n\n/**\n * Global runtime environment configuration\n *\n * Set this at application startup to configure environment for Edge Runtime.\n * Falls back to process.env when not set.\n *\n * @example\n * ```typescript\n * // Cloudflare Workers\n * export default {\n * async fetch(request: Request, env: Env) {\n * setRuntimeEnv(env);\n * // ... use SDK\n * }\n * }\n *\n * // Vercel Edge Functions (process.env works, but can override)\n * setRuntimeEnv({ NODE_ENV: 'production' });\n * ```\n */\nlet globalRuntimeEnv: RuntimeEnv | null = null;\n\n/**\n * Set global runtime environment configuration\n *\n * Use this to configure environment variables in Edge Runtime environments\n * where process.env is not available (like Cloudflare Workers).\n *\n * @param env - Environment configuration object\n */\nexport function setRuntimeEnv(env: RuntimeEnv): void {\n globalRuntimeEnv = env;\n}\n\n/**\n * Get global runtime environment configuration\n *\n * @returns Current runtime environment config or null\n */\nexport function getRuntimeEnv(): RuntimeEnv | null {\n return globalRuntimeEnv;\n}\n\n/**\n * Clear global runtime environment configuration (for testing)\n */\nexport function clearRuntimeEnv(): void {\n globalRuntimeEnv = null;\n}\n\n/**\n * Get environment variable value\n *\n * Priority:\n * 1. Explicitly set runtime env (via setRuntimeEnv)\n * 2. process.env (Node.js, Vercel Edge)\n * 3. undefined\n *\n * @param key - Environment variable name\n * @param defaultValue - Optional default value if not found\n * @returns Environment variable value or undefined\n *\n * @example\n * ```typescript\n * const nodeEnv = getEnv('NODE_ENV', 'development');\n * const isProduction = nodeEnv === 'production';\n *\n * const skipValidation = getEnv('DOCLO_SKIP_VALIDATION') === 'true';\n * ```\n */\nexport function getEnv(key: string, defaultValue?: string): string | undefined {\n // 1. Check explicitly set runtime env\n if (globalRuntimeEnv && key in globalRuntimeEnv) {\n return globalRuntimeEnv[key];\n }\n\n // 2. Check process.env (Node.js, Vercel Edge)\n if (typeof process !== 'undefined' && process.env) {\n const value = process.env[key];\n if (value !== undefined) {\n return value;\n }\n }\n\n // 3. Return default value\n return defaultValue;\n}\n\n/**\n * Check if running in production mode\n *\n * @returns True if NODE_ENV is 'production'\n */\nexport function isProduction(): boolean {\n return getEnv('NODE_ENV') === 'production';\n}\n\n/**\n * Check if running in development mode\n *\n * @returns True if NODE_ENV is 'development'\n */\nexport function isDevelopment(): boolean {\n return getEnv('NODE_ENV') === 'development';\n}\n\n/**\n * Check if running in test mode\n *\n * @returns True if NODE_ENV is 'test'\n */\nexport function isTest(): boolean {\n return getEnv('NODE_ENV') === 'test';\n}\n\n/**\n * Check if debug validation is enabled\n *\n * @returns True if DEBUG_VALIDATION is set to any truthy value\n */\nexport function isDebugValidation(): boolean {\n const debug = getEnv('DEBUG_VALIDATION');\n return Boolean(debug && debug !== '0' && debug !== 'false');\n}\n\n/**\n * Check if validation should be skipped\n *\n * CAUTION: Only use in trusted environments!\n *\n * @returns True if DOCLO_SKIP_VALIDATION is 'true'\n */\nexport function shouldSkipValidation(): boolean {\n return getEnv('DOCLO_SKIP_VALIDATION') === 'true';\n}\n\n/**\n * Detect current runtime environment\n *\n * @returns Runtime type: 'node', 'edge-vercel', 'workerd' (Cloudflare), 'browser', or 'unknown'\n */\nexport function detectRuntime(): 'node' | 'edge-vercel' | 'workerd' | 'browser' | 'unknown' {\n // Check for Cloudflare Workers\n if (typeof navigator !== 'undefined' && navigator.userAgent === 'Cloudflare-Workers') {\n return 'workerd';\n }\n\n // Check for Vercel Edge Functions\n if (typeof globalThis !== 'undefined' && 'EdgeRuntime' in globalThis) {\n return 'edge-vercel';\n }\n\n // Check for Node.js\n if (typeof process !== 'undefined' && process.versions?.node) {\n return 'node';\n }\n\n // Check for browser\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n return 'browser';\n }\n\n return 'unknown';\n}\n\n/**\n * Check if running in an Edge Runtime environment\n *\n * @returns True if running in Vercel Edge or Cloudflare Workers\n */\nexport function isEdgeRuntime(): boolean {\n const runtime = detectRuntime();\n return runtime === 'edge-vercel' || runtime === 'workerd';\n}\n\n/**\n * Check if running in Node.js\n *\n * @returns True if running in Node.js\n */\nexport function isNodeRuntime(): boolean {\n return detectRuntime() === 'node';\n}\n"],"mappings":";AAgEA,IAAI,mBAAsC;AAUnC,SAAS,cAAc,KAAuB;AACnD,qBAAmB;AACrB;AAOO,SAAS,gBAAmC;AACjD,SAAO;AACT;AAKO,SAAS,kBAAwB;AACtC,qBAAmB;AACrB;AAsBO,SAAS,OAAO,KAAa,cAA2C;AAE7E,MAAI,oBAAoB,OAAO,kBAAkB;AAC/C,WAAO,iBAAiB,GAAG;AAAA,EAC7B;AAGA,MAAI,OAAO,YAAY,eAAe,QAAQ,KAAK;AACjD,UAAM,QAAQ,QAAQ,IAAI,GAAG;AAC7B,QAAI,UAAU,QAAW;AACvB,aAAO;AAAA,IACT;AAAA,EACF;AAGA,SAAO;AACT;AAOO,SAAS,eAAwB;AACtC,SAAO,OAAO,UAAU,MAAM;AAChC;AAOO,SAAS,gBAAyB;AACvC,SAAO,OAAO,UAAU,MAAM;AAChC;AAOO,SAAS,SAAkB;AAChC,SAAO,OAAO,UAAU,MAAM;AAChC;AAOO,SAAS,oBAA6B;AAC3C,QAAM,QAAQ,OAAO,kBAAkB;AACvC,SAAO,QAAQ,SAAS,UAAU,OAAO,UAAU,OAAO;AAC5D;AASO,SAAS,uBAAgC;AAC9C,SAAO,OAAO,uBAAuB,MAAM;AAC7C;AAOO,SAAS,gBAA4E;AAE1F,MAAI,OAAO,cAAc,eAAe,UAAU,cAAc,sBAAsB;AACpF,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,eAAe,eAAe,iBAAiB,YAAY;AACpE,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,YAAY,eAAe,QAAQ,UAAU,MAAM;AAC5D,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;AACpE,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAOO,SAAS,gBAAyB;AACvC,QAAM,UAAU,cAAc;AAC9B,SAAO,YAAY,iBAAiB,YAAY;AAClD;AAOO,SAAS,gBAAyB;AACvC,SAAO,cAAc,MAAM;AAC7B;","names":[]}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL Validation and SSRF Protection
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ SECURITY CRITICAL: SSRF (Server-Side Request Forgery) Prevention
|
|
5
|
+
*
|
|
6
|
+
* This module blocks URLs that could be used in Server-Side Request Forgery attacks:
|
|
7
|
+
* - Internal IP addresses (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
|
|
8
|
+
* - Loopback addresses (127.0.0.0/8, ::1)
|
|
9
|
+
* - Cloud metadata services (AWS, GCP, Aliyun, etc.)
|
|
10
|
+
* - Link-local addresses (169.254.0.0/16)
|
|
11
|
+
*
|
|
12
|
+
* Attack example: An attacker tricks your server to make requests to:
|
|
13
|
+
* - http://169.254.169.254 (AWS credentials endpoint)
|
|
14
|
+
* - http://10.0.0.1:8080/admin (internal service)
|
|
15
|
+
* - http://localhost/api/admin (localhost admin endpoint)
|
|
16
|
+
*
|
|
17
|
+
* Prevents Server-Side Request Forgery attacks by validating URLs against blocklist
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Validate a URL to prevent SSRF (Server-Side Request Forgery) attacks
|
|
21
|
+
*
|
|
22
|
+
* ⚠️ SECURITY CRITICAL: SSRF Prevention
|
|
23
|
+
*
|
|
24
|
+
* This function blocks URLs that could be used to exploit the server:
|
|
25
|
+
* - Internal IP addresses (breaks firewall perimeter security)
|
|
26
|
+
* - Cloud metadata services (leaks credentials, API keys)
|
|
27
|
+
* - Localhost/loopback (access admin services, debug ports)
|
|
28
|
+
*
|
|
29
|
+
* By default, blocks internal network access automatically.
|
|
30
|
+
*
|
|
31
|
+
* @param urlString - The URL to validate
|
|
32
|
+
* @param options - Validation options
|
|
33
|
+
* - blockInternal (default: true) - Block internal IP ranges. ⚠️ Set to false only if you understand SSRF risks
|
|
34
|
+
* - allowedProtocols (default: ['http:', 'https:']) - Restrict to specific protocols
|
|
35
|
+
* @throws Error if URL is invalid, uses blocked protocol, or points to blocked IP/host
|
|
36
|
+
* @returns The validated URL object
|
|
37
|
+
* @security Always validate user-provided URLs. Do not set blockInternal to false without SSRF security review
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // Validate user-provided URL
|
|
42
|
+
* try {
|
|
43
|
+
* const url = validateUrl(userInput);
|
|
44
|
+
* const response = await fetch(url.toString());
|
|
45
|
+
* } catch (error) {
|
|
46
|
+
* // URL was malicious or pointed to internal resource
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
declare function validateUrl(urlString: string, options?: {
|
|
51
|
+
blockInternal?: boolean;
|
|
52
|
+
allowedProtocols?: string[];
|
|
53
|
+
}): URL;
|
|
54
|
+
/**
|
|
55
|
+
* Fetch a URL with SSRF protection and timeout
|
|
56
|
+
* @param url - The URL to fetch
|
|
57
|
+
* @param timeoutMs - Request timeout in milliseconds (default 30s)
|
|
58
|
+
* @throws Error if URL is invalid, blocked, or request times out
|
|
59
|
+
*/
|
|
60
|
+
declare function secureFetch(url: string, timeoutMs?: number): Promise<Response>;
|
|
61
|
+
/**
|
|
62
|
+
* Extract hostname from URL string for validation
|
|
63
|
+
* Useful for pre-validation checks before full URL parsing
|
|
64
|
+
*/
|
|
65
|
+
declare function getHostnameFromUrl(urlString: string): string;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Path Validation and Path Traversal Prevention
|
|
69
|
+
* Prevents attackers from accessing arbitrary files through path traversal
|
|
70
|
+
*/
|
|
71
|
+
/**
|
|
72
|
+
* Validate and normalize a file path to prevent traversal attacks
|
|
73
|
+
* @param filePath - The file path to validate
|
|
74
|
+
* @param basePath - The base directory that paths must be within
|
|
75
|
+
* @throws Error if path attempts to traverse outside basePath
|
|
76
|
+
* @returns The normalized safe path
|
|
77
|
+
*/
|
|
78
|
+
declare function validatePath(filePath: string, basePath: string): string;
|
|
79
|
+
/**
|
|
80
|
+
* Validate a path without a required base path (more permissive)
|
|
81
|
+
* Still prevents obvious traversal attempts
|
|
82
|
+
* @param filePath - The file path to validate
|
|
83
|
+
* @throws Error if path contains suspicious traversal patterns
|
|
84
|
+
* @returns The normalized path
|
|
85
|
+
*/
|
|
86
|
+
declare function validatePathSimple(filePath: string): string;
|
|
87
|
+
/**
|
|
88
|
+
* Check if a path would be safe to access
|
|
89
|
+
* @param filePath - The file path to check
|
|
90
|
+
* @param basePath - Optional base path restriction
|
|
91
|
+
* @returns true if path is safe, false otherwise
|
|
92
|
+
*/
|
|
93
|
+
declare function isPathSafe(filePath: string, basePath?: string): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Get the absolute path with validation
|
|
96
|
+
* @param filePath - The file path
|
|
97
|
+
* @param basePath - Optional base directory
|
|
98
|
+
* @returns Absolute path if valid
|
|
99
|
+
* @throws Error if path is invalid
|
|
100
|
+
*/
|
|
101
|
+
declare function getSafePath(filePath: string, basePath?: string): string;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Resource Limits and DoS Protection
|
|
105
|
+
*
|
|
106
|
+
* ⚠️ SECURITY WARNING: Resource limits are critical for protecting against:
|
|
107
|
+
* - Resource exhaustion attacks (large file downloads)
|
|
108
|
+
* - Denial of Service (slow loris, timeout attacks)
|
|
109
|
+
* - Memory exhaustion (deeply nested JSON, large arrays)
|
|
110
|
+
*
|
|
111
|
+
* Prevents resource exhaustion attacks through file size limits and timeouts
|
|
112
|
+
*/
|
|
113
|
+
/**
|
|
114
|
+
* Default resource limits
|
|
115
|
+
*
|
|
116
|
+
* ⚠️ SECURITY CRITICAL: These are conservative defaults designed to prevent DoS attacks.
|
|
117
|
+
*
|
|
118
|
+
* - MAX_FILE_SIZE (100MB): Prevents downloading very large files that could exhaust memory or disk
|
|
119
|
+
* - REQUEST_TIMEOUT (30s): Prevents slow-loris attacks and hung connections
|
|
120
|
+
* - MAX_JSON_DEPTH (100): Prevents billion laughs attack (deeply nested JSON)
|
|
121
|
+
*
|
|
122
|
+
* Do not increase these limits without understanding the security implications:
|
|
123
|
+
* - Higher file size limit → Greater risk of resource exhaustion
|
|
124
|
+
* - Lower timeout → May reject legitimate slow requests
|
|
125
|
+
* - Lower JSON depth → May reject valid documents
|
|
126
|
+
*
|
|
127
|
+
* @security These limits can be overridden by SDK users, but doing so reduces security.
|
|
128
|
+
*/
|
|
129
|
+
declare const DEFAULT_LIMITS: {
|
|
130
|
+
MAX_FILE_SIZE: number;
|
|
131
|
+
REQUEST_TIMEOUT: number;
|
|
132
|
+
MAX_JSON_DEPTH: number;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Validate file size before processing
|
|
136
|
+
*
|
|
137
|
+
* ⚠️ SECURITY WARNING: File size validation prevents resource exhaustion.
|
|
138
|
+
* - Without limits: attackers can force downloads of multi-gigabyte files
|
|
139
|
+
* - Memory impact: files are loaded into memory for base64 encoding
|
|
140
|
+
* - Disk impact: temporary storage of downloaded files
|
|
141
|
+
*
|
|
142
|
+
* @param size - The file size in bytes
|
|
143
|
+
* @param maxSize - Maximum allowed size in bytes (default 100MB, can be customized)
|
|
144
|
+
* @throws Error if file exceeds size limit
|
|
145
|
+
* @security Do not disable this check without understanding resource implications
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* // Standard check with default limit (100MB)
|
|
150
|
+
* validateFileSize(fileSize);
|
|
151
|
+
*
|
|
152
|
+
* // Custom limit for use case that requires larger files
|
|
153
|
+
* validateFileSize(fileSize, 500 * 1024 * 1024); // 500MB
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
declare function validateFileSize(size: number, maxSize?: number): void;
|
|
157
|
+
/**
|
|
158
|
+
* Create a fetch controller with timeout
|
|
159
|
+
* @param timeoutMs - Timeout in milliseconds (default 30s)
|
|
160
|
+
* @returns AbortController with timeout configured
|
|
161
|
+
*/
|
|
162
|
+
declare function createFetchController(timeoutMs?: number): AbortController;
|
|
163
|
+
/**
|
|
164
|
+
* Clean up fetch controller timeout
|
|
165
|
+
* @param controller - The AbortController to clean up
|
|
166
|
+
*/
|
|
167
|
+
declare function cleanupFetchController(controller: AbortController): void;
|
|
168
|
+
/**
|
|
169
|
+
* Execute a fetch with automatic timeout and cleanup
|
|
170
|
+
*
|
|
171
|
+
* ⚠️ SECURITY WARNING: Timeout protection prevents slow-loris and other timing attacks.
|
|
172
|
+
* - Without timeout: requests can hang indefinitely, consuming server resources
|
|
173
|
+
* - Slow-loris attack: attacker sends requests slowly to exhaust connection pool
|
|
174
|
+
* - Zombie connections: closed clients but open server connections
|
|
175
|
+
*
|
|
176
|
+
* Default timeout (30s) balances security with legitimate use:
|
|
177
|
+
* - Too short: may reject slow networks or legitimate large downloads
|
|
178
|
+
* - Too long: keeps server resources tied up, enables DoS attacks
|
|
179
|
+
*
|
|
180
|
+
* @param url - The URL to fetch
|
|
181
|
+
* @param options - Fetch options (method, headers, body, etc.)
|
|
182
|
+
* @param timeoutMs - Timeout in milliseconds (default 30s, can be customized)
|
|
183
|
+
* @returns The fetch response
|
|
184
|
+
* @throws Error if request times out
|
|
185
|
+
* @security Do not use very long timeouts without understanding DoS implications
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* // Default timeout (30 seconds)
|
|
190
|
+
* const response = await fetchWithTimeout('https://example.com/file.pdf');
|
|
191
|
+
*
|
|
192
|
+
* // Custom timeout for slower connections
|
|
193
|
+
* const response = await fetchWithTimeout(url, options, 60000); // 60 seconds
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
declare function fetchWithTimeout(url: string, options?: RequestInit, timeoutMs?: number): Promise<Response>;
|
|
197
|
+
/**
|
|
198
|
+
* Check buffer size before allocation
|
|
199
|
+
* @param size - The buffer size in bytes
|
|
200
|
+
* @param maxSize - Maximum allowed size (default 100MB)
|
|
201
|
+
* @throws Error if buffer would exceed memory limit
|
|
202
|
+
*/
|
|
203
|
+
declare function validateBufferSize(size: number, maxSize?: number): void;
|
|
204
|
+
/**
|
|
205
|
+
* Memory-safe JSON parse with depth limit
|
|
206
|
+
*
|
|
207
|
+
* ⚠️ SECURITY WARNING: Depth limits prevent billion laughs / XML bomb attacks in JSON format.
|
|
208
|
+
* - Billion laughs attack: deeply nested JSON causes exponential memory expansion
|
|
209
|
+
* - Stack overflow: deeply nested structures can exhaust call stack
|
|
210
|
+
* - Resource exhaustion: parsing deeply nested JSON consumes CPU and memory
|
|
211
|
+
*
|
|
212
|
+
* Attack example (evil.json):
|
|
213
|
+
* ```json
|
|
214
|
+
* {"a":{"b":{"c":{"d":{"e":...}}}}} // 1000+ levels deep
|
|
215
|
+
* ```
|
|
216
|
+
*
|
|
217
|
+
* Default limit (100 levels) prevents attacks while allowing legitimate documents.
|
|
218
|
+
*
|
|
219
|
+
* @param text - JSON string to parse
|
|
220
|
+
* @param maxDepth - Maximum nesting depth in levels (default 100, can be customized)
|
|
221
|
+
* @returns Parsed object
|
|
222
|
+
* @throws Error if JSON exceeds depth limit, size limit, or is invalid
|
|
223
|
+
* @security Do not disable depth checking without understanding XML bomb implications
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* // Standard parsing with default depth limit (100 levels)
|
|
228
|
+
* const data = safeJsonParse(jsonString);
|
|
229
|
+
*
|
|
230
|
+
* // Custom depth limit for data that needs deeper nesting
|
|
231
|
+
* const data = safeJsonParse(jsonString, 200); // 200 levels
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
declare function safeJsonParse(text: string, maxDepth?: number): unknown;
|
|
235
|
+
|
|
236
|
+
export { DEFAULT_LIMITS, cleanupFetchController, createFetchController, fetchWithTimeout, getHostnameFromUrl, getSafePath, isPathSafe, safeJsonParse, secureFetch, validateBufferSize, validateFileSize, validatePath, validatePathSimple, validateUrl };
|