vite-plugin-sri4 1.8.0 → 1.8.2
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/dist/index.cjs +64 -5
- package/dist/index.js +64 -5
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -9,7 +9,8 @@ const DEFAULT_OPTIONS = {
|
|
|
9
9
|
algorithm: 'sha384',
|
|
10
10
|
bypassDomains: [],
|
|
11
11
|
crossorigin: 'anonymous',
|
|
12
|
-
debug: false
|
|
12
|
+
debug: false,
|
|
13
|
+
inlineScripts: false
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
function log(message, options) {
|
|
@@ -20,6 +21,9 @@ function log(message, options) {
|
|
|
20
21
|
|
|
21
22
|
function computeSri(content, algorithm = 'sha384') {
|
|
22
23
|
try {
|
|
24
|
+
if (typeof content === 'string') {
|
|
25
|
+
content = Buffer.from(content, 'utf-8');
|
|
26
|
+
}
|
|
23
27
|
const hash = node_crypto.createHash(algorithm)
|
|
24
28
|
.update(content)
|
|
25
29
|
.digest('base64');
|
|
@@ -49,14 +53,21 @@ async function externalResourceIsCorsEnabled(url, options) {
|
|
|
49
53
|
function isBypassDomain(url, bypassDomains = []) {
|
|
50
54
|
if (!bypassDomains.length) return false;
|
|
51
55
|
try {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
let parsedUrl;
|
|
57
|
+
if (url.startsWith('//')) {
|
|
58
|
+
parsedUrl = new URL(`http:${url}`);
|
|
59
|
+
} else if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
60
|
+
parsedUrl = new URL(url);
|
|
61
|
+
} else {
|
|
62
|
+
parsedUrl = new URL(url, 'http://dummy');
|
|
63
|
+
}
|
|
64
|
+
|
|
55
65
|
return bypassDomains.some(
|
|
56
66
|
(domain) => parsedUrl.hostname === domain ||
|
|
57
67
|
parsedUrl.hostname.endsWith(`.${domain}`)
|
|
58
68
|
);
|
|
59
69
|
} catch (e) {
|
|
70
|
+
console.error(`${LOG_PREFIX} Failed to parse URL: ${url}`, e);
|
|
60
71
|
return false;
|
|
61
72
|
}
|
|
62
73
|
}
|
|
@@ -76,11 +87,23 @@ function getAllPossiblePaths(url) {
|
|
|
76
87
|
url.replace(/^static\//, '')
|
|
77
88
|
];
|
|
78
89
|
|
|
79
|
-
|
|
90
|
+
// Handle Vite's hashed filenames (e.g., index-DPifqqS2.js -> index.js)
|
|
91
|
+
const withoutHash = url.replace(/-[a-zA-Z0-9]{8}\./, '.');
|
|
80
92
|
if (withoutHash !== url) {
|
|
81
93
|
paths.push(...getAllPossiblePaths(withoutHash));
|
|
82
94
|
}
|
|
83
95
|
|
|
96
|
+
// Handle variations with and without /static/ prefix for hashed files
|
|
97
|
+
const hashedMatch = url.match(/^(?:\/static\/)?(.*?)-[a-zA-Z0-9]{8}(\..*?)$/);
|
|
98
|
+
if (hashedMatch) {
|
|
99
|
+
const [, base, ext] = hashedMatch;
|
|
100
|
+
paths.push(
|
|
101
|
+
`${base}${ext}`,
|
|
102
|
+
`/static/${base}${ext}`,
|
|
103
|
+
`static/${base}${ext}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
84
107
|
return [...new Set(paths)];
|
|
85
108
|
}
|
|
86
109
|
|
|
@@ -129,6 +152,9 @@ async function processTag(tag, url, options, sriMap) {
|
|
|
129
152
|
let integrity = null;
|
|
130
153
|
|
|
131
154
|
log(`Checking possible paths for ${url}:`, options);
|
|
155
|
+
log(`Possible paths:`, options);
|
|
156
|
+
possiblePaths.forEach(path => log(` - ${path}`, options));
|
|
157
|
+
|
|
132
158
|
for (const path of possiblePaths) {
|
|
133
159
|
log(`- Checking path: ${path}`, options);
|
|
134
160
|
if (sriMap.has(path)) {
|
|
@@ -152,6 +178,29 @@ async function processTag(tag, url, options, sriMap) {
|
|
|
152
178
|
return tag;
|
|
153
179
|
}
|
|
154
180
|
|
|
181
|
+
async function processInlineScript(tag, options) {
|
|
182
|
+
if (tag.includes('integrity=')) {
|
|
183
|
+
log(`Skip inline script with existing integrity attribute: ${tag}`, options);
|
|
184
|
+
return tag;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const content = tag.match(/<script[^>]*>([\s\S]*?)<\/script>/)?.[1]?.trim();
|
|
188
|
+
if (!content) {
|
|
189
|
+
log(`Skip empty inline script: ${tag}`, options);
|
|
190
|
+
return tag;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const hash = computeSri(content, options.algorithm);
|
|
194
|
+
if (hash) {
|
|
195
|
+
log(`Computing SRI for inline script: ${hash}`, options);
|
|
196
|
+
const newTag = tag.replace('>', ` integrity="${hash}">`);
|
|
197
|
+
log(`New inline script tag: ${newTag}`, options);
|
|
198
|
+
return newTag;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return tag;
|
|
202
|
+
}
|
|
203
|
+
|
|
155
204
|
function sri(userOptions = {}) {
|
|
156
205
|
const options = { ...DEFAULT_OPTIONS, ...userOptions };
|
|
157
206
|
const sriMap = new Map();
|
|
@@ -223,6 +272,16 @@ function sri(userOptions = {}) {
|
|
|
223
272
|
html = html.replace(tag, newTag);
|
|
224
273
|
}
|
|
225
274
|
|
|
275
|
+
// Process inline scripts if enabled
|
|
276
|
+
if (options.inlineScripts) {
|
|
277
|
+
const inlineScripts = html.match(/<script[^>]*>([^<]+)<\/script>/g) || [];
|
|
278
|
+
log(`Found ${inlineScripts.length} inline script tags`, options);
|
|
279
|
+
for (const tag of inlineScripts) {
|
|
280
|
+
const newTag = await processInlineScript(tag, options);
|
|
281
|
+
html = html.replace(tag, newTag);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
226
285
|
// Process link tags
|
|
227
286
|
const linkTags = html.match(/<link[^>]+href=["']([^"']+)["'][^>]*>/g) || [];
|
|
228
287
|
log(`Found ${linkTags.length} link tags`, options);
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,8 @@ const DEFAULT_OPTIONS = {
|
|
|
7
7
|
algorithm: 'sha384',
|
|
8
8
|
bypassDomains: [],
|
|
9
9
|
crossorigin: 'anonymous',
|
|
10
|
-
debug: false
|
|
10
|
+
debug: false,
|
|
11
|
+
inlineScripts: false
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
function log(message, options) {
|
|
@@ -18,6 +19,9 @@ function log(message, options) {
|
|
|
18
19
|
|
|
19
20
|
function computeSri(content, algorithm = 'sha384') {
|
|
20
21
|
try {
|
|
22
|
+
if (typeof content === 'string') {
|
|
23
|
+
content = Buffer.from(content, 'utf-8');
|
|
24
|
+
}
|
|
21
25
|
const hash = createHash(algorithm)
|
|
22
26
|
.update(content)
|
|
23
27
|
.digest('base64');
|
|
@@ -47,14 +51,21 @@ async function externalResourceIsCorsEnabled(url, options) {
|
|
|
47
51
|
function isBypassDomain(url, bypassDomains = []) {
|
|
48
52
|
if (!bypassDomains.length) return false;
|
|
49
53
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
let parsedUrl;
|
|
55
|
+
if (url.startsWith('//')) {
|
|
56
|
+
parsedUrl = new URL(`http:${url}`);
|
|
57
|
+
} else if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
58
|
+
parsedUrl = new URL(url);
|
|
59
|
+
} else {
|
|
60
|
+
parsedUrl = new URL(url, 'http://dummy');
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
return bypassDomains.some(
|
|
54
64
|
(domain) => parsedUrl.hostname === domain ||
|
|
55
65
|
parsedUrl.hostname.endsWith(`.${domain}`)
|
|
56
66
|
);
|
|
57
67
|
} catch (e) {
|
|
68
|
+
console.error(`${LOG_PREFIX} Failed to parse URL: ${url}`, e);
|
|
58
69
|
return false;
|
|
59
70
|
}
|
|
60
71
|
}
|
|
@@ -74,11 +85,23 @@ function getAllPossiblePaths(url) {
|
|
|
74
85
|
url.replace(/^static\//, '')
|
|
75
86
|
];
|
|
76
87
|
|
|
77
|
-
|
|
88
|
+
// Handle Vite's hashed filenames (e.g., index-DPifqqS2.js -> index.js)
|
|
89
|
+
const withoutHash = url.replace(/-[a-zA-Z0-9]{8}\./, '.');
|
|
78
90
|
if (withoutHash !== url) {
|
|
79
91
|
paths.push(...getAllPossiblePaths(withoutHash));
|
|
80
92
|
}
|
|
81
93
|
|
|
94
|
+
// Handle variations with and without /static/ prefix for hashed files
|
|
95
|
+
const hashedMatch = url.match(/^(?:\/static\/)?(.*?)-[a-zA-Z0-9]{8}(\..*?)$/);
|
|
96
|
+
if (hashedMatch) {
|
|
97
|
+
const [, base, ext] = hashedMatch;
|
|
98
|
+
paths.push(
|
|
99
|
+
`${base}${ext}`,
|
|
100
|
+
`/static/${base}${ext}`,
|
|
101
|
+
`static/${base}${ext}`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
82
105
|
return [...new Set(paths)];
|
|
83
106
|
}
|
|
84
107
|
|
|
@@ -127,6 +150,9 @@ async function processTag(tag, url, options, sriMap) {
|
|
|
127
150
|
let integrity = null;
|
|
128
151
|
|
|
129
152
|
log(`Checking possible paths for ${url}:`, options);
|
|
153
|
+
log(`Possible paths:`, options);
|
|
154
|
+
possiblePaths.forEach(path => log(` - ${path}`, options));
|
|
155
|
+
|
|
130
156
|
for (const path of possiblePaths) {
|
|
131
157
|
log(`- Checking path: ${path}`, options);
|
|
132
158
|
if (sriMap.has(path)) {
|
|
@@ -150,6 +176,29 @@ async function processTag(tag, url, options, sriMap) {
|
|
|
150
176
|
return tag;
|
|
151
177
|
}
|
|
152
178
|
|
|
179
|
+
async function processInlineScript(tag, options) {
|
|
180
|
+
if (tag.includes('integrity=')) {
|
|
181
|
+
log(`Skip inline script with existing integrity attribute: ${tag}`, options);
|
|
182
|
+
return tag;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const content = tag.match(/<script[^>]*>([\s\S]*?)<\/script>/)?.[1]?.trim();
|
|
186
|
+
if (!content) {
|
|
187
|
+
log(`Skip empty inline script: ${tag}`, options);
|
|
188
|
+
return tag;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const hash = computeSri(content, options.algorithm);
|
|
192
|
+
if (hash) {
|
|
193
|
+
log(`Computing SRI for inline script: ${hash}`, options);
|
|
194
|
+
const newTag = tag.replace('>', ` integrity="${hash}">`);
|
|
195
|
+
log(`New inline script tag: ${newTag}`, options);
|
|
196
|
+
return newTag;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return tag;
|
|
200
|
+
}
|
|
201
|
+
|
|
153
202
|
function sri(userOptions = {}) {
|
|
154
203
|
const options = { ...DEFAULT_OPTIONS, ...userOptions };
|
|
155
204
|
const sriMap = new Map();
|
|
@@ -221,6 +270,16 @@ function sri(userOptions = {}) {
|
|
|
221
270
|
html = html.replace(tag, newTag);
|
|
222
271
|
}
|
|
223
272
|
|
|
273
|
+
// Process inline scripts if enabled
|
|
274
|
+
if (options.inlineScripts) {
|
|
275
|
+
const inlineScripts = html.match(/<script[^>]*>([^<]+)<\/script>/g) || [];
|
|
276
|
+
log(`Found ${inlineScripts.length} inline script tags`, options);
|
|
277
|
+
for (const tag of inlineScripts) {
|
|
278
|
+
const newTag = await processInlineScript(tag, options);
|
|
279
|
+
html = html.replace(tag, newTag);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
224
283
|
// Process link tags
|
|
225
284
|
const linkTags = html.match(/<link[^>]+href=["']([^"']+)["'][^>]*>/g) || [];
|
|
226
285
|
log(`Found ${linkTags.length} link tags`, options);
|