@dk/hipp 0.1.12 → 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 +33 -74
- 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.12```
|
|
183
|
-
<!-- HIPP-MANIFEST -->
|
|
184
|
-
```json
|
|
185
|
-
{
|
|
186
|
-
"hash": "9213a0bc269441f5dc3dc07fe07eba4d127815ae78f0d7e07474547bb342ce1b",
|
|
187
|
-
"signature": "Bg5/in81S3ia4x4W2C1WzvXeZPxspCdXEcSHCyLT3aUaEF5JrSANcIerZXrgbAvDyrxsf9O2wDJ/0cZNI4KOAA=="
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
<!-- /HIPP-MANIFEST -->
|
package/hipp.js
CHANGED
|
@@ -123,50 +123,12 @@ 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;
|
|
169
130
|
let inJson = false;
|
|
131
|
+
let lastValid = null;
|
|
170
132
|
|
|
171
133
|
for (let i = 0; i < lines.length; i++) {
|
|
172
134
|
const line = lines[i];
|
|
@@ -185,8 +147,8 @@ function extractJsonMetaFromReadme(readmeContent) {
|
|
|
185
147
|
const jsonStr = lines.slice(jsonStart, i + 1).join('\n');
|
|
186
148
|
try {
|
|
187
149
|
const parsed = JSON.parse(jsonStr);
|
|
188
|
-
if (parsed.origin && parsed.tag) {
|
|
189
|
-
|
|
150
|
+
if (parsed.origin && parsed.tag && parsed.hash && parsed.signature) {
|
|
151
|
+
lastValid = parsed;
|
|
190
152
|
}
|
|
191
153
|
} catch {
|
|
192
154
|
// continue searching
|
|
@@ -195,7 +157,15 @@ function extractJsonMetaFromReadme(readmeContent) {
|
|
|
195
157
|
}
|
|
196
158
|
}
|
|
197
159
|
}
|
|
198
|
-
return
|
|
160
|
+
return lastValid;
|
|
161
|
+
}
|
|
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');
|
|
199
169
|
}
|
|
200
170
|
|
|
201
171
|
function safeStageName(name) {
|
|
@@ -489,23 +459,12 @@ async function runVerify(packageSpec) {
|
|
|
489
459
|
}
|
|
490
460
|
|
|
491
461
|
const stagedReadme = fs.readFileSync(stagedReadmePath, 'utf8');
|
|
492
|
-
const
|
|
493
|
-
if (!
|
|
494
|
-
fail(`❌
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
const manifestStr = extractManifestFromReadme(stagedReadme);
|
|
498
|
-
if (!manifestStr) {
|
|
499
|
-
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`);
|
|
500
465
|
}
|
|
501
466
|
|
|
502
|
-
const
|
|
503
|
-
if (!manifest || !manifest.hash || !manifest.signature) {
|
|
504
|
-
fail(`❌ Invalid manifest format`);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
const originUrl = jsonMeta.origin;
|
|
508
|
-
const tag = jsonMeta.tag;
|
|
467
|
+
const { origin: originUrl, tag, hash: npmHash, signature } = manifest;
|
|
509
468
|
|
|
510
469
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `hipp-verify-git-`));
|
|
511
470
|
const stageDir = fs.mkdtempSync(path.join(os.tmpdir(), `hipp-verify-stage-`));
|
|
@@ -530,21 +489,16 @@ async function runVerify(packageSpec) {
|
|
|
530
489
|
fail(`❌ README.md not found in git at tag ${tag}`);
|
|
531
490
|
}
|
|
532
491
|
|
|
533
|
-
|
|
534
|
-
const verifyBlock = '```npx @dk/hipp ' + pkgName + '@' + tag + '```';
|
|
535
|
-
const jsonMetaStr = '```json\n{\n "origin": "' + originUrl + '",\n "tag": "' + tag + '"\n}\n```';
|
|
536
|
-
stagedReadme = stagedReadme.trimEnd() + '\n\n' + jsonMetaStr + '\n\n' + verifyBlock + '\n';
|
|
537
|
-
|
|
538
|
-
const stagedHash = computeReadmeHash(stagedReadme);
|
|
492
|
+
const stagedHash = sha256(fs.readFileSync(stagedReadmePath, 'utf8'));
|
|
539
493
|
|
|
540
|
-
if (stagedHash !==
|
|
494
|
+
if (stagedHash !== npmHash) {
|
|
541
495
|
fail(`❌ Hash mismatch: git content does not match npm manifest`);
|
|
542
496
|
}
|
|
543
497
|
|
|
544
|
-
log.success(`🔒 Content hash verified: ${
|
|
498
|
+
log.success(`🔒 Content hash verified: ${npmHash.slice(0, 12)}...`);
|
|
545
499
|
|
|
546
|
-
const signData = buildSignData(
|
|
547
|
-
const signatureValid = verifySignature(signData,
|
|
500
|
+
const signData = buildSignData(npmHash, originUrl, tag);
|
|
501
|
+
const signatureValid = verifySignature(signData, signature, publicKey);
|
|
548
502
|
|
|
549
503
|
if (!signatureValid) {
|
|
550
504
|
fail(`❌ Signature verification failed`);
|
|
@@ -652,15 +606,20 @@ async function run() {
|
|
|
652
606
|
stagedReadme = fs.readFileSync(stagedReadmePath, 'utf8');
|
|
653
607
|
}
|
|
654
608
|
|
|
655
|
-
|
|
656
|
-
const jsonMetaStr = '```json\n{\n "origin": "' + provenance.remoteUrl + '",\n "tag": "' + rawTag + '"\n}\n```';
|
|
657
|
-
stagedReadme = stagedReadme.trimEnd() + '\n\n' + jsonMetaStr + '\n\n' + verifyBlock + '\n';
|
|
609
|
+
stagedReadme = stagedReadme.trimEnd() + '\n\n';
|
|
658
610
|
|
|
659
|
-
const readmeHash =
|
|
611
|
+
const readmeHash = sha256(stagedReadme);
|
|
660
612
|
const dataToSign = buildSignData(readmeHash, provenance.remoteUrl, rawTag);
|
|
661
613
|
const signature = signContent(dataToSign, privateKey);
|
|
662
|
-
|
|
663
|
-
|
|
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';
|
|
664
623
|
|
|
665
624
|
fs.writeFileSync(stagedReadmePath, stagedReadme);
|
|
666
625
|
|