@dealcrawl/sdk 2.0.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/README.md +540 -0
- package/dist/sdk/src/client.d.ts +285 -0
- package/dist/sdk/src/client.d.ts.map +1 -0
- package/dist/sdk/src/client.js +336 -0
- package/dist/sdk/src/client.js.map +1 -0
- package/dist/sdk/src/error.d.ts +54 -0
- package/dist/sdk/src/error.d.ts.map +1 -0
- package/dist/sdk/src/error.js +115 -0
- package/dist/sdk/src/error.js.map +1 -0
- package/dist/sdk/src/index.d.ts +58 -0
- package/dist/sdk/src/index.d.ts.map +1 -0
- package/dist/sdk/src/index.js +65 -0
- package/dist/sdk/src/index.js.map +1 -0
- package/dist/sdk/src/resources/account.d.ts +143 -0
- package/dist/sdk/src/resources/account.d.ts.map +1 -0
- package/dist/sdk/src/resources/account.js +186 -0
- package/dist/sdk/src/resources/account.js.map +1 -0
- package/dist/sdk/src/resources/crawl.d.ts +87 -0
- package/dist/sdk/src/resources/crawl.d.ts.map +1 -0
- package/dist/sdk/src/resources/crawl.js +205 -0
- package/dist/sdk/src/resources/crawl.js.map +1 -0
- package/dist/sdk/src/resources/data.d.ts +157 -0
- package/dist/sdk/src/resources/data.d.ts.map +1 -0
- package/dist/sdk/src/resources/data.js +239 -0
- package/dist/sdk/src/resources/data.js.map +1 -0
- package/dist/sdk/src/resources/dork.d.ts +104 -0
- package/dist/sdk/src/resources/dork.d.ts.map +1 -0
- package/dist/sdk/src/resources/dork.js +163 -0
- package/dist/sdk/src/resources/dork.js.map +1 -0
- package/dist/sdk/src/resources/extract.d.ts +105 -0
- package/dist/sdk/src/resources/extract.d.ts.map +1 -0
- package/dist/sdk/src/resources/extract.js +246 -0
- package/dist/sdk/src/resources/extract.js.map +1 -0
- package/dist/sdk/src/resources/index.d.ts +14 -0
- package/dist/sdk/src/resources/index.d.ts.map +1 -0
- package/dist/sdk/src/resources/index.js +14 -0
- package/dist/sdk/src/resources/index.js.map +1 -0
- package/dist/sdk/src/resources/keys.d.ts +124 -0
- package/dist/sdk/src/resources/keys.d.ts.map +1 -0
- package/dist/sdk/src/resources/keys.js +168 -0
- package/dist/sdk/src/resources/keys.js.map +1 -0
- package/dist/sdk/src/resources/scrape.d.ts +53 -0
- package/dist/sdk/src/resources/scrape.d.ts.map +1 -0
- package/dist/sdk/src/resources/scrape.js +85 -0
- package/dist/sdk/src/resources/scrape.js.map +1 -0
- package/dist/sdk/src/resources/status.d.ts +100 -0
- package/dist/sdk/src/resources/status.d.ts.map +1 -0
- package/dist/sdk/src/resources/status.js +133 -0
- package/dist/sdk/src/resources/status.js.map +1 -0
- package/dist/sdk/src/resources/webhooks.d.ts +126 -0
- package/dist/sdk/src/resources/webhooks.d.ts.map +1 -0
- package/dist/sdk/src/resources/webhooks.js +167 -0
- package/dist/sdk/src/resources/webhooks.js.map +1 -0
- package/dist/sdk/src/types/config.d.ts +45 -0
- package/dist/sdk/src/types/config.d.ts.map +1 -0
- package/dist/sdk/src/types/config.js +10 -0
- package/dist/sdk/src/types/config.js.map +1 -0
- package/dist/sdk/src/types/index.d.ts +8 -0
- package/dist/sdk/src/types/index.d.ts.map +1 -0
- package/dist/sdk/src/types/index.js +8 -0
- package/dist/sdk/src/types/index.js.map +1 -0
- package/dist/sdk/src/types/options.d.ts +286 -0
- package/dist/sdk/src/types/options.d.ts.map +1 -0
- package/dist/sdk/src/types/options.js +6 -0
- package/dist/sdk/src/types/options.js.map +1 -0
- package/dist/sdk/src/types/responses.d.ts +385 -0
- package/dist/sdk/src/types/responses.d.ts.map +1 -0
- package/dist/sdk/src/types/responses.js +6 -0
- package/dist/sdk/src/types/responses.js.map +1 -0
- package/dist/sdk/src/utils/polling.d.ts +57 -0
- package/dist/sdk/src/utils/polling.d.ts.map +1 -0
- package/dist/sdk/src/utils/polling.js +110 -0
- package/dist/sdk/src/utils/polling.js.map +1 -0
- package/dist/sdk/src/utils/request.d.ts +47 -0
- package/dist/sdk/src/utils/request.d.ts.map +1 -0
- package/dist/sdk/src/utils/request.js +161 -0
- package/dist/sdk/src/utils/request.js.map +1 -0
- package/dist/shared/src/constants/errors.d.ts +26 -0
- package/dist/shared/src/constants/errors.d.ts.map +1 -0
- package/dist/shared/src/constants/errors.js +39 -0
- package/dist/shared/src/constants/errors.js.map +1 -0
- package/dist/shared/src/constants/http.d.ts +26 -0
- package/dist/shared/src/constants/http.d.ts.map +1 -0
- package/dist/shared/src/constants/http.js +26 -0
- package/dist/shared/src/constants/http.js.map +1 -0
- package/dist/shared/src/constants/index.d.ts +4 -0
- package/dist/shared/src/constants/index.d.ts.map +1 -0
- package/dist/shared/src/constants/index.js +5 -0
- package/dist/shared/src/constants/index.js.map +1 -0
- package/dist/shared/src/constants/limits.d.ts +52 -0
- package/dist/shared/src/constants/limits.d.ts.map +1 -0
- package/dist/shared/src/constants/limits.js +43 -0
- package/dist/shared/src/constants/limits.js.map +1 -0
- package/dist/shared/src/index.d.ts +5 -0
- package/dist/shared/src/index.d.ts.map +1 -0
- package/dist/shared/src/index.js +11 -0
- package/dist/shared/src/index.js.map +1 -0
- package/dist/shared/src/lib/index.d.ts +2 -0
- package/dist/shared/src/lib/index.d.ts.map +1 -0
- package/dist/shared/src/lib/index.js +2 -0
- package/dist/shared/src/lib/index.js.map +1 -0
- package/dist/shared/src/lib/redis.d.ts +14 -0
- package/dist/shared/src/lib/redis.d.ts.map +1 -0
- package/dist/shared/src/lib/redis.js +60 -0
- package/dist/shared/src/lib/redis.js.map +1 -0
- package/dist/shared/src/types/api-key.types.d.ts +94 -0
- package/dist/shared/src/types/api-key.types.d.ts.map +1 -0
- package/dist/shared/src/types/api-key.types.js +30 -0
- package/dist/shared/src/types/api-key.types.js.map +1 -0
- package/dist/shared/src/types/api.types.d.ts +38 -0
- package/dist/shared/src/types/api.types.d.ts.map +1 -0
- package/dist/shared/src/types/api.types.js +2 -0
- package/dist/shared/src/types/api.types.js.map +1 -0
- package/dist/shared/src/types/client.types.d.ts +73 -0
- package/dist/shared/src/types/client.types.d.ts.map +1 -0
- package/dist/shared/src/types/client.types.js +9 -0
- package/dist/shared/src/types/client.types.js.map +1 -0
- package/dist/shared/src/types/crawl.types.d.ts +65 -0
- package/dist/shared/src/types/crawl.types.d.ts.map +1 -0
- package/dist/shared/src/types/crawl.types.js +2 -0
- package/dist/shared/src/types/crawl.types.js.map +1 -0
- package/dist/shared/src/types/deal.types.d.ts +210 -0
- package/dist/shared/src/types/deal.types.d.ts.map +1 -0
- package/dist/shared/src/types/deal.types.js +6 -0
- package/dist/shared/src/types/deal.types.js.map +1 -0
- package/dist/shared/src/types/dork.types.d.ts +29 -0
- package/dist/shared/src/types/dork.types.d.ts.map +1 -0
- package/dist/shared/src/types/dork.types.js +2 -0
- package/dist/shared/src/types/dork.types.js.map +1 -0
- package/dist/shared/src/types/index.d.ts +8 -0
- package/dist/shared/src/types/index.d.ts.map +1 -0
- package/dist/shared/src/types/index.js +9 -0
- package/dist/shared/src/types/index.js.map +1 -0
- package/dist/shared/src/types/scrape.types.d.ts +151 -0
- package/dist/shared/src/types/scrape.types.d.ts.map +1 -0
- package/dist/shared/src/types/scrape.types.js +2 -0
- package/dist/shared/src/types/scrape.types.js.map +1 -0
- package/dist/shared/src/utils/date.d.ts +7 -0
- package/dist/shared/src/utils/date.d.ts.map +1 -0
- package/dist/shared/src/utils/date.js +25 -0
- package/dist/shared/src/utils/date.js.map +1 -0
- package/dist/shared/src/utils/hash.d.ts +4 -0
- package/dist/shared/src/utils/hash.d.ts.map +1 -0
- package/dist/shared/src/utils/hash.js +21 -0
- package/dist/shared/src/utils/hash.js.map +1 -0
- package/dist/shared/src/utils/index.d.ts +7 -0
- package/dist/shared/src/utils/index.d.ts.map +1 -0
- package/dist/shared/src/utils/index.js +8 -0
- package/dist/shared/src/utils/index.js.map +1 -0
- package/dist/shared/src/utils/logger.d.ts +66 -0
- package/dist/shared/src/utils/logger.d.ts.map +1 -0
- package/dist/shared/src/utils/logger.js +268 -0
- package/dist/shared/src/utils/logger.js.map +1 -0
- package/dist/shared/src/utils/retry.d.ts +11 -0
- package/dist/shared/src/utils/retry.d.ts.map +1 -0
- package/dist/shared/src/utils/retry.js +36 -0
- package/dist/shared/src/utils/retry.js.map +1 -0
- package/dist/shared/src/utils/url-validator.d.ts +37 -0
- package/dist/shared/src/utils/url-validator.d.ts.map +1 -0
- package/dist/shared/src/utils/url-validator.js +179 -0
- package/dist/shared/src/utils/url-validator.js.map +1 -0
- package/dist/shared/src/utils/url.d.ts +6 -0
- package/dist/shared/src/utils/url.d.ts.map +1 -0
- package/dist/shared/src/utils/url.js +56 -0
- package/dist/shared/src/utils/url.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const defaultOptions = {
|
|
2
|
+
maxRetries: 3,
|
|
3
|
+
initialDelayMs: 1000,
|
|
4
|
+
maxDelayMs: 30000,
|
|
5
|
+
backoffMultiplier: 2,
|
|
6
|
+
shouldRetry: () => true,
|
|
7
|
+
};
|
|
8
|
+
export async function retry(fn, options) {
|
|
9
|
+
const opts = { ...defaultOptions, ...options };
|
|
10
|
+
let lastError = null;
|
|
11
|
+
let delay = opts.initialDelayMs;
|
|
12
|
+
for (let attempt = 1; attempt <= opts.maxRetries; attempt++) {
|
|
13
|
+
try {
|
|
14
|
+
return await fn();
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
18
|
+
if (attempt === opts.maxRetries || !opts.shouldRetry(lastError, attempt)) {
|
|
19
|
+
throw lastError;
|
|
20
|
+
}
|
|
21
|
+
await sleep(delay);
|
|
22
|
+
delay = Math.min(delay * opts.backoffMultiplier, opts.maxDelayMs);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
throw lastError;
|
|
26
|
+
}
|
|
27
|
+
export function sleep(ms) {
|
|
28
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
29
|
+
}
|
|
30
|
+
export async function withTimeout(promise, timeoutMs) {
|
|
31
|
+
const timeout = new Promise((_, reject) => {
|
|
32
|
+
setTimeout(() => reject(new Error("Operation timed out")), timeoutMs);
|
|
33
|
+
});
|
|
34
|
+
return Promise.race([promise, timeout]);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../../../../shared/src/utils/retry.ts"],"names":[],"mappings":"AAQA,MAAM,cAAc,GAA2B;IAC7C,UAAU,EAAE,CAAC;IACb,cAAc,EAAE,IAAI;IACpB,UAAU,EAAE,KAAK;IACjB,iBAAiB,EAAE,CAAC;IACpB,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI;CACxB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,KAAK,CACzB,EAAoB,EACpB,OAAsB;IAEtB,MAAM,IAAI,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,OAAO,EAAE,CAAC;IAC/C,IAAI,SAAS,GAAiB,IAAI,CAAC;IACnC,IAAI,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC;IAEhC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QAC5D,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAEtE,IAAI,OAAO,KAAK,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC;gBACzE,MAAM,SAAS,CAAC;YAClB,CAAC;YAED,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;YACnB,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,MAAM,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAAmB,EACnB,SAAiB;IAEjB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;QAC/C,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL Validator with SSRF Protection
|
|
3
|
+
*
|
|
4
|
+
* Prevents Server-Side Request Forgery (SSRF) attacks by blocking:
|
|
5
|
+
* - Private IP ranges (127.0.0.1, 192.168.x.x, 10.x.x.x, etc.)
|
|
6
|
+
* - Local/Link-local addresses
|
|
7
|
+
* - File:// and other dangerous protocols
|
|
8
|
+
* - Localhost and internal domains
|
|
9
|
+
*/
|
|
10
|
+
export declare class URLValidationError extends Error {
|
|
11
|
+
readonly code: "INVALID_URL" | "BLOCKED_PROTOCOL" | "BLOCKED_HOSTNAME" | "PRIVATE_IP" | "SSRF_RISK";
|
|
12
|
+
constructor(message: string, code: "INVALID_URL" | "BLOCKED_PROTOCOL" | "BLOCKED_HOSTNAME" | "PRIVATE_IP" | "SSRF_RISK");
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Validate URL for SSRF protection
|
|
16
|
+
*
|
|
17
|
+
* @throws {URLValidationError} If URL is invalid or poses SSRF risk
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateURL(urlString: string): URL;
|
|
20
|
+
/**
|
|
21
|
+
* Validate URL and return boolean (no throw)
|
|
22
|
+
*/
|
|
23
|
+
export declare function isValidURL(urlString: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Validate multiple URLs
|
|
26
|
+
* @returns Array of validation results
|
|
27
|
+
*/
|
|
28
|
+
export declare function validateURLs(urls: string[]): Array<{
|
|
29
|
+
url: string;
|
|
30
|
+
valid: boolean;
|
|
31
|
+
error?: string;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Sanitize URL by removing dangerous components
|
|
35
|
+
*/
|
|
36
|
+
export declare function sanitizeURL(urlString: string): string | null;
|
|
37
|
+
//# sourceMappingURL=url-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-validator.d.ts","sourceRoot":"","sources":["../../../../../shared/src/utils/url-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4CH,qBAAa,kBAAmB,SAAQ,KAAK;aAGzB,IAAI,EAChB,aAAa,GACb,kBAAkB,GAClB,kBAAkB,GAClB,YAAY,GACZ,WAAW;gBANf,OAAO,EAAE,MAAM,EACC,IAAI,EAChB,aAAa,GACb,kBAAkB,GAClB,kBAAkB,GAClB,YAAY,GACZ,WAAW;CAKlB;AAkDD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,CA4DlD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAOrD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EAAE,GACb,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAaxD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe5D"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL Validator with SSRF Protection
|
|
3
|
+
*
|
|
4
|
+
* Prevents Server-Side Request Forgery (SSRF) attacks by blocking:
|
|
5
|
+
* - Private IP ranges (127.0.0.1, 192.168.x.x, 10.x.x.x, etc.)
|
|
6
|
+
* - Local/Link-local addresses
|
|
7
|
+
* - File:// and other dangerous protocols
|
|
8
|
+
* - Localhost and internal domains
|
|
9
|
+
*/
|
|
10
|
+
// ============================================
|
|
11
|
+
// PRIVATE IP RANGES (RFC 1918 + others)
|
|
12
|
+
// ============================================
|
|
13
|
+
const PRIVATE_IP_PATTERNS = [
|
|
14
|
+
// IPv4 Loopback
|
|
15
|
+
/^127\./,
|
|
16
|
+
// IPv4 Private ranges
|
|
17
|
+
/^10\./,
|
|
18
|
+
/^172\.(1[6-9]|2[0-9]|3[0-1])\./,
|
|
19
|
+
/^192\.168\./,
|
|
20
|
+
// Link-local
|
|
21
|
+
/^169\.254\./,
|
|
22
|
+
// IPv4 Broadcast
|
|
23
|
+
/^255\.255\.255\.255$/,
|
|
24
|
+
// IPv6 Loopback
|
|
25
|
+
/^::1$/,
|
|
26
|
+
/^::ffff:127\./,
|
|
27
|
+
// IPv6 Link-local
|
|
28
|
+
/^fe80:/i,
|
|
29
|
+
// IPv6 Unique local
|
|
30
|
+
/^fc00:/i,
|
|
31
|
+
/^fd00:/i,
|
|
32
|
+
];
|
|
33
|
+
const BLOCKED_HOSTNAMES = [
|
|
34
|
+
"localhost",
|
|
35
|
+
"0.0.0.0",
|
|
36
|
+
"[::]",
|
|
37
|
+
"[::1]",
|
|
38
|
+
"metadata.google.internal", // GCP metadata
|
|
39
|
+
"169.254.169.254", // AWS/Azure metadata
|
|
40
|
+
];
|
|
41
|
+
const DANGEROUS_PROTOCOLS = ["file:", "ftp:", "gopher:", "jar:", "data:"];
|
|
42
|
+
const ALLOWED_PROTOCOLS = ["http:", "https:"];
|
|
43
|
+
// ============================================
|
|
44
|
+
// VALIDATION ERROR TYPES
|
|
45
|
+
// ============================================
|
|
46
|
+
export class URLValidationError extends Error {
|
|
47
|
+
code;
|
|
48
|
+
constructor(message, code) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.code = code;
|
|
51
|
+
this.name = "URLValidationError";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// ============================================
|
|
55
|
+
// VALIDATION FUNCTIONS
|
|
56
|
+
// ============================================
|
|
57
|
+
/**
|
|
58
|
+
* Check if hostname is a private/internal IP
|
|
59
|
+
*/
|
|
60
|
+
function isPrivateIP(hostname) {
|
|
61
|
+
// Remove brackets from IPv6
|
|
62
|
+
const cleaned = hostname.replace(/^\[|\]$/g, "");
|
|
63
|
+
for (const pattern of PRIVATE_IP_PATTERNS) {
|
|
64
|
+
if (pattern.test(cleaned)) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Check if hostname is blocked
|
|
72
|
+
*/
|
|
73
|
+
function isBlockedHostname(hostname) {
|
|
74
|
+
const lower = hostname.toLowerCase();
|
|
75
|
+
// Check exact matches
|
|
76
|
+
if (BLOCKED_HOSTNAMES.includes(lower)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
// Check if ends with .local (mDNS)
|
|
80
|
+
if (lower.endsWith(".local")) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
// Check if is an internal domain
|
|
84
|
+
if (lower.endsWith(".internal") ||
|
|
85
|
+
lower.endsWith(".localhost") ||
|
|
86
|
+
lower === "localhost") {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Validate URL for SSRF protection
|
|
93
|
+
*
|
|
94
|
+
* @throws {URLValidationError} If URL is invalid or poses SSRF risk
|
|
95
|
+
*/
|
|
96
|
+
export function validateURL(urlString) {
|
|
97
|
+
// Parse URL
|
|
98
|
+
let url;
|
|
99
|
+
try {
|
|
100
|
+
url = new URL(urlString);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
throw new URLValidationError(`Invalid URL format: "${urlString}"`, "INVALID_URL");
|
|
104
|
+
}
|
|
105
|
+
// Check protocol
|
|
106
|
+
if (!ALLOWED_PROTOCOLS.includes(url.protocol)) {
|
|
107
|
+
if (DANGEROUS_PROTOCOLS.includes(url.protocol)) {
|
|
108
|
+
throw new URLValidationError(`Dangerous protocol blocked: ${url.protocol}`, "BLOCKED_PROTOCOL");
|
|
109
|
+
}
|
|
110
|
+
throw new URLValidationError(`Protocol not allowed: ${url.protocol} (only HTTP/HTTPS allowed)`, "BLOCKED_PROTOCOL");
|
|
111
|
+
}
|
|
112
|
+
// Check for blocked hostnames
|
|
113
|
+
if (isBlockedHostname(url.hostname)) {
|
|
114
|
+
throw new URLValidationError(`Access to internal/localhost addresses is blocked: ${url.hostname}`, "BLOCKED_HOSTNAME");
|
|
115
|
+
}
|
|
116
|
+
// Check for private IPs
|
|
117
|
+
if (isPrivateIP(url.hostname)) {
|
|
118
|
+
throw new URLValidationError(`Access to private IP addresses is blocked: ${url.hostname}`, "PRIVATE_IP");
|
|
119
|
+
}
|
|
120
|
+
// Check for suspicious patterns
|
|
121
|
+
// Username/password in URL can be used for SSRF
|
|
122
|
+
if (url.username || url.password) {
|
|
123
|
+
throw new URLValidationError("URLs with credentials are not allowed", "SSRF_RISK");
|
|
124
|
+
}
|
|
125
|
+
// Check for URL redirections via @ symbol
|
|
126
|
+
if (url.href.includes("@")) {
|
|
127
|
+
throw new URLValidationError("URLs with @ symbol are not allowed (potential SSRF via redirect)", "SSRF_RISK");
|
|
128
|
+
}
|
|
129
|
+
return url;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Validate URL and return boolean (no throw)
|
|
133
|
+
*/
|
|
134
|
+
export function isValidURL(urlString) {
|
|
135
|
+
try {
|
|
136
|
+
validateURL(urlString);
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Validate multiple URLs
|
|
145
|
+
* @returns Array of validation results
|
|
146
|
+
*/
|
|
147
|
+
export function validateURLs(urls) {
|
|
148
|
+
return urls.map((url) => {
|
|
149
|
+
try {
|
|
150
|
+
validateURL(url);
|
|
151
|
+
return { url, valid: true };
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
return {
|
|
155
|
+
url,
|
|
156
|
+
valid: false,
|
|
157
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Sanitize URL by removing dangerous components
|
|
164
|
+
*/
|
|
165
|
+
export function sanitizeURL(urlString) {
|
|
166
|
+
try {
|
|
167
|
+
const url = new URL(urlString);
|
|
168
|
+
// Force HTTPS for security
|
|
169
|
+
url.protocol = "https:";
|
|
170
|
+
// Remove credentials
|
|
171
|
+
url.username = "";
|
|
172
|
+
url.password = "";
|
|
173
|
+
return url.href;
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=url-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-validator.js","sourceRoot":"","sources":["../../../../../shared/src/utils/url-validator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function normalizeUrl(url: string): string;
|
|
2
|
+
export declare function getDomain(url: string): string;
|
|
3
|
+
export declare function isValidUrl(url: string): boolean;
|
|
4
|
+
export declare function isSameDomain(url1: string, url2: string): boolean;
|
|
5
|
+
export declare function joinUrl(base: string, path: string): string;
|
|
6
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../../../../shared/src/utils/url.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA0BhD;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM7C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAO/C;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAM1D"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function normalizeUrl(url) {
|
|
2
|
+
try {
|
|
3
|
+
const parsed = new URL(url);
|
|
4
|
+
parsed.hash = "";
|
|
5
|
+
// Remove common tracking params
|
|
6
|
+
const trackingParams = [
|
|
7
|
+
"utm_source",
|
|
8
|
+
"utm_medium",
|
|
9
|
+
"utm_campaign",
|
|
10
|
+
"utm_content",
|
|
11
|
+
"utm_term",
|
|
12
|
+
"ref",
|
|
13
|
+
"fbclid",
|
|
14
|
+
"gclid",
|
|
15
|
+
];
|
|
16
|
+
trackingParams.forEach((param) => parsed.searchParams.delete(param));
|
|
17
|
+
let normalized = parsed.href;
|
|
18
|
+
// Remove trailing slash
|
|
19
|
+
if (normalized.endsWith("/") && parsed.pathname !== "/") {
|
|
20
|
+
normalized = normalized.slice(0, -1);
|
|
21
|
+
}
|
|
22
|
+
return normalized;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return url;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function getDomain(url) {
|
|
29
|
+
try {
|
|
30
|
+
return new URL(url).hostname;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return "";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function isValidUrl(url) {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = new URL(url);
|
|
39
|
+
return ["http:", "https:"].includes(parsed.protocol);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function isSameDomain(url1, url2) {
|
|
46
|
+
return getDomain(url1) === getDomain(url2);
|
|
47
|
+
}
|
|
48
|
+
export function joinUrl(base, path) {
|
|
49
|
+
try {
|
|
50
|
+
return new URL(path, base).href;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return path;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../../../../../shared/src/utils/url.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;QACjB,gCAAgC;QAChC,MAAM,cAAc,GAAG;YACrB,YAAY;YACZ,YAAY;YACZ,cAAc;YACd,aAAa;YACb,UAAU;YACV,KAAK;YACL,QAAQ;YACR,OAAO;SACR,CAAC;QACF,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAErE,IAAI,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC;QAC7B,wBAAwB;QACxB,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;YACxD,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,IAAY;IACrD,OAAO,SAAS,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,IAAY;IAChD,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dealcrawl/sdk",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Official SDK for DealCrawl web scraping and crawling API",
|
|
5
|
+
"author": "DealUp <contact@dealup.cc>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/DealUp-cc/DealCrawl.git",
|
|
10
|
+
"directory": "packages/sdk"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"dealcrawl",
|
|
14
|
+
"web-scraping",
|
|
15
|
+
"crawler",
|
|
16
|
+
"scraper",
|
|
17
|
+
"deal-finder",
|
|
18
|
+
"api-client",
|
|
19
|
+
"sdk"
|
|
20
|
+
],
|
|
21
|
+
"main": "./dist/sdk/src/index.js",
|
|
22
|
+
"types": "./dist/sdk/src/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"import": "./dist/sdk/src/index.js",
|
|
26
|
+
"require": "./dist/sdk/src/index.js",
|
|
27
|
+
"types": "./dist/sdk/src/index.d.ts"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc",
|
|
36
|
+
"type-check": "tsc --noEmit",
|
|
37
|
+
"clean": "rm -rf dist"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@dealcrawl/shared": "workspace:*"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"typescript": "^5.7.0"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18.0.0"
|
|
47
|
+
},
|
|
48
|
+
"sideEffects": false
|
|
49
|
+
}
|