@dk/hipp 0.1.13 → 0.1.14
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 +3 -11
- package/hipp.js +30 -72
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -175,16 +175,8 @@ PERFORMANCE OF THIS SOFTWARE.
|
|
|
175
175
|
```json
|
|
176
176
|
{
|
|
177
177
|
"origin": "git@github.com:dmytri/hipp.git",
|
|
178
|
-
"tag": "v0.1.
|
|
178
|
+
"tag": "v0.1.14",
|
|
179
|
+
"hash": "c2a32be7dbb481c7f9e9444b993616bb1e10d41cf9c0b787642873833ebcc77e",
|
|
180
|
+
"signature": "RsopqB7L4VWwi1jBmG1dG6L+Zo0ZV7m5k6jfA+KwafViXgWw6KQniJcCs8aqgujarAANd5AzyHBdlWCNoIFzBg=="
|
|
179
181
|
}
|
|
180
182
|
```
|
|
181
|
-
|
|
182
|
-
```npx @dk/hipp @dk/hipp@0.1.13```
|
|
183
|
-
<!-- HIPP-MANIFEST -->
|
|
184
|
-
```json
|
|
185
|
-
{
|
|
186
|
-
"hash": "0157b775d49e9715e954bc9838f5017632c0fd2e2418d814b0e2bbb92abe3fc3",
|
|
187
|
-
"signature": "jyq8xJjFAt5HtqQWPPHw52A2ph7zVxEYD1QClEY3jRFcVFdUlaJj+sGatgP/FChjaI57HZM8flYUvl2r1DA5Dw=="
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
<!-- /HIPP-MANIFEST -->
|
package/hipp.js
CHANGED
|
@@ -123,46 +123,7 @@ function buildSignData(hash, origin, tag) {
|
|
|
123
123
|
return `${hash}\n${origin}\n${tag}\n`;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
const MANIFEST_END = '<!-- /HIPP-MANIFEST -->';
|
|
128
|
-
|
|
129
|
-
function appendManifestToReadme(readmeContent, manifest) {
|
|
130
|
-
return `${readmeContent}${MANIFEST_START}\n\`\`\`json\n${manifest}\n\`\`\`\n${MANIFEST_END}\n`;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function extractManifestFromReadme(readmeContent) {
|
|
134
|
-
const startIdx = readmeContent.indexOf(MANIFEST_START);
|
|
135
|
-
if (startIdx === -1) return null;
|
|
136
|
-
const endIdx = readmeContent.indexOf(MANIFEST_END, startIdx);
|
|
137
|
-
if (endIdx === -1) return null;
|
|
138
|
-
const content = readmeContent.slice(startIdx + MANIFEST_START.length, endIdx).trim();
|
|
139
|
-
const match = content.match(/^```json\n(.+)\n```$/s);
|
|
140
|
-
if (!match) return null;
|
|
141
|
-
return match[1];
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function stripManifestFromReadme(readmeContent) {
|
|
145
|
-
const startIdx = readmeContent.indexOf(MANIFEST_START);
|
|
146
|
-
const endIdx = readmeContent.indexOf(MANIFEST_END, startIdx);
|
|
147
|
-
if (startIdx === -1 || endIdx === -1) return readmeContent;
|
|
148
|
-
|
|
149
|
-
const endLineIdx = readmeContent.indexOf('\n', endIdx);
|
|
150
|
-
const endOfManifest = endLineIdx !== -1 ? endLineIdx + 1 : readmeContent.length;
|
|
151
|
-
|
|
152
|
-
const beforeWithNewline = readmeContent.slice(0, startIdx);
|
|
153
|
-
const lastNewlineBefore = beforeWithNewline.lastIndexOf('\n');
|
|
154
|
-
const before = lastNewlineBefore !== -1 ? beforeWithNewline.slice(0, lastNewlineBefore) : beforeWithNewline;
|
|
155
|
-
|
|
156
|
-
const after = readmeContent.slice(endOfManifest);
|
|
157
|
-
return before + '\n' + after;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function computeReadmeHash(readmeContent) {
|
|
161
|
-
const stripped = stripManifestFromReadme(readmeContent);
|
|
162
|
-
return sha256(stripped);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function extractJsonMetaFromReadme(readmeContent) {
|
|
126
|
+
function findLastJsonBlock(readmeContent) {
|
|
166
127
|
const lines = readmeContent.split('\n');
|
|
167
128
|
let jsonStart = -1;
|
|
168
129
|
let braceCount = 0;
|
|
@@ -186,7 +147,7 @@ function extractJsonMetaFromReadme(readmeContent) {
|
|
|
186
147
|
const jsonStr = lines.slice(jsonStart, i + 1).join('\n');
|
|
187
148
|
try {
|
|
188
149
|
const parsed = JSON.parse(jsonStr);
|
|
189
|
-
if (parsed.origin && parsed.tag) {
|
|
150
|
+
if (parsed.origin && parsed.tag && parsed.hash && parsed.signature) {
|
|
190
151
|
lastValid = parsed;
|
|
191
152
|
}
|
|
192
153
|
} catch {
|
|
@@ -199,6 +160,14 @@ function extractJsonMetaFromReadme(readmeContent) {
|
|
|
199
160
|
return lastValid;
|
|
200
161
|
}
|
|
201
162
|
|
|
163
|
+
function computeReadmeHash(readmeContent) {
|
|
164
|
+
const jsonBlock = findLastJsonBlock(readmeContent);
|
|
165
|
+
if (!jsonBlock) return sha256(readmeContent);
|
|
166
|
+
const jsonStr = JSON.stringify(jsonBlock, null, 2);
|
|
167
|
+
const beforeJson = readmeContent.split('```json')[0];
|
|
168
|
+
return sha256(beforeJson + '```json\n' + jsonStr + '\n```\n');
|
|
169
|
+
}
|
|
170
|
+
|
|
202
171
|
function safeStageName(name) {
|
|
203
172
|
return name.replace(/[^a-zA-Z0-9._-]/g, '-');
|
|
204
173
|
}
|
|
@@ -490,23 +459,12 @@ async function runVerify(packageSpec) {
|
|
|
490
459
|
}
|
|
491
460
|
|
|
492
461
|
const stagedReadme = fs.readFileSync(stagedReadmePath, 'utf8');
|
|
493
|
-
const
|
|
494
|
-
if (!
|
|
495
|
-
fail(`❌
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
const manifestStr = extractManifestFromReadme(stagedReadme);
|
|
499
|
-
if (!manifestStr) {
|
|
500
|
-
fail(`❌ Manifest not found in README`);
|
|
462
|
+
const manifest = findLastJsonBlock(stagedReadme);
|
|
463
|
+
if (!manifest || !manifest.origin || !manifest.tag || !manifest.hash || !manifest.signature) {
|
|
464
|
+
fail(`❌ Manifest not found or invalid in README`);
|
|
501
465
|
}
|
|
502
466
|
|
|
503
|
-
const
|
|
504
|
-
if (!manifest || !manifest.hash || !manifest.signature) {
|
|
505
|
-
fail(`❌ Invalid manifest format`);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
const originUrl = jsonMeta.origin;
|
|
509
|
-
const tag = jsonMeta.tag;
|
|
467
|
+
const { origin: originUrl, tag, hash: npmHash, signature } = manifest;
|
|
510
468
|
|
|
511
469
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `hipp-verify-git-`));
|
|
512
470
|
const stageDir = fs.mkdtempSync(path.join(os.tmpdir(), `hipp-verify-stage-`));
|
|
@@ -531,21 +489,16 @@ async function runVerify(packageSpec) {
|
|
|
531
489
|
fail(`❌ README.md not found in git at tag ${tag}`);
|
|
532
490
|
}
|
|
533
491
|
|
|
534
|
-
|
|
535
|
-
const verifyBlock = '```npx @dk/hipp ' + pkgName + '@' + tag + '```';
|
|
536
|
-
const jsonMetaStr = '```json\n{\n "origin": "' + originUrl + '",\n "tag": "' + tag + '"\n}\n```';
|
|
537
|
-
stagedReadme = stagedReadme.trimEnd() + '\n\n' + jsonMetaStr + '\n\n' + verifyBlock + '\n';
|
|
538
|
-
|
|
539
|
-
const stagedHash = computeReadmeHash(stagedReadme);
|
|
492
|
+
const stagedHash = sha256(fs.readFileSync(stagedReadmePath, 'utf8'));
|
|
540
493
|
|
|
541
|
-
if (stagedHash !==
|
|
494
|
+
if (stagedHash !== npmHash) {
|
|
542
495
|
fail(`❌ Hash mismatch: git content does not match npm manifest`);
|
|
543
496
|
}
|
|
544
497
|
|
|
545
|
-
log.success(`🔒 Content hash verified: ${
|
|
498
|
+
log.success(`🔒 Content hash verified: ${npmHash.slice(0, 12)}...`);
|
|
546
499
|
|
|
547
|
-
const signData = buildSignData(
|
|
548
|
-
const signatureValid = verifySignature(signData,
|
|
500
|
+
const signData = buildSignData(npmHash, originUrl, tag);
|
|
501
|
+
const signatureValid = verifySignature(signData, signature, publicKey);
|
|
549
502
|
|
|
550
503
|
if (!signatureValid) {
|
|
551
504
|
fail(`❌ Signature verification failed`);
|
|
@@ -653,15 +606,20 @@ async function run() {
|
|
|
653
606
|
stagedReadme = fs.readFileSync(stagedReadmePath, 'utf8');
|
|
654
607
|
}
|
|
655
608
|
|
|
656
|
-
|
|
657
|
-
const jsonMetaStr = '```json\n{\n "origin": "' + provenance.remoteUrl + '",\n "tag": "' + rawTag + '"\n}\n```';
|
|
658
|
-
stagedReadme = stagedReadme.trimEnd() + '\n\n' + jsonMetaStr + '\n\n' + verifyBlock + '\n';
|
|
609
|
+
stagedReadme = stagedReadme.trimEnd() + '\n\n';
|
|
659
610
|
|
|
660
|
-
const readmeHash =
|
|
611
|
+
const readmeHash = sha256(stagedReadme);
|
|
661
612
|
const dataToSign = buildSignData(readmeHash, provenance.remoteUrl, rawTag);
|
|
662
613
|
const signature = signContent(dataToSign, privateKey);
|
|
663
|
-
|
|
664
|
-
|
|
614
|
+
|
|
615
|
+
const manifestJson = {
|
|
616
|
+
origin: provenance.remoteUrl,
|
|
617
|
+
tag: rawTag,
|
|
618
|
+
hash: readmeHash,
|
|
619
|
+
signature: signature,
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
stagedReadme += '```json\n' + JSON.stringify(manifestJson, null, 2) + '\n```\n';
|
|
665
623
|
|
|
666
624
|
fs.writeFileSync(stagedReadmePath, stagedReadme);
|
|
667
625
|
|