git-userhub 3.0.7 → 3.0.8
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/bin/git-user.js +65 -48
- package/package.json +3 -2
package/bin/git-user.js
CHANGED
|
@@ -12,6 +12,31 @@ const PKG_JSON = require('../package.json');
|
|
|
12
12
|
const REPO = 'divyo-argha/git-user';
|
|
13
13
|
const VERSION = `v${PKG_JSON.version}`;
|
|
14
14
|
|
|
15
|
+
// --- START PINNED HASHES ---
|
|
16
|
+
const PINNED_HASHES = {
|
|
17
|
+
"git-user_darwin_arm64.tar.gz": {
|
|
18
|
+
"archive": "759e6a4137bc9caed58b393f751fa4c670a3dea98ef072a6551f38d41a9fc9ce",
|
|
19
|
+
"binary": "9b39841ca909f8dfcffff9a856771a65a8615ffb5e872e4c1108f0fa2e0d1752"
|
|
20
|
+
},
|
|
21
|
+
"git-user_darwin_x86_64.tar.gz": {
|
|
22
|
+
"archive": "eda5a46ea9f440174408d33659de38b7623da28b94ee447a3dc7fa509ffced83",
|
|
23
|
+
"binary": "efd1e62a40659f5e9132448ae5d1c5a5f23069a5277db73641de726cd28c55f1"
|
|
24
|
+
},
|
|
25
|
+
"git-user_linux_arm64.tar.gz": {
|
|
26
|
+
"archive": "3e311264bedf0672eee3625396d2e42d1c72d2a263a56fe2a7de9e5c54a2e2d3",
|
|
27
|
+
"binary": "f177a44c4c7d0a79840a404b669ce924c0117d58db6d407880007684efea64a2"
|
|
28
|
+
},
|
|
29
|
+
"git-user_linux_x86_64.tar.gz": {
|
|
30
|
+
"archive": "aab7e800a2b49a265409e0fbc3c64d4282b334bca222ea975532bd92dc7869dc",
|
|
31
|
+
"binary": "31172516986f3020bda2d5186ef7034919c09690399f849dcc980b542ed060f8"
|
|
32
|
+
},
|
|
33
|
+
"git-user_windows_x86_64.tar.gz": {
|
|
34
|
+
"archive": "eee27a75cfa131053b0901d8432216881aae25fda4586258b9b357ffa3558455",
|
|
35
|
+
"binary": "36e2ebc6272fe828b9b6a49d2623a049d001e826cca2c6ad428f178e9ca09403"
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
// --- END PINNED HASHES ---
|
|
39
|
+
|
|
15
40
|
const platform = os.platform();
|
|
16
41
|
const arch = os.arch();
|
|
17
42
|
|
|
@@ -30,17 +55,41 @@ if (!osName || !archName) {
|
|
|
30
55
|
const finalBinaryName = `git-user-${platform}-${arch}${ext}`;
|
|
31
56
|
const finalBinaryPath = path.join(__dirname, finalBinaryName);
|
|
32
57
|
const assetName = `git-user_${osName}_${archName}.tar.gz`;
|
|
58
|
+
const pinnedData = PINNED_HASHES[assetName];
|
|
59
|
+
|
|
60
|
+
function computeHashSync(filePath) {
|
|
61
|
+
const hash = crypto.createHash('sha256');
|
|
62
|
+
const buffer = fs.readFileSync(filePath);
|
|
63
|
+
hash.update(buffer);
|
|
64
|
+
return hash.digest('hex');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function computeHash(file) {
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
const hash = crypto.createHash('sha256');
|
|
70
|
+
const stream = fs.createReadStream(file);
|
|
71
|
+
stream.on('data', d => hash.update(d));
|
|
72
|
+
stream.on('end', () => resolve(hash.digest('hex')));
|
|
73
|
+
stream.on('error', reject);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
33
76
|
|
|
34
77
|
function runBinary() {
|
|
78
|
+
if (pinnedData) {
|
|
79
|
+
const currentHash = computeHashSync(finalBinaryPath);
|
|
80
|
+
if (currentHash !== pinnedData.binary) {
|
|
81
|
+
console.error(`❌ Security Error: Cached binary hash mismatch! Deleting compromised binary.`);
|
|
82
|
+
fs.unlinkSync(finalBinaryPath);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
35
87
|
const child = spawn(finalBinaryPath, process.argv.slice(2), {
|
|
36
88
|
stdio: 'inherit',
|
|
37
89
|
shell: false
|
|
38
90
|
});
|
|
39
91
|
|
|
40
|
-
child.on('exit', (code) =>
|
|
41
|
-
process.exit(code || 0);
|
|
42
|
-
});
|
|
43
|
-
|
|
92
|
+
child.on('exit', (code) => process.exit(code || 0));
|
|
44
93
|
child.on('error', (err) => {
|
|
45
94
|
console.error('❌ Failed to start git-user:', err.message);
|
|
46
95
|
process.exit(1);
|
|
@@ -53,18 +102,7 @@ if (fs.existsSync(finalBinaryPath)) {
|
|
|
53
102
|
}
|
|
54
103
|
|
|
55
104
|
// FIRST RUN: Download binary
|
|
56
|
-
console.log(`[git-user]
|
|
57
|
-
|
|
58
|
-
function fetchJson(url) {
|
|
59
|
-
return new Promise((resolve, reject) => {
|
|
60
|
-
https.get(url, { headers: { 'User-Agent': 'node' } }, (res) => {
|
|
61
|
-
if (res.statusCode !== 200) return reject(new Error(`API Error ${res.statusCode}`));
|
|
62
|
-
let data = '';
|
|
63
|
-
res.on('data', c => data += c);
|
|
64
|
-
res.on('end', () => resolve(JSON.parse(data)));
|
|
65
|
-
}).on('error', reject);
|
|
66
|
-
});
|
|
67
|
-
}
|
|
105
|
+
console.log(`[git-user] Downloading cryptographically signed binary for ${platform}-${arch}...`);
|
|
68
106
|
|
|
69
107
|
function fetchFile(url, dest) {
|
|
70
108
|
return new Promise((resolve, reject) => {
|
|
@@ -80,39 +118,19 @@ function fetchFile(url, dest) {
|
|
|
80
118
|
});
|
|
81
119
|
}
|
|
82
120
|
|
|
83
|
-
function verifyHash(file, expected) {
|
|
84
|
-
return new Promise((resolve, reject) => {
|
|
85
|
-
const hash = crypto.createHash('sha256');
|
|
86
|
-
const stream = fs.createReadStream(file);
|
|
87
|
-
stream.on('data', d => hash.update(d));
|
|
88
|
-
stream.on('end', () => {
|
|
89
|
-
if (hash.digest('hex') !== expected) reject(new Error("Checksum mismatch!"));
|
|
90
|
-
else resolve();
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
121
|
async function install() {
|
|
96
122
|
try {
|
|
97
|
-
const release = await fetchJson(`https://api.github.com/repos/${REPO}/releases/tags/${VERSION}`);
|
|
98
|
-
const checksumAsset = release.assets.find(a => a.name.endsWith('checksums.txt'));
|
|
99
|
-
const binAsset = release.assets.find(a => a.name === assetName);
|
|
100
|
-
|
|
101
|
-
if (!checksumAsset || !binAsset) {
|
|
102
|
-
throw new Error("Could not find required assets on GitHub Release");
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const checksumPath = path.join(__dirname, 'checksums.txt');
|
|
106
123
|
const archivePath = path.join(__dirname, assetName);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
const downloadUrl = `https://github.com/${REPO}/releases/download/${VERSION}/${assetName}`;
|
|
125
|
+
|
|
126
|
+
await fetchFile(downloadUrl, archivePath);
|
|
127
|
+
|
|
128
|
+
if (pinnedData) {
|
|
129
|
+
const archiveHash = await computeHash(archivePath);
|
|
130
|
+
if (archiveHash !== pinnedData.archive) {
|
|
131
|
+
throw new Error("Archive checksum mismatch! Connection may be compromised.");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
116
134
|
|
|
117
135
|
const binaryNameInArchive = platform === 'win32' ? 'git-user.exe' : 'git-user';
|
|
118
136
|
await tar.extract({
|
|
@@ -127,9 +145,8 @@ async function install() {
|
|
|
127
145
|
if (platform !== 'win32') fs.chmodSync(finalBinaryPath, 0o755);
|
|
128
146
|
|
|
129
147
|
fs.unlinkSync(archivePath);
|
|
130
|
-
fs.unlinkSync(checksumPath);
|
|
131
148
|
|
|
132
|
-
console.log(`[git-user] Installation complete.\n`);
|
|
149
|
+
console.log(`[git-user] Installation and verification complete.\n`);
|
|
133
150
|
runBinary();
|
|
134
151
|
} catch (err) {
|
|
135
152
|
console.error(`\n❌ git-user installation failed: ${err.message}`);
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-userhub",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.8",
|
|
4
4
|
"description": "Switch Git accounts in one command. No config editing. No SSH key chaos.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"git-user": "bin/git-user.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"test": "echo \"No tests yet\" && exit 0"
|
|
9
|
+
"test": "echo \"No tests yet\" && exit 0",
|
|
10
|
+
"prepublishOnly": "node scripts/inject-hashes.js"
|
|
10
11
|
},
|
|
11
12
|
"keywords": [
|
|
12
13
|
"git",
|