chrome-agent 0.9.0 → 0.11.0
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 +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +69 -5
package/README.md
CHANGED
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { createHash } from 'crypto';
|
|
3
4
|
import { chmodSync, createWriteStream, existsSync, mkdirSync, readFileSync, renameSync, rmSync } from 'fs';
|
|
4
5
|
import { get } from 'https';
|
|
5
6
|
import { arch, platform } from 'os';
|
|
@@ -64,6 +65,60 @@ async function downloadFile(url, destination) {
|
|
|
64
65
|
}).catch((error) => { rmSync(tempPath, { force: true }); throw error; });
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
/** Fetch a small text asset (the checksum file) into memory. */
|
|
69
|
+
async function fetchText(url) {
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
const request = (currentUrl, redirects = 10) => {
|
|
72
|
+
get(currentUrl, {
|
|
73
|
+
headers: { Accept: 'text/plain', 'User-Agent': `chrome-agent/${version}` },
|
|
74
|
+
}, (response) => {
|
|
75
|
+
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
76
|
+
response.resume();
|
|
77
|
+
if (redirects === 0) { reject(new Error('Too many redirects')); return; }
|
|
78
|
+
request(new URL(response.headers.location, currentUrl), redirects - 1);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (response.statusCode !== 200) {
|
|
82
|
+
response.resume();
|
|
83
|
+
reject(new Error(`HTTP ${response.statusCode} from ${currentUrl}`));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
let body = '';
|
|
87
|
+
response.setEncoding('utf8');
|
|
88
|
+
response.on('data', (chunk) => { body += chunk; });
|
|
89
|
+
response.on('end', () => resolve(body));
|
|
90
|
+
response.on('error', reject);
|
|
91
|
+
}).on('error', reject).setTimeout(30_000, function() { this.destroy(new Error('Timeout')); });
|
|
92
|
+
};
|
|
93
|
+
request(url);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function sha256File(path) {
|
|
98
|
+
return createHash('sha256').update(readFileSync(path)).digest('hex');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Compare a downloaded file against the checksum published beside it.
|
|
103
|
+
*
|
|
104
|
+
* The binary is fetched over the network and chmod +x'd; without this there was nothing
|
|
105
|
+
* anywhere in the chain saying the bytes are the ones that were built. A mismatch deletes
|
|
106
|
+
* the file rather than leaving a half-trusted binary on disk: a corrupted-but-running
|
|
107
|
+
* binary surfaces as confusing behaviour later, which is worse than a failed install.
|
|
108
|
+
*/
|
|
109
|
+
export function verifyChecksum(actualHex, expectedRaw, binaryName) {
|
|
110
|
+
const expected = String(expectedRaw).trim().split(/\s+/)[0].toLowerCase();
|
|
111
|
+
if (!/^[0-9a-f]{64}$/.test(expected)) {
|
|
112
|
+
throw new Error(`Malformed checksum for ${binaryName}: "${String(expectedRaw).trim().slice(0, 80)}"`);
|
|
113
|
+
}
|
|
114
|
+
if (actualHex.toLowerCase() !== expected) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Checksum mismatch for ${binaryName}: expected ${expected}, got ${actualHex}. ` +
|
|
117
|
+
'The download was corrupted or the release asset does not match what was published.'
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
67
122
|
async function main() {
|
|
68
123
|
const targetKey = getTargetKey();
|
|
69
124
|
const binaryName = targetKey ? supportedTargets[targetKey] : null;
|
|
@@ -90,11 +145,20 @@ async function main() {
|
|
|
90
145
|
console.log(`chrome-agent: downloading native binary for ${platform()}-${arch()}...`);
|
|
91
146
|
|
|
92
147
|
await downloadFile(url, binaryPath);
|
|
148
|
+
try {
|
|
149
|
+
verifyChecksum(sha256File(binaryPath), await fetchText(`${url}.sha256`), binaryName);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
rmSync(binaryPath, { force: true });
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
93
154
|
if (platform() !== 'win32') chmodSync(binaryPath, 0o755);
|
|
94
|
-
console.log(`chrome-agent: installed ${binaryName}`);
|
|
155
|
+
console.log(`chrome-agent: installed ${binaryName} (sha256 verified)`);
|
|
95
156
|
}
|
|
96
157
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
});
|
|
158
|
+
// Importable for the unit tests without running the install.
|
|
159
|
+
if (process.env.CHROME_AGENT_POSTINSTALL_NOOP !== '1') {
|
|
160
|
+
main().catch((error) => {
|
|
161
|
+
console.error(`chrome-agent postinstall failed: ${error.message}`);
|
|
162
|
+
process.exitCode = 1;
|
|
163
|
+
});
|
|
164
|
+
}
|