@skyramp/skyramp 1.3.10 → 1.3.12

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.
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Copyright (c) Skyramp Corporation.
3
+ *
4
+ * PDF Viewer module for execution-time injection
5
+ */
6
+
7
+ export { PdfUrlValidator, ValidationResult } from './validator';
8
+ export { PDF_VIEWER_INJECTION_SCRIPT } from './bundle';
@@ -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 };
package/src/utils.d.ts CHANGED
@@ -104,3 +104,11 @@ export function checkForUpdate(component: string): Promise<void>;
104
104
  * The Skyramp YAML version constant.
105
105
  */
106
106
  export const SKYRAMP_YAML_VERSION: string;
107
+
108
+ /**
109
+ * Returns the base URL from an environment variable, with a fallback default.
110
+ * @param envVar - The environment variable name
111
+ * @param defaultUrl - The default URL if the env var is not set
112
+ * @returns The resolved base URL
113
+ */
114
+ export function getBaseUrl(envVar: string, defaultUrl: string): string;
package/src/utils.js CHANGED
@@ -257,6 +257,10 @@ function checkForUpdate(component) {
257
257
  });
258
258
  }
259
259
 
260
+ function getBaseUrl(envVar, defaultUrl) {
261
+ return process.env[envVar] ?? defaultUrl;
262
+ }
263
+
260
264
  module.exports = {
261
265
  createTestDescriptionFromScenario,
262
266
  getYamlBytes,
@@ -267,5 +271,6 @@ module.exports = {
267
271
  iterate,
268
272
  pushToolEvent,
269
273
  checkForUpdate,
274
+ getBaseUrl,
270
275
  SKYRAMP_YAML_VERSION
271
276
  }