@backendsystems/nibble 0.3.0 → 0.3.1

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.
Files changed (2) hide show
  1. package/bin/install.js +88 -1
  2. package/package.json +1 -1
package/bin/install.js CHANGED
@@ -4,6 +4,7 @@ const fs = require('fs');
4
4
  const os = require('os');
5
5
  const path = require('path');
6
6
  const https = require('https');
7
+ const crypto = require('crypto');
7
8
  const { spawnSync } = require('child_process');
8
9
 
9
10
  const PROJECT = 'nibble';
@@ -56,6 +57,90 @@ function download(url, outFile) {
56
57
  });
57
58
  }
58
59
 
60
+ function downloadText(url) {
61
+ return new Promise((resolve, reject) => {
62
+ const req = https.get(url, (res) => {
63
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
64
+ res.resume();
65
+ return resolve(downloadText(res.headers.location));
66
+ }
67
+
68
+ if (res.statusCode !== 200) {
69
+ res.resume();
70
+ return reject(new Error(`download failed (${res.statusCode}): ${url}`));
71
+ }
72
+
73
+ let data = '';
74
+ res.setEncoding('utf8');
75
+ res.on('data', (chunk) => {
76
+ data += chunk;
77
+ });
78
+ res.on('end', () => resolve(data));
79
+ res.on('error', reject);
80
+ });
81
+ req.on('error', reject);
82
+ });
83
+ }
84
+
85
+ function parseChecksums(text) {
86
+ const checksums = new Map();
87
+ for (const line of text.split(/\r?\n/)) {
88
+ const trimmed = line.trim();
89
+ if (!trimmed || trimmed.startsWith('#')) {
90
+ continue;
91
+ }
92
+
93
+ const match = trimmed.match(/^([a-fA-F0-9]{64})\s+\*?(.+)$/);
94
+ if (!match) {
95
+ continue;
96
+ }
97
+ checksums.set(match[2].trim(), match[1].toLowerCase());
98
+ }
99
+ return checksums;
100
+ }
101
+
102
+ function sha256File(filePath) {
103
+ const hash = crypto.createHash('sha256');
104
+ hash.update(fs.readFileSync(filePath));
105
+ return hash.digest('hex');
106
+ }
107
+
108
+ async function verifyArchiveChecksum(urlBase, archivePath, archiveName, version) {
109
+ const checksumCandidates = [
110
+ 'checksums.txt',
111
+ `${PROJECT}_${version}_checksums.txt`,
112
+ ];
113
+
114
+ let checksumFile = '';
115
+ let checksumName = '';
116
+ for (const candidate of checksumCandidates) {
117
+ try {
118
+ checksumFile = await downloadText(`${urlBase}/${candidate}`);
119
+ checksumName = candidate;
120
+ break;
121
+ } catch (err) {
122
+ if (!String(err.message).includes('download failed (404)')) {
123
+ throw err;
124
+ }
125
+ }
126
+ }
127
+
128
+ if (!checksumFile) {
129
+ throw new Error(`no checksum file found (tried: ${checksumCandidates.join(', ')})`);
130
+ }
131
+
132
+ const checksums = parseChecksums(checksumFile);
133
+ const expected = checksums.get(archiveName);
134
+ if (!expected) {
135
+ throw new Error(`checksum for ${archiveName} not found in ${checksumName}`);
136
+ }
137
+
138
+ const actual = sha256File(archivePath);
139
+ if (actual !== expected) {
140
+ throw new Error(`checksum mismatch for ${archiveName}`);
141
+ }
142
+ }
143
+
59
144
  function run(cmd, args) {
60
145
  const result = spawnSync(cmd, args, { stdio: 'inherit' });
61
146
  if (result.status !== 0) {
@@ -99,7 +184,8 @@ async function main() {
99
184
  const assetBase = `${PROJECT}_${osName}_${arch}`;
100
185
  const ext = 'tar.gz';
101
186
  const asset = `${assetBase}.${ext}`;
102
- const url = `https://github.com/${OWNER}/${REPO}/releases/download/${tag}/${asset}`;
187
+ const urlBase = `https://github.com/${OWNER}/${REPO}/releases/download/${tag}`;
188
+ const url = `${urlBase}/${asset}`;
103
189
 
104
190
  const cacheDir = fs.mkdtempSync(path.join(os.tmpdir(), 'nibble-npm-'));
105
191
  const archivePath = path.join(cacheDir, asset);
@@ -107,6 +193,7 @@ async function main() {
107
193
  try {
108
194
  console.log(`Downloading ${asset} from ${url}`);
109
195
  await download(url, archivePath);
196
+ await verifyArchiveChecksum(urlBase, archivePath, asset, pkgVersion);
110
197
  installFromArchive(archivePath, osName);
111
198
  console.log('Installed nibble binary successfully');
112
199
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backendsystems/nibble",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Fast local network scanner with hardware identification and a terminal UI",
5
5
  "bin": {
6
6
  "nibble": "bin/nibble.js"