@dintero/sri-to-dist 1.0.3 → 1.0.5
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/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# sri-to-dist
|
|
2
2
|
|
|
3
|
-
[](https://github.com/Dintero/sri-to-dist/actions)[](https://github.com/Dintero/sri-to-dist/actions)[](https://www.npmjs.com/package/@dintero/sri-to-dist)
|
|
4
4
|
|
|
5
5
|
A tool to add subresource integrity (SRI) hashes to HTML files.
|
|
6
6
|
|
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
declare const extractLinkRel: (scriptOrLinkTag: string) => string | undefined;
|
|
2
2
|
declare const isSriTag: (scriptOrLinkTag: string) => boolean;
|
|
3
|
+
export declare const isImportMapTag: (scriptOrLinkTag: string) => boolean;
|
|
4
|
+
type ImportMapJson = {
|
|
5
|
+
imports: {
|
|
6
|
+
[key: string]: string;
|
|
7
|
+
};
|
|
8
|
+
integrity?: {
|
|
9
|
+
[src: string]: string;
|
|
10
|
+
};
|
|
11
|
+
scopes?: {
|
|
12
|
+
[scope: string]: {
|
|
13
|
+
[key: string]: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export declare const parseImportMap: (scriptOrLinkTag: string) => ImportMapJson;
|
|
18
|
+
export declare const extractImports: (importMapJson: ImportMapJson) => {
|
|
19
|
+
src: string;
|
|
20
|
+
oldHash: string | undefined;
|
|
21
|
+
}[];
|
|
22
|
+
export declare const toSriImportMap: (tag: string, importMapJson: ImportMapJson, newIntegrityMap: ImportMapJson["integrity"]) => string;
|
|
3
23
|
declare const toHtmlWithSri: (htmlContent: string, baseDir: string, baseUrl?: string, noRemote?: boolean, verify?: boolean) => Promise<string>;
|
|
4
24
|
declare const getContent: (src: string, baseDir: string, baseUrl?: string, noRemote?: boolean) => Promise<Buffer>;
|
|
5
25
|
declare const fetchRemoteContent: (src: string) => Promise<Buffer>;
|
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.handleUpdatedHtml = exports.toSriScriptTag = exports.ensureCrossoriginAnonymous = exports.calculateSha384 = exports.fetchRemoteContent = exports.readLocalContent = exports.getContent = exports.toHtmlWithSri = exports.isSriTag = exports.extractLinkRel = void 0;
|
|
36
|
+
exports.handleUpdatedHtml = exports.toSriScriptTag = exports.ensureCrossoriginAnonymous = exports.calculateSha384 = exports.fetchRemoteContent = exports.readLocalContent = exports.getContent = exports.toHtmlWithSri = exports.isSriTag = exports.extractLinkRel = exports.toSriImportMap = exports.extractImports = exports.parseImportMap = exports.isImportMapTag = void 0;
|
|
37
37
|
const fs = __importStar(require("node:fs"));
|
|
38
38
|
const path = __importStar(require("node:path"));
|
|
39
39
|
const node_crypto_1 = require("node:crypto");
|
|
@@ -93,18 +93,88 @@ const isSriTag = (scriptOrLinkTag) => {
|
|
|
93
93
|
return false;
|
|
94
94
|
};
|
|
95
95
|
exports.isSriTag = isSriTag;
|
|
96
|
+
const isImportMapTag = (scriptOrLinkTag) => {
|
|
97
|
+
return scriptOrLinkTag.startsWith("<script ") && scriptOrLinkTag.includes('type="importmap"');
|
|
98
|
+
};
|
|
99
|
+
exports.isImportMapTag = isImportMapTag;
|
|
100
|
+
const parseImportMap = (scriptOrLinkTag) => {
|
|
101
|
+
// Extract content between script tags
|
|
102
|
+
const contentRegex = /<script[^>]*>([\s\S]*?)<\/script>/;
|
|
103
|
+
const contentMatch = scriptOrLinkTag.match(contentRegex);
|
|
104
|
+
if (!contentMatch || !contentMatch[1]) {
|
|
105
|
+
throw new Error(`Failed to parse import map for tag ${scriptOrLinkTag}`);
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const content = contentMatch[1].trim();
|
|
109
|
+
// Parse the import map JSON
|
|
110
|
+
const importMapJson = JSON.parse(content);
|
|
111
|
+
if (typeof importMapJson !== 'object' || Array.isArray(importMapJson)) {
|
|
112
|
+
throw new Error(`Failed to parse import map for tag ${scriptOrLinkTag}`);
|
|
113
|
+
}
|
|
114
|
+
return importMapJson;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
throw new Error(`Failed to parse import map for tag ${scriptOrLinkTag}`);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
exports.parseImportMap = parseImportMap;
|
|
121
|
+
const extractImports = (importMapJson) => {
|
|
122
|
+
const imports = importMapJson.imports ?
|
|
123
|
+
Object.keys(importMapJson.imports).map(key => {
|
|
124
|
+
const src = importMapJson.imports[key];
|
|
125
|
+
return {
|
|
126
|
+
src,
|
|
127
|
+
oldHash: importMapJson.integrity?.[src]
|
|
128
|
+
};
|
|
129
|
+
}) : [];
|
|
130
|
+
return imports;
|
|
131
|
+
};
|
|
132
|
+
exports.extractImports = extractImports;
|
|
133
|
+
const toSriImportMap = (tag, importMapJson, newIntegrityMap) => {
|
|
134
|
+
const newImportMap = JSON.stringify({
|
|
135
|
+
...importMapJson,
|
|
136
|
+
integrity: newIntegrityMap,
|
|
137
|
+
});
|
|
138
|
+
return tag.replace(/<script([^>]*)>([\s\S]*?)<\/script>/, (_, attributes, _content) => {
|
|
139
|
+
return `<script${attributes}>${newImportMap}</script>`;
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
exports.toSriImportMap = toSriImportMap;
|
|
96
143
|
const toHtmlWithSri = async (htmlContent, baseDir, baseUrl, noRemote, verify) => {
|
|
97
144
|
// Find all script and link tags that should have integrity hashes
|
|
98
|
-
const re = /<(script|link)\s+[^>]*(?:src|href)="([^"]+)"[^>]
|
|
145
|
+
const re = /<(script|link)\s+[^>]*(?:src|href)="?([^"]+)?"?[^>]*>(?:(.*?)<\/\1>)?|<script\s+[^>]*type="importmap"[^>]*>([\s\S]*?)<\/script>/g;
|
|
99
146
|
let updatedHtml = htmlContent;
|
|
100
147
|
const matches = htmlContent.matchAll(re);
|
|
101
|
-
for (const
|
|
148
|
+
for (const match of matches) {
|
|
149
|
+
const [scriptOrLinkTag, _, src] = match;
|
|
102
150
|
// Skip non supported resources
|
|
103
151
|
if (!isSriTag(scriptOrLinkTag)) {
|
|
104
152
|
continue;
|
|
105
153
|
}
|
|
106
154
|
// Get content of script or link
|
|
107
155
|
try {
|
|
156
|
+
if ((0, exports.isImportMapTag)(scriptOrLinkTag)) {
|
|
157
|
+
const importMapJson = (0, exports.parseImportMap)(scriptOrLinkTag);
|
|
158
|
+
const imports = (0, exports.extractImports)(importMapJson);
|
|
159
|
+
const newIntegrityMap = {};
|
|
160
|
+
for (const { src, oldHash } of imports) {
|
|
161
|
+
const content = await getContent(src, baseDir, baseUrl, noRemote);
|
|
162
|
+
const hashHex = calculateSha384(content);
|
|
163
|
+
const integrity = `sha384-${hashHex}`;
|
|
164
|
+
if (verify) {
|
|
165
|
+
if (!oldHash) {
|
|
166
|
+
throw new Error(`Missing hash for ${src}, expected ${integrity}`);
|
|
167
|
+
}
|
|
168
|
+
if (oldHash !== integrity) {
|
|
169
|
+
throw new Error(`Invalid hash ${oldHash} for ${src}, expected ${integrity}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
newIntegrityMap[src] = integrity;
|
|
173
|
+
}
|
|
174
|
+
const sriImportMapTag = (0, exports.toSriImportMap)(scriptOrLinkTag, importMapJson, newIntegrityMap);
|
|
175
|
+
updatedHtml = updatedHtml.replace(scriptOrLinkTag, sriImportMapTag);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
108
178
|
const content = await getContent(src, baseDir, baseUrl, noRemote);
|
|
109
179
|
// Calculate SHA-384 hash and create integrity attribute value
|
|
110
180
|
const hashHex = calculateSha384(content);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dintero/sri-to-dist",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "HTML tool for adding subresource integrity hashes",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@types/temp": "0.9.4",
|
|
38
38
|
"semantic-release": "24.2.3",
|
|
39
39
|
"temp": "0.9.4",
|
|
40
|
-
"typescript": "5.8.
|
|
40
|
+
"typescript": "5.8.3",
|
|
41
41
|
"vitest": "3.1.1"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
package/dist/package.json
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@dintero/sri-to-dist",
|
|
3
|
-
"version": "1.0.3",
|
|
4
|
-
"description": "HTML tool for adding subresource integrity hashes",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"bin": {
|
|
8
|
-
"sri-to-dist": "bin/sri-to-dist.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"dist",
|
|
12
|
-
"bin"
|
|
13
|
-
],
|
|
14
|
-
"scripts": {
|
|
15
|
-
"build": "tsc",
|
|
16
|
-
"prepare": "npm run build",
|
|
17
|
-
"start": "node bin/sri-to-dist.js",
|
|
18
|
-
"test": "vitest run",
|
|
19
|
-
"test:watch": "vitest",
|
|
20
|
-
"lint": "biome lint .",
|
|
21
|
-
"format": "biome format . --write",
|
|
22
|
-
"prepublishOnly": "yarn run build"
|
|
23
|
-
},
|
|
24
|
-
"keywords": [
|
|
25
|
-
"subresource",
|
|
26
|
-
"integrity",
|
|
27
|
-
"sri",
|
|
28
|
-
"html",
|
|
29
|
-
"security"
|
|
30
|
-
],
|
|
31
|
-
"homepage": "https://github.com/Dintero/sri-to-dist?tab=readme-ov-file#sri-to-dist",
|
|
32
|
-
"author": "Sven Nicolai Viig <sven@dintero.com> (http://dintero.com)",
|
|
33
|
-
"license": "MIT",
|
|
34
|
-
"devDependencies": {
|
|
35
|
-
"@biomejs/biome": "1.9.4",
|
|
36
|
-
"@types/node": "22.14.0",
|
|
37
|
-
"@types/temp": "0.9.4",
|
|
38
|
-
"semantic-release": "24.2.3",
|
|
39
|
-
"temp": "0.9.4",
|
|
40
|
-
"typescript": "5.8.2",
|
|
41
|
-
"vitest": "3.1.1"
|
|
42
|
-
},
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"commander": "13.1.0"
|
|
45
|
-
},
|
|
46
|
-
"bugs": {
|
|
47
|
-
"url": "https://github.com/Dintero/sri-to-dist/issues"
|
|
48
|
-
},
|
|
49
|
-
"private": false,
|
|
50
|
-
"publishConfig": {
|
|
51
|
-
"access": "public"
|
|
52
|
-
},
|
|
53
|
-
"repository": {
|
|
54
|
-
"type": "git",
|
|
55
|
-
"url": "https://github.com/Dintero/sri-to-dist.git"
|
|
56
|
-
}
|
|
57
|
-
}
|
|
File without changes
|
|
File without changes
|