@dk/hipp 0.1.29 ā 0.1.30
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 +8 -6
- package/hipp.js +105 -66
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -135,6 +135,7 @@ The manifest contains:
|
|
|
135
135
|
"email": "jane@example.com",
|
|
136
136
|
"npm": "10.2.4",
|
|
137
137
|
"node": "v20.11.0",
|
|
138
|
+
"git": "2.34.1",
|
|
138
139
|
"hipp": "0.1.22"
|
|
139
140
|
}
|
|
140
141
|
```
|
|
@@ -241,20 +242,21 @@ PERFORMANCE OF THIS SOFTWARE.
|
|
|
241
242
|
Verify this package with [@dk/hipp](https://www.npmjs.com/package/@dk/hipp):
|
|
242
243
|
|
|
243
244
|
```bash
|
|
244
|
-
npx @dk/hipp verify @dk/hipp@0.1.
|
|
245
|
+
npx @dk/hipp verify @dk/hipp@0.1.30
|
|
245
246
|
```
|
|
246
247
|
|
|
247
248
|
```json
|
|
248
249
|
{
|
|
249
250
|
"origin": "git@github.com:dmytri/hipp.git",
|
|
250
|
-
"tag": "v0.1.
|
|
251
|
-
"revision": "
|
|
252
|
-
"hash": "
|
|
253
|
-
"signature": "
|
|
251
|
+
"tag": "v0.1.30",
|
|
252
|
+
"revision": "0d839e7b5eaba3c88a61c64c4524d3552c76140c",
|
|
253
|
+
"hash": "9005dcdfea5d0d29d28d74fea47954a1c367a2730f7ddb36e89f8683785a3c4a",
|
|
254
|
+
"signature": "XKG7xLbzCDxYWC2V+RhEoUWyU0Wp4F6ytufa4NJ6824Sl17VR6Abe/Aw/VS1MfOZFxifJrWGB0e8MYdZ1hCaBg==",
|
|
254
255
|
"name": "Dmytri Kleiner",
|
|
255
256
|
"email": "dev@dmytri.to",
|
|
256
257
|
"npm": "11.12.1",
|
|
257
258
|
"node": "v25.8.2",
|
|
258
|
-
"
|
|
259
|
+
"git": "git version 2.47.3",
|
|
260
|
+
"hipp": "0.1.30"
|
|
259
261
|
}
|
|
260
262
|
```
|
package/hipp.js
CHANGED
|
@@ -483,12 +483,20 @@ async function runVerify(packageSpec) {
|
|
|
483
483
|
fail(`ā Manifest not found or invalid in README`);
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
-
const { origin: originUrl, tag, revision, signature, name, email, npm: npmVer, node: nodeVer, hipp: hippVer } = manifest;
|
|
486
|
+
const { origin: originUrl, tag, revision, signature, name, email, npm: npmVer, node: nodeVer, hipp: hippVer, git: gitVer } = manifest;
|
|
487
487
|
|
|
488
488
|
log.info(`šæ Cloning git origin at tag ${tag}...`);
|
|
489
489
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `hipp-verify-git-`));
|
|
490
490
|
const stageDir = fs.mkdtempSync(path.join(os.tmpdir(), `hipp-verify-stage-`));
|
|
491
491
|
|
|
492
|
+
const results = {
|
|
493
|
+
revision: false,
|
|
494
|
+
pub: false,
|
|
495
|
+
signature: false,
|
|
496
|
+
manifestHash: false,
|
|
497
|
+
rebuild: false,
|
|
498
|
+
};
|
|
499
|
+
|
|
492
500
|
try {
|
|
493
501
|
let cloneResult;
|
|
494
502
|
try {
|
|
@@ -505,81 +513,110 @@ async function runVerify(packageSpec) {
|
|
|
505
513
|
|
|
506
514
|
const clonedRevision = git(['rev-parse', 'HEAD'], { cwd: tmpDir });
|
|
507
515
|
if (clonedRevision !== revision) {
|
|
508
|
-
|
|
516
|
+
log.error(`ā Revision mismatch: manifest ${revision.slice(0, 12)} != cloned ${clonedRevision.slice(0, 12)}`);
|
|
517
|
+
} else {
|
|
518
|
+
log.success(`š·ļø Revision verified: ${revision.slice(0, 12)}...`);
|
|
519
|
+
results.revision = true;
|
|
509
520
|
}
|
|
510
|
-
log.success(`š·ļø Revision verified: ${revision.slice(0, 12)}...`);
|
|
511
521
|
|
|
512
522
|
const publicKeyPath = path.join(tmpDir, 'hipp.pub');
|
|
513
523
|
if (!fs.existsSync(publicKeyPath)) {
|
|
514
|
-
|
|
515
|
-
}
|
|
524
|
+
log.error(`ā hipp.pub not found in git at tag ${tag}`);
|
|
525
|
+
} else {
|
|
526
|
+
const publicKey = fs.readFileSync(publicKeyPath, 'utf8');
|
|
516
527
|
|
|
517
|
-
|
|
528
|
+
log.info(`šļø Staging git files...`);
|
|
529
|
+
const trackedFiles = getTrackedFilesFromDir(tmpDir);
|
|
530
|
+
copyTrackedFilesFromDir(stageDir, tmpDir, trackedFiles);
|
|
518
531
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
532
|
+
log.info(`š¦ Packing clean git files...`);
|
|
533
|
+
const { tarballHash: cleanHash } = packAndHash(stageDir);
|
|
534
|
+
log.success(`š¦ Clean hash: ${cleanHash.slice(0, 12)}...`);
|
|
522
535
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
536
|
+
log.info(`š Check 2: Verifying manifest hash...`);
|
|
537
|
+
if (cleanHash !== manifest.hash) {
|
|
538
|
+
log.error(`ā Manifest hash mismatch: clean ${cleanHash.slice(0, 12)} != manifest ${manifest.hash.slice(0, 12)}`);
|
|
539
|
+
} else {
|
|
540
|
+
log.success(`š Manifest hash verified`);
|
|
541
|
+
results.manifestHash = true;
|
|
542
|
+
}
|
|
526
543
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
544
|
+
log.info(`š Check 1: Verifying signature...`);
|
|
545
|
+
const signData = buildSignData(manifest.hash, originUrl, tag, revision, name, email);
|
|
546
|
+
const signatureValid = verifySignature(signData, signature, publicKey);
|
|
547
|
+
if (!signatureValid) {
|
|
548
|
+
log.error(`ā Signature verification failed`);
|
|
549
|
+
} else {
|
|
550
|
+
log.success(`š Signature verified`);
|
|
551
|
+
results.signature = true;
|
|
552
|
+
results.pub = true;
|
|
553
|
+
}
|
|
532
554
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
log.success(`š¦ Rebuild hash: ${rebuildHash.slice(0, 12)}...`);
|
|
563
|
-
|
|
564
|
-
if (rebuildHash !== npmHash) {
|
|
565
|
-
log.error(`ā Rebuild mismatch!`);
|
|
566
|
-
log.error(` NPM tarball: ${npmHash}`);
|
|
567
|
-
log.error(` Git rebuild: ${rebuildHash}`);
|
|
568
|
-
fail(`ā Package integrity compromised`);
|
|
555
|
+
log.info(`š Check 3: Rebuilding from source...`);
|
|
556
|
+
const stagedReadmePath = path.join(stageDir, 'README.md');
|
|
557
|
+
let stagedReadme = fs.readFileSync(stagedReadmePath, 'utf8');
|
|
558
|
+
const tagVersion = semver.clean(tag);
|
|
559
|
+
if (!tagVersion) {
|
|
560
|
+
log.error(`ā Tag ${tag} is not valid semver`);
|
|
561
|
+
}
|
|
562
|
+
stagedReadme = stagedReadme.trimEnd() + '\n\n## Verify\n\n' +
|
|
563
|
+
'Verify this package with [@dk/hipp](https://www.npmjs.com/package/@dk/hipp):\n\n' +
|
|
564
|
+
'```bash\n' +
|
|
565
|
+
`npx @dk/hipp verify ${pkgName}@${tagVersion}\n` +
|
|
566
|
+
'```\n\n' +
|
|
567
|
+
'```json\n' + JSON.stringify(manifest, null, 2) + '\n```\n';
|
|
568
|
+
fs.writeFileSync(stagedReadmePath, stagedReadme);
|
|
569
|
+
|
|
570
|
+
const stagedPkgPath = path.join(stageDir, 'package.json');
|
|
571
|
+
const stagedPkg = JSON.parse(fs.readFileSync(stagedPkgPath, 'utf8'));
|
|
572
|
+
stagedPkg.version = tagVersion;
|
|
573
|
+
fs.writeFileSync(stagedPkgPath, JSON.stringify(stagedPkg, null, 2) + '\n');
|
|
574
|
+
|
|
575
|
+
const { tarballHash: rebuildHash } = packAndHash(stageDir);
|
|
576
|
+
log.success(`š¦ Rebuild hash: ${rebuildHash.slice(0, 12)}...`);
|
|
577
|
+
|
|
578
|
+
if (rebuildHash !== npmHash) {
|
|
579
|
+
log.error(`ā Rebuild mismatch: rebuild ${rebuildHash.slice(0, 12)} != npm ${npmHash.slice(0, 12)}`);
|
|
580
|
+
} else {
|
|
581
|
+
log.success(`š Rebuild verified`);
|
|
582
|
+
results.rebuild = true;
|
|
583
|
+
}
|
|
569
584
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
585
|
+
|
|
586
|
+
const allPassed = results.revision && results.pub && results.signature && results.manifestHash && results.rebuild;
|
|
587
|
+
|
|
588
|
+
if (allPassed) {
|
|
589
|
+
log.success(`\nā
Verified: all checks passed`);
|
|
590
|
+
log.info(`š Publisher: ${name} <${email}>`);
|
|
591
|
+
log.info(`š Origin: ${originUrl}`);
|
|
592
|
+
log.info(`š Tag: ${tag}`);
|
|
593
|
+
if (npmVer || nodeVer || hippVer || gitVer) {
|
|
594
|
+
const parts = [];
|
|
595
|
+
const displayHipp = hippVer === '0.0.0' ? tagVersion : hippVer;
|
|
596
|
+
if (hippVer) parts.push(`hipp: ${displayHipp}`);
|
|
597
|
+
if (npmVer) parts.push(`npm: ${npmVer}`);
|
|
598
|
+
if (nodeVer) parts.push(`node: ${nodeVer}`);
|
|
599
|
+
if (gitVer) parts.push(`git: ${gitVer}`);
|
|
600
|
+
log.info(`ā¹ļø ${parts.join(' | ')}`);
|
|
601
|
+
}
|
|
602
|
+
log.info(`\nThis proves npm matches git. It does NOT prove:`);
|
|
603
|
+
log.info(` - The code is safe or bug-free`);
|
|
604
|
+
log.info(` - The publisher is trustworthy`);
|
|
605
|
+
log.info(` - The name/email is accurate`);
|
|
606
|
+
} else {
|
|
607
|
+
log.error(`\nā Verification failed.`);
|
|
608
|
+
if (results.revision && results.pub && results.signature) {
|
|
609
|
+
log.info(`\nRevision and signature verified. This failure may be due to tool`);
|
|
610
|
+
log.info(`version differences between publishing and verification environments.`);
|
|
611
|
+
if (npmVer || nodeVer || hippVer || gitVer) {
|
|
612
|
+
log.info(`\nPublished with: ${[
|
|
613
|
+
hippVer && `hipp: ${hippVer}`,
|
|
614
|
+
npmVer && `npm: ${npmVer}`,
|
|
615
|
+
nodeVer && `node: ${nodeVer}`,
|
|
616
|
+
gitVer && `git: ${gitVer}`,
|
|
617
|
+
].filter(Boolean).join(' | ')}`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
583
620
|
}
|
|
584
621
|
} finally {
|
|
585
622
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
@@ -682,6 +719,7 @@ async function run() {
|
|
|
682
719
|
const revision = refInfo.head;
|
|
683
720
|
const npmVersion = runCmd('npm', ['--version']).stdout.trim();
|
|
684
721
|
const nodeVersion = process.version;
|
|
722
|
+
const gitVersion = runCmd('git', ['--version']).stdout.trim();
|
|
685
723
|
const hippPkgPath = path.join(path.dirname(process.argv[1]), 'package.json');
|
|
686
724
|
const hippPkg = JSON.parse(fs.readFileSync(hippPkgPath, 'utf8'));
|
|
687
725
|
const hippVersion = hippPkg.version === '0.0.0' ? version : hippPkg.version;
|
|
@@ -699,6 +737,7 @@ async function run() {
|
|
|
699
737
|
email: email,
|
|
700
738
|
npm: npmVersion,
|
|
701
739
|
node: nodeVersion,
|
|
740
|
+
git: gitVersion,
|
|
702
741
|
hipp: hippVersion,
|
|
703
742
|
};
|
|
704
743
|
|