1
- const { existsSync, mkdirSync } = require ( "fs" ) ;
1
+ const { createHash } = require ( "crypto" ) ;
2
+ const { chmodSync, existsSync, mkdirSync, readFileSync } = require ( "fs" ) ;
2
3
const { join } = require ( "path" ) ;
3
4
const { spawnSync } = require ( "child_process" ) ;
4
5
5
6
const axios = require ( "axios" ) ;
6
7
const tar = require ( "tar" ) ;
7
8
const rimraf = require ( "rimraf" ) ;
8
9
9
- const error = msg => {
10
- console . error ( msg ) ;
11
- process . exit ( 1 ) ;
12
- } ;
13
-
14
10
class Binary {
15
11
constructor ( name , url ) {
16
12
let errors = [ ] ;
@@ -38,7 +34,8 @@ class Binary {
38
34
} ) ;
39
35
errorMsg +=
40
36
'\n\nCorrect usage: new Binary("my-binary", "https://example.com/binary/download.tar.gz")' ;
41
- error ( errorMsg ) ;
37
+ console . error ( errorMsg ) ;
38
+ process . exit ( 1 ) ;
42
39
}
43
40
this . url = url ;
44
41
this . name = name ;
@@ -60,7 +57,9 @@ class Binary {
60
57
61
58
// console.log(`Downloading release from ${this.url}`);
62
59
60
+ // Stream the file from the GitHub release page
63
61
return axios ( { ...fetchOptions , url : this . url , responseType : "stream" } )
62
+ // untar the stream and write to disk
64
63
. then ( res => {
65
64
return new Promise ( ( resolve , reject ) => {
66
65
const sink = tar . x ( { strip : 1 , C : this . installDirectory } ) ;
@@ -69,17 +68,40 @@ class Binary {
69
68
sink . on ( 'error' , err => reject ( err ) ) ;
70
69
} ) ;
71
70
} )
71
+ // calculate a checksum of the untarred binary
72
+ . then ( ( ) => {
73
+ const fileBuffer = readFileSync ( this . binaryPath ) ;
74
+ const hashsum = createHash ( "sha256" ) ;
75
+ hashsum . update ( fileBuffer ) ;
76
+ const calculated_checksum = hashsum . digest ( 'hex' ) ;
77
+
78
+ const advertised_checksums = readFileSync ( join ( __dirname , "SHASUMS256.txt" ) ) ;
79
+
80
+ return new Promise ( ( resolve , reject ) => {
81
+ if ( advertised_checksums . includes ( calculated_checksum ) ) {
82
+ resolve ( ) ;
83
+ } else {
84
+ chmodSync ( this . binaryPath , 0o400 ) ;
85
+ console . error ( `Calculated unexpected checksum ${ calculated_checksum } for file ${ this . binaryPath } ` ) ;
86
+ console . error ( 'This file has been stripped of executable permissions but you should quarantine or delete it and open an issue.' ) ;
87
+ reject ( new Error ( 'Unexpected checksum' ) ) ;
88
+ }
89
+ } ) ;
90
+ } )
72
91
. then ( ( ) => {
73
92
// console.log(`${this.name} has been installed!`);
74
93
} )
75
- . catch ( e => {
76
- error ( `Error fetching release: ${ e . message } ` ) ;
94
+ . catch ( err => {
95
+ console . error ( `Error fetching release:` ) ;
96
+ console . error ( err ) ;
97
+ process . exit ( 1 ) ;
77
98
} ) ;
78
99
}
79
100
80
101
run ( ) {
81
102
if ( ! existsSync ( this . binaryPath ) ) {
82
- error ( `You must install ${ this . name } before you can run it` ) ;
103
+ console . error ( `You must install ${ this . name } before you can run it` ) ;
104
+ process . exit ( 1 ) ;
83
105
}
84
106
85
107
const [ , , ...args ] = process . argv ;
@@ -89,7 +111,8 @@ class Binary {
89
111
const result = spawnSync ( this . binaryPath , args , options ) ;
90
112
91
113
if ( result . error ) {
92
- error ( result . error ) ;
114
+ console . error ( result . error ) ;
115
+ process . exit ( 1 ) ;
93
116
}
94
117
95
118
process . exit ( result . status ) ;
0 commit comments