vite-plugin-sri4 1.8.0 → 1.8.1
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 +48 -4
- package/dist/index.js +48 -4
- 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
|
}
|
|
@@ -152,6 +163,29 @@ async function processTag(tag, url, options, sriMap) {
|
|
|
152
163
|
return tag;
|
|
153
164
|
}
|
|
154
165
|
|
|
166
|
+
async function processInlineScript(tag, options) {
|
|
167
|
+
if (tag.includes('integrity=')) {
|
|
168
|
+
log(`Skip inline script with existing integrity attribute: ${tag}`, options);
|
|
169
|
+
return tag;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const content = tag.match(/<script[^>]*>([\s\S]*?)<\/script>/)?.[1]?.trim();
|
|
173
|
+
if (!content) {
|
|
174
|
+
log(`Skip empty inline script: ${tag}`, options);
|
|
175
|
+
return tag;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const hash = computeSri(content, options.algorithm);
|
|
179
|
+
if (hash) {
|
|
180
|
+
log(`Computing SRI for inline script: ${hash}`, options);
|
|
181
|
+
const newTag = tag.replace('>', ` integrity="${hash}">`);
|
|
182
|
+
log(`New inline script tag: ${newTag}`, options);
|
|
183
|
+
return newTag;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return tag;
|
|
187
|
+
}
|
|
188
|
+
|
|
155
189
|
function sri(userOptions = {}) {
|
|
156
190
|
const options = { ...DEFAULT_OPTIONS, ...userOptions };
|
|
157
191
|
const sriMap = new Map();
|
|
@@ -223,6 +257,16 @@ function sri(userOptions = {}) {
|
|
|
223
257
|
html = html.replace(tag, newTag);
|
|
224
258
|
}
|
|
225
259
|
|
|
260
|
+
// Process inline scripts if enabled
|
|
261
|
+
if (options.inlineScripts) {
|
|
262
|
+
const inlineScripts = html.match(/<script[^>]*>([^<]+)<\/script>/g) || [];
|
|
263
|
+
log(`Found ${inlineScripts.length} inline script tags`, options);
|
|
264
|
+
for (const tag of inlineScripts) {
|
|
265
|
+
const newTag = await processInlineScript(tag, options);
|
|
266
|
+
html = html.replace(tag, newTag);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
226
270
|
// Process link tags
|
|
227
271
|
const linkTags = html.match(/<link[^>]+href=["']([^"']+)["'][^>]*>/g) || [];
|
|
228
272
|
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
|
}
|
|
@@ -150,6 +161,29 @@ async function processTag(tag, url, options, sriMap) {
|
|
|
150
161
|
return tag;
|
|
151
162
|
}
|
|
152
163
|
|
|
164
|
+
async function processInlineScript(tag, options) {
|
|
165
|
+
if (tag.includes('integrity=')) {
|
|
166
|
+
log(`Skip inline script with existing integrity attribute: ${tag}`, options);
|
|
167
|
+
return tag;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const content = tag.match(/<script[^>]*>([\s\S]*?)<\/script>/)?.[1]?.trim();
|
|
171
|
+
if (!content) {
|
|
172
|
+
log(`Skip empty inline script: ${tag}`, options);
|
|
173
|
+
return tag;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const hash = computeSri(content, options.algorithm);
|
|
177
|
+
if (hash) {
|
|
178
|
+
log(`Computing SRI for inline script: ${hash}`, options);
|
|
179
|
+
const newTag = tag.replace('>', ` integrity="${hash}">`);
|
|
180
|
+
log(`New inline script tag: ${newTag}`, options);
|
|
181
|
+
return newTag;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return tag;
|
|
185
|
+
}
|
|
186
|
+
|
|
153
187
|
function sri(userOptions = {}) {
|
|
154
188
|
const options = { ...DEFAULT_OPTIONS, ...userOptions };
|
|
155
189
|
const sriMap = new Map();
|
|
@@ -221,6 +255,16 @@ function sri(userOptions = {}) {
|
|
|
221
255
|
html = html.replace(tag, newTag);
|
|
222
256
|
}
|
|
223
257
|
|
|
258
|
+
// Process inline scripts if enabled
|
|
259
|
+
if (options.inlineScripts) {
|
|
260
|
+
const inlineScripts = html.match(/<script[^>]*>([^<]+)<\/script>/g) || [];
|
|
261
|
+
log(`Found ${inlineScripts.length} inline script tags`, options);
|
|
262
|
+
for (const tag of inlineScripts) {
|
|
263
|
+
const newTag = await processInlineScript(tag, options);
|
|
264
|
+
html = html.replace(tag, newTag);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
224
268
|
// Process link tags
|
|
225
269
|
const linkTags = html.match(/<link[^>]+href=["']([^"']+)["'][^>]*>/g) || [];
|
|
226
270
|
log(`Found ${linkTags.length} link tags`, options);
|