@dk/hipp 0.1.17 â 0.1.20
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 +30 -15
- package/hipp.js +37 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -109,32 +109,36 @@ The manifest contains:
|
|
|
109
109
|
{
|
|
110
110
|
"origin": "git@github.com:dk/your-package.git",
|
|
111
111
|
"tag": "v1.0.0",
|
|
112
|
+
"revision": "<git-commit-hash>",
|
|
112
113
|
"hash": "<sha256-of-tarball>",
|
|
113
114
|
"signature": "<base64-ed25519-signature>",
|
|
114
115
|
"name": "Jane Developer",
|
|
115
|
-
"email": "jane@example.com"
|
|
116
|
+
"email": "jane@example.com",
|
|
117
|
+
"npm": "10.2.4",
|
|
118
|
+
"node": "v20.11.0"
|
|
116
119
|
}
|
|
117
120
|
```
|
|
118
121
|
|
|
119
122
|
**Step 2: Clone git and verify**
|
|
120
123
|
|
|
121
124
|
3. Clone the repository at the tagged commit (using origin/tag from manifest)
|
|
122
|
-
4.
|
|
123
|
-
5.
|
|
124
|
-
6.
|
|
125
|
-
7.
|
|
125
|
+
4. Verify the cloned commit hash matches the `revision` field in manifest
|
|
126
|
+
5. Stage all tracked files
|
|
127
|
+
6. Run `npm pack` to create a tarball
|
|
128
|
+
7. Compute SHA256 hash of the clean tarball
|
|
129
|
+
8. Compare with the `hash` field from the npm manifest
|
|
126
130
|
|
|
127
131
|
**Step 3: Verify signature**
|
|
128
132
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
133
|
+
9. Read `hipp.pub` from the cloned repository
|
|
134
|
+
10. Verify the signature was created by signing:
|
|
135
|
+
`hash + "\n" + origin + "\n" + tag + "\n" + revision + "\n" + name + "\n" + email`
|
|
132
136
|
|
|
133
137
|
**Step 4: Rebuild verification**
|
|
134
138
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
11. Append the manifest to the staged README
|
|
140
|
+
12. Update the staged `package.json` version to match the tag
|
|
141
|
+
13. Run `npm pack` again and verify the hash matches the npm tarball
|
|
138
142
|
|
|
139
143
|
### Three Verification Checks
|
|
140
144
|
|
|
@@ -211,13 +215,24 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
211
215
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
212
216
|
PERFORMANCE OF THIS SOFTWARE.
|
|
213
217
|
|
|
218
|
+
## Verify
|
|
219
|
+
|
|
220
|
+
Verify this package with [@dk/hipp](https://www.npmjs.com/package/@dk/hipp):
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
npx @dk/hipp verify @dk/hipp@0.1.20
|
|
224
|
+
```
|
|
225
|
+
|
|
214
226
|
```json
|
|
215
227
|
{
|
|
216
228
|
"origin": "git@github.com:dmytri/hipp.git",
|
|
217
|
-
"tag": "v0.1.
|
|
218
|
-
"
|
|
219
|
-
"
|
|
229
|
+
"tag": "v0.1.20",
|
|
230
|
+
"revision": "1d5a16d3e837ffd79d4c6704cf1945f6d0b84a60",
|
|
231
|
+
"hash": "71fe169e31a68c92c249ae2a443a4062dc379b36bf854a290576e7b595ca4e58",
|
|
232
|
+
"signature": "UpVrjogkhwUyPvGj6bK4vOUj3p9R/JDvuDaupOibXFyK5FibLWzpMwjQWHJa78wYx4D4f/BmAn99J3Xt4icaDg==",
|
|
220
233
|
"name": "Dmytri Kleiner",
|
|
221
|
-
"email": "dev@dmytri.to"
|
|
234
|
+
"email": "dev@dmytri.to",
|
|
235
|
+
"npm": "11.12.1",
|
|
236
|
+
"node": "v25.8.2"
|
|
222
237
|
}
|
|
223
238
|
```
|
package/hipp.js
CHANGED
|
@@ -121,8 +121,8 @@ function verifySignature(data, signature, publicKey) {
|
|
|
121
121
|
}, Buffer.from(signature, 'base64'));
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
function buildSignData(hash, origin, tag, name, email) {
|
|
125
|
-
return `${hash}\n${origin}\n${tag}\n${name}\n${email}\n`;
|
|
124
|
+
function buildSignData(hash, origin, tag, revision, name, email) {
|
|
125
|
+
return `${hash}\n${origin}\n${tag}\n${revision}\n${name}\n${email}\n`;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
function findLastJsonBlock(readmeContent) {
|
|
@@ -463,11 +463,11 @@ async function runVerify(packageSpec) {
|
|
|
463
463
|
|
|
464
464
|
const npmReadme = fs.readFileSync(npmReadmePath, 'utf8');
|
|
465
465
|
manifest = findLastJsonBlock(npmReadme);
|
|
466
|
-
if (!manifest || !manifest.origin || !manifest.tag || !manifest.hash || !manifest.signature || !manifest.name || !manifest.email) {
|
|
466
|
+
if (!manifest || !manifest.origin || !manifest.tag || !manifest.revision || !manifest.hash || !manifest.signature || !manifest.name || !manifest.email) {
|
|
467
467
|
fail(`â Manifest not found or invalid in README`);
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
-
const { origin: originUrl, tag, signature, name, email } = manifest;
|
|
470
|
+
const { origin: originUrl, tag, revision, signature, name, email, npm: npmVer, node: nodeVer } = manifest;
|
|
471
471
|
|
|
472
472
|
log.info(`đŋ Cloning git origin at tag ${tag}...`);
|
|
473
473
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), `hipp-verify-git-`));
|
|
@@ -476,6 +476,12 @@ async function runVerify(packageSpec) {
|
|
|
476
476
|
try {
|
|
477
477
|
git(['clone', '--branch', tag, '--depth', '1', originUrl, tmpDir], { stdio: 'pipe' });
|
|
478
478
|
|
|
479
|
+
const clonedRevision = git(['rev-parse', 'HEAD'], { cwd: tmpDir });
|
|
480
|
+
if (clonedRevision !== revision) {
|
|
481
|
+
fail(`â Revision mismatch: manifest claims ${revision.slice(0, 12)} but tag points to ${clonedRevision.slice(0, 12)}`);
|
|
482
|
+
}
|
|
483
|
+
log.success(`đˇī¸ Revision verified: ${revision.slice(0, 12)}...`);
|
|
484
|
+
|
|
479
485
|
const publicKeyPath = path.join(tmpDir, 'hipp.pub');
|
|
480
486
|
if (!fs.existsSync(publicKeyPath)) {
|
|
481
487
|
fail(`â hipp.pub not found in git at tag ${tag}`);
|
|
@@ -498,7 +504,7 @@ async function runVerify(packageSpec) {
|
|
|
498
504
|
log.success(`đ Manifest hash verified`);
|
|
499
505
|
|
|
500
506
|
log.info(`đ Check 1: Verifying signature...`);
|
|
501
|
-
const signData = buildSignData(manifest.hash, originUrl, tag, name, email);
|
|
507
|
+
const signData = buildSignData(manifest.hash, originUrl, tag, revision, name, email);
|
|
502
508
|
const signatureValid = verifySignature(signData, signature, publicKey);
|
|
503
509
|
if (!signatureValid) {
|
|
504
510
|
fail(`â Signature verification failed`);
|
|
@@ -508,15 +514,20 @@ async function runVerify(packageSpec) {
|
|
|
508
514
|
log.info(`đ Check 3: Rebuilding from source...`);
|
|
509
515
|
const stagedReadmePath = path.join(stageDir, 'README.md');
|
|
510
516
|
let stagedReadme = fs.readFileSync(stagedReadmePath, 'utf8');
|
|
511
|
-
stagedReadme = stagedReadme.trimEnd() + '\n\n```json\n' + JSON.stringify(manifest, null, 2) + '\n```\n';
|
|
512
|
-
fs.writeFileSync(stagedReadmePath, stagedReadme);
|
|
513
|
-
|
|
514
|
-
const stagedPkgPath = path.join(stageDir, 'package.json');
|
|
515
|
-
const stagedPkg = JSON.parse(fs.readFileSync(stagedPkgPath, 'utf8'));
|
|
516
517
|
const tagVersion = semver.clean(tag);
|
|
517
518
|
if (!tagVersion) {
|
|
518
519
|
fail(`â Tag ${tag} is not valid semver`);
|
|
519
520
|
}
|
|
521
|
+
stagedReadme = stagedReadme.trimEnd() + '\n\n## Verify\n\n' +
|
|
522
|
+
'Verify this package with [@dk/hipp](https://www.npmjs.com/package/@dk/hipp):\n\n' +
|
|
523
|
+
'```bash\n' +
|
|
524
|
+
`npx @dk/hipp verify ${pkgName}@${tagVersion}\n` +
|
|
525
|
+
'```\n\n' +
|
|
526
|
+
'```json\n' + JSON.stringify(manifest, null, 2) + '\n```\n';
|
|
527
|
+
fs.writeFileSync(stagedReadmePath, stagedReadme);
|
|
528
|
+
|
|
529
|
+
const stagedPkgPath = path.join(stageDir, 'package.json');
|
|
530
|
+
const stagedPkg = JSON.parse(fs.readFileSync(stagedPkgPath, 'utf8'));
|
|
520
531
|
stagedPkg.version = tagVersion;
|
|
521
532
|
fs.writeFileSync(stagedPkgPath, JSON.stringify(stagedPkg, null, 2) + '\n');
|
|
522
533
|
|
|
@@ -535,6 +546,9 @@ async function runVerify(packageSpec) {
|
|
|
535
546
|
log.info(`đ Publisher: ${name} <${email}>`);
|
|
536
547
|
log.info(`đ Origin: ${originUrl}`);
|
|
537
548
|
log.info(`đ Tag: ${tag}`);
|
|
549
|
+
if (npmVer && nodeVer) {
|
|
550
|
+
log.info(`âšī¸ npm: ${npmVer} | node: ${nodeVer}`);
|
|
551
|
+
}
|
|
538
552
|
} finally {
|
|
539
553
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
540
554
|
fs.rmSync(stageDir, { recursive: true, force: true });
|
|
@@ -633,19 +647,30 @@ async function run() {
|
|
|
633
647
|
}
|
|
634
648
|
|
|
635
649
|
const { name, email } = getGitUserInfo();
|
|
636
|
-
const
|
|
650
|
+
const revision = refInfo.head;
|
|
651
|
+
const npmVersion = runCmd('npm', ['--version']).stdout.trim();
|
|
652
|
+
const nodeVersion = process.version;
|
|
653
|
+
const dataToSign = buildSignData(tarballHash, provenance.remoteUrl, rawTag, revision, name, email);
|
|
637
654
|
const signature = signContent(dataToSign, privateKey);
|
|
638
655
|
|
|
639
656
|
const manifestJson = {
|
|
640
657
|
origin: provenance.remoteUrl,
|
|
641
658
|
tag: rawTag,
|
|
659
|
+
revision: revision,
|
|
642
660
|
hash: tarballHash,
|
|
643
661
|
signature: signature,
|
|
644
662
|
name: name,
|
|
645
663
|
email: email,
|
|
664
|
+
npm: npmVersion,
|
|
665
|
+
node: nodeVersion,
|
|
646
666
|
};
|
|
647
667
|
|
|
648
|
-
stagedReadme = stagedReadme.trimEnd() + '\n\n
|
|
668
|
+
stagedReadme = stagedReadme.trimEnd() + '\n\n## Verify\n\n' +
|
|
669
|
+
'Verify this package with [@dk/hipp](https://www.npmjs.com/package/@dk/hipp):\n\n' +
|
|
670
|
+
'```bash\n' +
|
|
671
|
+
`npx @dk/hipp verify ${pkg.name}@${version}\n` +
|
|
672
|
+
'```\n\n' +
|
|
673
|
+
'```json\n' + JSON.stringify(manifestJson, null, 2) + '\n```\n';
|
|
649
674
|
fs.writeFileSync(stagedReadmePath, stagedReadme);
|
|
650
675
|
|
|
651
676
|
const stagedPkgPath = path.join(stageDir, 'package.json');
|