@skyramp/skyramp 1.3.10 → 1.3.11
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 +5 -3
- package/scripts/build-pdf-bundle.js +141 -0
- package/scripts/pdf-execution-helpers.js +340 -0
- package/src/classes/MockV2.d.ts +9 -17
- package/src/classes/MockV2.js +20 -22
- package/src/classes/SmartPlaywright.js +328 -2
- package/src/pdfViewer/bundle.d.ts +11 -0
- package/src/pdfViewer/bundle.js +1349 -0
- package/src/pdfViewer/index.d.ts +8 -0
- package/src/pdfViewer/index.js +14 -0
- package/src/pdfViewer/validator.d.ts +25 -0
- package/src/pdfViewer/validator.js +119 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Skyramp Corporation.
|
|
3
|
+
*
|
|
4
|
+
* PDF Viewer module for execution-time injection
|
|
5
|
+
* Barrel exports for all PDF viewer components
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { PdfUrlValidator } = require('./validator');
|
|
9
|
+
const { PDF_VIEWER_INJECTION_SCRIPT } = require('./bundle');
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
PdfUrlValidator,
|
|
13
|
+
PDF_VIEWER_INJECTION_SCRIPT
|
|
14
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Skyramp Corporation.
|
|
3
|
+
*
|
|
4
|
+
* URL validator for PDF fetching - prevents SSRF attacks
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface ValidationResult {
|
|
8
|
+
valid: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export declare class PdfUrlValidator {
|
|
13
|
+
static readonly MAX_PDF_SIZE: number;
|
|
14
|
+
static readonly PRIVATE_IP_RANGES: RegExp[];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Validates a PDF URL to prevent SSRF attacks
|
|
18
|
+
*/
|
|
19
|
+
static validateUrl(urlString: string): ValidationResult;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Validates a fetch response to ensure it's a valid PDF
|
|
23
|
+
*/
|
|
24
|
+
static validateResponse(response: Response): Promise<ValidationResult>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Skyramp Corporation.
|
|
3
|
+
*
|
|
4
|
+
* URL validator for PDF fetching - prevents SSRF attacks
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class PdfUrlValidator {
|
|
8
|
+
static MAX_PDF_SIZE = 50 * 1024 * 1024; // 50MB
|
|
9
|
+
|
|
10
|
+
// Private IP ranges to block (RFC 1918 + loopback + link-local)
|
|
11
|
+
static PRIVATE_IP_RANGES = [
|
|
12
|
+
/^127\./, // 127.0.0.0/8 (loopback)
|
|
13
|
+
/^10\./, // 10.0.0.0/8 (private)
|
|
14
|
+
/^172\.(1[6-9]|2[0-9]|3[0-1])\./, // 172.16.0.0/12 (private)
|
|
15
|
+
/^192\.168\./, // 192.168.0.0/16 (private)
|
|
16
|
+
/^169\.254\./, // 169.254.0.0/16 (link-local)
|
|
17
|
+
/^::1$/, // IPv6 loopback
|
|
18
|
+
/^fe80:/i, // IPv6 link-local
|
|
19
|
+
/^fc00:/i, // IPv6 unique local
|
|
20
|
+
/^fd00:/i, // IPv6 unique local
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Validates a PDF URL to prevent SSRF attacks
|
|
25
|
+
* @param {string} urlString - The URL to validate
|
|
26
|
+
* @returns {{valid: boolean, error?: string}} Validation result
|
|
27
|
+
*/
|
|
28
|
+
static validateUrl(urlString) {
|
|
29
|
+
// Check for empty/null
|
|
30
|
+
if (!urlString || typeof urlString !== 'string') {
|
|
31
|
+
return { valid: false, error: 'URL is empty or invalid' };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let url;
|
|
35
|
+
try {
|
|
36
|
+
url = new URL(urlString);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
return { valid: false, error: `Invalid URL format: ${error}` };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Only allow http/https protocols
|
|
42
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
43
|
+
return {
|
|
44
|
+
valid: false,
|
|
45
|
+
error: `Protocol '${url.protocol}' not allowed. Only http: and https: are permitted`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Block localhost by hostname
|
|
50
|
+
const hostname = url.hostname.toLowerCase();
|
|
51
|
+
if (hostname === 'localhost' || hostname === '0.0.0.0') {
|
|
52
|
+
return {
|
|
53
|
+
valid: false,
|
|
54
|
+
error: `Hostname '${hostname}' not allowed (localhost blocked)`
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check for private IP ranges
|
|
59
|
+
for (const range of this.PRIVATE_IP_RANGES) {
|
|
60
|
+
if (range.test(hostname)) {
|
|
61
|
+
return {
|
|
62
|
+
valid: false,
|
|
63
|
+
error: `IP address '${hostname}' not allowed (private IP range blocked)`
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Allow http:// only for localhost in development (but we already blocked localhost above)
|
|
69
|
+
// So effectively, only https:// is allowed for remote hosts
|
|
70
|
+
if (url.protocol === 'http:' && hostname !== 'localhost') {
|
|
71
|
+
return {
|
|
72
|
+
valid: false,
|
|
73
|
+
error: 'http:// protocol only allowed for localhost. Use https:// for remote hosts'
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { valid: true };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Validates a fetch response to ensure it's a valid PDF
|
|
82
|
+
* @param {Response} response - The fetch response to validate
|
|
83
|
+
* @returns {Promise<{valid: boolean, error?: string}>} Validation result
|
|
84
|
+
*/
|
|
85
|
+
static async validateResponse(response) {
|
|
86
|
+
// Check status code
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
return {
|
|
89
|
+
valid: false,
|
|
90
|
+
error: `HTTP ${response.status}: ${response.statusText}`
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check Content-Type
|
|
95
|
+
const contentType = response.headers.get('content-type');
|
|
96
|
+
if (contentType && !contentType.includes('application/pdf')) {
|
|
97
|
+
return {
|
|
98
|
+
valid: false,
|
|
99
|
+
error: `Invalid Content-Type: ${contentType} (expected application/pdf)`
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check Content-Length if available
|
|
104
|
+
const contentLength = response.headers.get('content-length');
|
|
105
|
+
if (contentLength) {
|
|
106
|
+
const size = parseInt(contentLength, 10);
|
|
107
|
+
if (size > this.MAX_PDF_SIZE) {
|
|
108
|
+
return {
|
|
109
|
+
valid: false,
|
|
110
|
+
error: `PDF too large: ${size} bytes (max ${this.MAX_PDF_SIZE} bytes)`
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return { valid: true };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = { PdfUrlValidator };
|