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 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
- const parsedUrl = url.startsWith('//')
53
- ? new URL(`http:${url}`)
54
- : new URL(url, 'http://dummy');
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
- const parsedUrl = url.startsWith('//')
51
- ? new URL(`http:${url}`)
52
- : new URL(url, 'http://dummy');
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-sri4",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "A Vite plugin to generate Subresource Integrity (SRI) hashes for output files.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",