@drifted/raven 0.0.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.
- package/README.md +56 -0
- package/bin/cli.js +167 -0
- package/config/secret.json +3 -0
- package/index.js +178 -0
- package/package.json +28 -0
- package/test/cli.test.js +130 -0
- package/test/data/original.json +3 -0
- package/test/data/original.json.encrypted +0 -0
- package/test/helpers.js +49 -0
- package/test/raven.test.js +36 -0
- package/test/workspace/config/secret.json +3 -0
- package/test/workspace/example.json +1 -0
- package/usage.txt +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
# jsondatafile
|
|
3
|
+
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install -g @drifted/jsondatafile
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
> raven usage
|
|
12
|
+
|
|
13
|
+
> raven secret
|
|
14
|
+
secret: fVu1houhxJS0QeIGSURG0Mzm1C8o3QedhRETu8/ZVM4=
|
|
15
|
+
|
|
16
|
+
> raven encrypt -s 'fVu1houhxJS0QeIGSURG0Mzm1C8o3QedhRETu8/ZVM4=' -d '{"test": true}'
|
|
17
|
+
data: 75e1eb5497506fb3c19212546f6bbd64:4aa059c75da768a5cffbd739d47ccb77:4221fa37367a53ea00d033148cae
|
|
18
|
+
|
|
19
|
+
> raven decrypt -s 'fVu1houhxJS0QeIGSURG0Mzm1C8o3QedhRETu8/ZVM4=' -d '75e1eb5497506fb3c19212546f6bbd64:4aa059c75da768a5cffbd739d47ccb77:4221fa37367a53ea00d033148cae'
|
|
20
|
+
{ test: true }
|
|
21
|
+
|
|
22
|
+
> raven conceal -f theraven.txt
|
|
23
|
+
|
|
24
|
+
theraven.txt encrypted
|
|
25
|
+
secret: /n6hOiIoIg/qaQFQVMyyqNUB0pDAZmPyLblU/nKz4vg=
|
|
26
|
+
|
|
27
|
+
> raven expose -f theraven.txt -s "/n6hOiIoIg/qaQFQVMyyqNUB0pDAZmPyLblU/nKz4vg="
|
|
28
|
+
|
|
29
|
+
theraven.txt decrypted
|
|
30
|
+
secret: /n6hOiIoIg/qaQFQVMyyqNUB0pDAZmPyLblU/nKz4vg=
|
|
31
|
+
|
|
32
|
+
> raven init
|
|
33
|
+
file created at config/secret.json # create default secret
|
|
34
|
+
|
|
35
|
+
> raven init
|
|
36
|
+
config/secret.json already exists. # won't overwrite existing
|
|
37
|
+
|
|
38
|
+
> raven generate
|
|
39
|
+
file created at config/secret.json # force new file to be created
|
|
40
|
+
|
|
41
|
+
> raven show
|
|
42
|
+
{
|
|
43
|
+
"secret": "O9JorHTlz2Wk+oaGq1XpLZzYxwblzlYd3+No7l8eupQ="
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
> raven conceal -f theraven.txt
|
|
47
|
+
|
|
48
|
+
theraven.txt encrypted
|
|
49
|
+
secret: O9JorHTlz2Wk+oaGq1XpLZzYxwblzlYd3+No7l8eupQ=
|
|
50
|
+
|
|
51
|
+
> raven expose -f theraven.txt
|
|
52
|
+
|
|
53
|
+
theraven.txt decrypted
|
|
54
|
+
secret: O9JorHTlz2Wk+oaGq1XpLZzYxwblzlYd3+No7l8eupQ=
|
|
55
|
+
|
|
56
|
+
```
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
var CarrierPigeon = require('carrier-pigeon');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var fs = require('fs');
|
|
6
|
+
var crypto = require('crypto');
|
|
7
|
+
const JSONDataFile = require(path.join(__dirname, '..'));
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
var parser = new CarrierPigeon({strict: true});
|
|
11
|
+
parser.commands('usage', 'show', 'secret', 'init', 'generate', 'expose', 'conceal', 'encrypt', 'decrypt');
|
|
12
|
+
parser.option('file', {type: 'file'});
|
|
13
|
+
parser.option('secret', {});
|
|
14
|
+
parser.option('data', {});
|
|
15
|
+
parser.option('json', {default: false});
|
|
16
|
+
parser.option('location', {type: 'file', default: process.cwd()});
|
|
17
|
+
parser.option('verbose', {default: false});
|
|
18
|
+
|
|
19
|
+
var options = parser.parse(process.argv);
|
|
20
|
+
var command = options.command;
|
|
21
|
+
delete options.command;
|
|
22
|
+
|
|
23
|
+
process.chdir(options.location);
|
|
24
|
+
delete options.location;
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if (options.verbose) {
|
|
30
|
+
console.log('command:',command);
|
|
31
|
+
console.log('options:',options);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if (command == 'encrypt') {
|
|
37
|
+
var file = new JSONDataFile(options);
|
|
38
|
+
|
|
39
|
+
var data = file.encrypt(options.data);
|
|
40
|
+
if (options.json) {
|
|
41
|
+
console.log(JSON.stringify({action: 'encrypt', options: options, data: data}, null, 2))
|
|
42
|
+
} else {
|
|
43
|
+
console.log('data:', data);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
if (command == 'decrypt') {
|
|
50
|
+
var data = options.data;
|
|
51
|
+
var secret = options.secret;
|
|
52
|
+
|
|
53
|
+
delete options.data;
|
|
54
|
+
var file = new JSONDataFile(options);
|
|
55
|
+
var json = file.decrypt(data);
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
json = JSON.parse(json);
|
|
59
|
+
} catch(error) {
|
|
60
|
+
if (options.verbose) {
|
|
61
|
+
console.log(error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (options.json) {
|
|
65
|
+
console.log(JSON.stringify({action: 'decrypt', options: options, data: json}, null, 2))
|
|
66
|
+
} else {
|
|
67
|
+
console.log(json);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if (command == 'generate') {
|
|
74
|
+
JSONDataFile.generate().then((generated) => {
|
|
75
|
+
if (options.json) {
|
|
76
|
+
|
|
77
|
+
console.log(JSON.stringify({
|
|
78
|
+
action: 'generate',
|
|
79
|
+
options: options,
|
|
80
|
+
location: process.cwd(),
|
|
81
|
+
success: generated
|
|
82
|
+
}, null, 2))
|
|
83
|
+
|
|
84
|
+
} else {
|
|
85
|
+
console.log('file created at', JSONDataFile.secretFile().replace(process.cwd()+path.sep, ''));
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
if (command == 'init') {
|
|
93
|
+
JSONDataFile.init().then((initialized) => {
|
|
94
|
+
if (options.json) {
|
|
95
|
+
console.log(JSON.stringify({action: 'init', options: options, location: process.cwd(), success: initialized}, null, 2))
|
|
96
|
+
} else {
|
|
97
|
+
if (initialized) {
|
|
98
|
+
console.log('file created at', JSONDataFile.secretFile().replace(process.cwd()+path.sep, ''));
|
|
99
|
+
} else {
|
|
100
|
+
console.log(JSONDataFile.secretFile().replace(process.cwd()+path.sep, ''), 'already exists.');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if (command == 'secret') {
|
|
109
|
+
if (options.json) {
|
|
110
|
+
console.log(JSON.stringify({ secret: JSONDataFile.secret() }, null, 2));
|
|
111
|
+
} else {
|
|
112
|
+
console.log('secret:', JSONDataFile.secret());
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
if (command == 'show') {
|
|
119
|
+
var json = JSON.parse(JSONDataFile.show());
|
|
120
|
+
if (options.json) {
|
|
121
|
+
console.log(JSON.stringify(json, null, 2));
|
|
122
|
+
} else {
|
|
123
|
+
console.log('secret:', json.secret);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
if (command == 'expose') {
|
|
130
|
+
var file = new JSONDataFile(options);
|
|
131
|
+
//console.log(file);
|
|
132
|
+
file.expose().then(() => {
|
|
133
|
+
if (options.json) {
|
|
134
|
+
console.log(JSON.stringify({file: file.shortPath, action: 'expose', options: options, secret: file.secret}, null, 2))
|
|
135
|
+
} else {
|
|
136
|
+
console.log('');
|
|
137
|
+
console.log(file.shortPath(), 'decrypted');
|
|
138
|
+
console.log('secret:', file.secret);
|
|
139
|
+
console.log('');
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
if (command == 'conceal') {
|
|
147
|
+
var file = new JSONDataFile(options);
|
|
148
|
+
//console.log(file);
|
|
149
|
+
file.conceal().then(() => {
|
|
150
|
+
if (options.json) {
|
|
151
|
+
console.log(JSON.stringify({file: file.shortPath, action: 'conceal', options: options, secret: file.secret}, null, 2))
|
|
152
|
+
} else {
|
|
153
|
+
console.log('');
|
|
154
|
+
console.log(file.shortPath(), 'encrypted');
|
|
155
|
+
console.log('secret:', file.secret);
|
|
156
|
+
console.log('');
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if (command == 'usage') {
|
|
164
|
+
console.log(fs.readFileSync(path.join(__dirname, '..', 'usage.txt')).toString());
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
package/index.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
const ivLength = 16;
|
|
6
|
+
|
|
7
|
+
class JSONDataFile {
|
|
8
|
+
constructor(options={}) {
|
|
9
|
+
Object.assign(this, options)
|
|
10
|
+
|
|
11
|
+
if (this.data != undefined && typeof this.data == 'string') {
|
|
12
|
+
try {
|
|
13
|
+
this.data = JSON.parse(this.data);
|
|
14
|
+
} catch(error) {
|
|
15
|
+
//console.log(error)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (this.algorithm == undefined) {
|
|
20
|
+
this.algorithm = 'aes-256-gcm';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (this.secret == undefined && JSONDataFile.hasFile()) {
|
|
24
|
+
this.populateFromFile();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (this.secret == undefined) {
|
|
28
|
+
this.secret = crypto.randomBytes(32).toString('base64');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
populateFromFile() {
|
|
33
|
+
var data = fs.readFileSync(JSONDataFile.secretFile()).toString();
|
|
34
|
+
var json = JSON.parse(data);
|
|
35
|
+
this.secret = json.secret;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
shortPath() {
|
|
39
|
+
return this.file.replace(process.cwd()+"/", "")
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static show() {
|
|
43
|
+
return fs.readFileSync(this.secretFile()).toString();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static secretFile() {
|
|
47
|
+
return path.join(process.cwd(), 'config', 'secret.json');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static hasFile() {
|
|
51
|
+
var exists = fs.existsSync(this.secretFile());
|
|
52
|
+
return exists;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static generate() {
|
|
56
|
+
var self = this;
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
var secret = self.secret();
|
|
59
|
+
|
|
60
|
+
fs.writeFile(self.secretFile(), JSON.stringify({secret}, null, 2), () => {
|
|
61
|
+
resolve(true);
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static init() {
|
|
67
|
+
var self = this;
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
fs.mkdir(path.join(process.cwd(), 'config'), { recursive: true }, (err) => {
|
|
70
|
+
if (err) throw err;
|
|
71
|
+
|
|
72
|
+
if (self.hasFile() == false) {
|
|
73
|
+
self.generate().then(() => {
|
|
74
|
+
resolve(true);
|
|
75
|
+
})
|
|
76
|
+
} else {
|
|
77
|
+
resolve(false)
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static secret() {
|
|
84
|
+
return crypto.randomBytes(32).toString('base64');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
encrypt(text) {
|
|
88
|
+
const iv = crypto.randomBytes(ivLength);
|
|
89
|
+
const cipher = crypto.createCipheriv(this.algorithm, Buffer.from(this.secret, 'base64'), iv);
|
|
90
|
+
|
|
91
|
+
let encrypted = cipher.update(text, 'utf8', 'hex');
|
|
92
|
+
encrypted += cipher.final('hex');
|
|
93
|
+
|
|
94
|
+
const authTag = cipher.getAuthTag().toString('hex');
|
|
95
|
+
|
|
96
|
+
// Return IV, Auth Tag, and Encrypted data together (often as a single string)
|
|
97
|
+
return `${iv.toString('hex')}:${authTag}:${encrypted}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
decrypt(encryptedData) {
|
|
101
|
+
const [ivHex, authTagHex, encryptedText] = encryptedData.split(':');
|
|
102
|
+
|
|
103
|
+
const decipher = crypto.createDecipheriv(
|
|
104
|
+
this.algorithm,
|
|
105
|
+
Buffer.from(this.secret, 'base64'),
|
|
106
|
+
Buffer.from(ivHex, 'hex')
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
decipher.setAuthTag(Buffer.from(authTagHex, 'hex'));
|
|
110
|
+
|
|
111
|
+
let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
|
|
112
|
+
decrypted += decipher.final('utf8');
|
|
113
|
+
|
|
114
|
+
return decrypted;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
read() {
|
|
119
|
+
var self = this;
|
|
120
|
+
|
|
121
|
+
return new Promise(async(resolve) => {
|
|
122
|
+
fs.readFile(self.file, 'utf8', async (err, data) => {
|
|
123
|
+
self.data = data;
|
|
124
|
+
|
|
125
|
+
resolve(data);
|
|
126
|
+
});
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
save() {
|
|
131
|
+
var self = this;
|
|
132
|
+
return new Promise(async(resolve) => {
|
|
133
|
+
fs.writeFile(self.file, self.data, error => {
|
|
134
|
+
var success = true;
|
|
135
|
+
|
|
136
|
+
if (error) {
|
|
137
|
+
success = false;
|
|
138
|
+
reject({success, error});
|
|
139
|
+
} else {
|
|
140
|
+
resolve({success});
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
conceal() {
|
|
147
|
+
var self = this;
|
|
148
|
+
return new Promise(async(resolve) => {
|
|
149
|
+
self.read().then((data) => {
|
|
150
|
+
self.data = self.encrypt(data);
|
|
151
|
+
|
|
152
|
+
self.save().then((response) => {
|
|
153
|
+
resolve(response);
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
expose() {
|
|
160
|
+
var self = this;
|
|
161
|
+
return new Promise(async(resolve) => {
|
|
162
|
+
self.read().then((data) => {
|
|
163
|
+
self.data = self.decrypt(data);
|
|
164
|
+
|
|
165
|
+
self.save().then((response) => {
|
|
166
|
+
resolve(response);
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
module.exports = JSONDataFile;
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@drifted/raven",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"homepage": "https://github.com/driftless-group/raven",
|
|
6
|
+
"bin": {
|
|
7
|
+
"raven": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "@drifted/raven"
|
|
12
|
+
},
|
|
13
|
+
"license": "UNLICENSED",
|
|
14
|
+
"author": "Scott Ballantyne",
|
|
15
|
+
"type": "commonjs",
|
|
16
|
+
"main": "index.js",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "mocha -w",
|
|
19
|
+
"encrypt": "./bin/file.js encrypt",
|
|
20
|
+
"decrypt": "./bin/file.js decrypt"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@drifted/qa": "^0.0.5"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"carrier-pigeon": "^0.1.11"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/test/cli.test.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
|
|
5
|
+
const JSONDataFile = require(path.join(__dirname, '..'));
|
|
6
|
+
var { exec } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
doneMessage
|
|
10
|
+
} = require('@drifted/qa');
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
run, ensure, remove
|
|
14
|
+
} = require(path.join(__dirname, 'helpers'));
|
|
15
|
+
|
|
16
|
+
var defaults = {
|
|
17
|
+
secret: 'ffdnZY17Fw+sup2+lhOOt6PW++/RkLTXRaLL3RJsjzE='
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('raven', function() {
|
|
21
|
+
|
|
22
|
+
describe('human', function() {
|
|
23
|
+
it('secret', function(done) {
|
|
24
|
+
run('secret').then((stdout) => {
|
|
25
|
+
var secret = stdout.replace('secret: ', '');
|
|
26
|
+
|
|
27
|
+
assert.equal(secret.length, 45);
|
|
28
|
+
assert.notEqual(secret, undefined);
|
|
29
|
+
|
|
30
|
+
done();
|
|
31
|
+
}).catch(console.log)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('init', function(done) {
|
|
35
|
+
var homeConfig = path.join(__dirname, 'workspace', 'config')
|
|
36
|
+
remove(homeConfig).then(() => {
|
|
37
|
+
run('init').then((stdout) => {
|
|
38
|
+
var json = JSON.parse(fs.readFileSync(path.join(homeConfig, 'secret.json')).toString());
|
|
39
|
+
|
|
40
|
+
//console.log(json);
|
|
41
|
+
assert.equal(json.secret.length, 44);
|
|
42
|
+
assert.notEqual(json.secret, undefined);
|
|
43
|
+
|
|
44
|
+
done();
|
|
45
|
+
}).catch(doneMessage(done));
|
|
46
|
+
}).catch(doneMessage(done));
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('generate', function(done) {
|
|
50
|
+
var homeConfig = path.join(__dirname, 'workspace', 'config')
|
|
51
|
+
remove(homeConfig).then(() => {
|
|
52
|
+
run('init').then((stdout) => {
|
|
53
|
+
var initial = JSON.parse(fs.readFileSync(path.join(homeConfig, 'secret.json')).toString());
|
|
54
|
+
assert.equal(initial.secret.length, 44);
|
|
55
|
+
assert.notEqual(initial.secret, undefined);
|
|
56
|
+
|
|
57
|
+
run('generate').then(() => {
|
|
58
|
+
var regenerated = JSON.parse(fs.readFileSync(path.join(homeConfig, 'secret.json')).toString());
|
|
59
|
+
assert.notEqual(initial.secret, regenerated.secret);
|
|
60
|
+
assert.equal(regenerated.secret.length, 44);
|
|
61
|
+
assert.notEqual(regenerated.secret, undefined);
|
|
62
|
+
|
|
63
|
+
done();
|
|
64
|
+
});
|
|
65
|
+
}).catch(doneMessage(done));
|
|
66
|
+
}).catch(doneMessage(done));
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe('json', function() {
|
|
71
|
+
it('show', function(done) {
|
|
72
|
+
run('show', '-j').then((stdout) => {
|
|
73
|
+
var json = JSON.parse(stdout);
|
|
74
|
+
|
|
75
|
+
assert.equal(json.secret.length, 44);
|
|
76
|
+
assert.notEqual(json.secret, undefined);
|
|
77
|
+
|
|
78
|
+
done();
|
|
79
|
+
}).catch(doneMessage(done));
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
describe('files', function() {
|
|
84
|
+
it('conceal/expose', function(done) {
|
|
85
|
+
var secret = JSONDataFile.secret();
|
|
86
|
+
var file = path.join(__dirname, 'theraven.txt');
|
|
87
|
+
var initial = fs.readFileSync(file).toString();
|
|
88
|
+
|
|
89
|
+
run('conceal', '-f', file, '-s', secret).then(() => {
|
|
90
|
+
var encrypted = fs.readFileSync(file).toString();
|
|
91
|
+
run('expose', '-f', file, '-s', secret).then(() => {
|
|
92
|
+
var final = fs.readFileSync(file).toString();
|
|
93
|
+
|
|
94
|
+
assert.equal(initial, final);
|
|
95
|
+
assert.notEqual(initial, encrypted);
|
|
96
|
+
assert.notEqual(encrypted, final);
|
|
97
|
+
|
|
98
|
+
done();
|
|
99
|
+
}).catch(doneMessage(done));
|
|
100
|
+
}).catch(doneMessage(done));
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('repeatedly concealing/exposing', function(done) {
|
|
104
|
+
var secret = JSONDataFile.secret();
|
|
105
|
+
var file = path.join(__dirname, 'theraven.txt');
|
|
106
|
+
var initial = fs.readFileSync(file).toString();
|
|
107
|
+
run('conceal', '-f', file, '-s', secret).then(() => {
|
|
108
|
+
run('conceal', '-f', file, '-s', secret).then(() => {
|
|
109
|
+
run('conceal', '-f', file, '-s', secret).then(() => {
|
|
110
|
+
var encrypted = fs.readFileSync(file).toString();
|
|
111
|
+
run('expose', '-f', file, '-s', secret).then(() => {
|
|
112
|
+
run('expose', '-f', file, '-s', secret).then(() => {
|
|
113
|
+
run('expose', '-f', file, '-s', secret).then(() => {
|
|
114
|
+
var final = fs.readFileSync(file).toString();
|
|
115
|
+
|
|
116
|
+
assert.equal(initial, final);
|
|
117
|
+
assert.notEqual(initial, encrypted);
|
|
118
|
+
assert.notEqual(encrypted, final);
|
|
119
|
+
|
|
120
|
+
done();
|
|
121
|
+
}).catch(doneMessage(done));
|
|
122
|
+
}).catch(doneMessage(done));
|
|
123
|
+
}).catch(doneMessage(done));
|
|
124
|
+
}).catch(doneMessage(done));
|
|
125
|
+
}).catch(doneMessage(done));
|
|
126
|
+
}).catch(doneMessage(done));
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
})
|
|
File without changes
|
package/test/helpers.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
var { exec } = require('child_process');
|
|
4
|
+
var { mkdir } = require('fs');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
function run(...specifics) {
|
|
9
|
+
var parts = [process.execPath, path.join(__dirname, '..', 'bin', 'cli.js')].concat(specifics);
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
exec(parts.join(' '), (err, stdout, stdin) => {
|
|
12
|
+
if (err) {
|
|
13
|
+
reject(err)
|
|
14
|
+
} else {
|
|
15
|
+
resolve(stdout);
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports.run = run;
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
function ensure(pathname) {
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
if (fs.existsSync(pathname)) {
|
|
27
|
+
resolve();
|
|
28
|
+
} else {
|
|
29
|
+
fs.mkdir(pathname, { recursive: true }, (err, stdout, stdin) => {
|
|
30
|
+
resolve();
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
module.exports.ensure = ensure;
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
function remove(pathname) {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
if (fs.existsSync(pathname)) {
|
|
41
|
+
fs.rm(pathname, { recursive: true, force: true }, (err, stdout, stdin) => {
|
|
42
|
+
resolve();
|
|
43
|
+
})
|
|
44
|
+
} else {
|
|
45
|
+
resolve();
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
module.exports.remove = remove;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
|
|
4
|
+
const JSONDataFile = require(path.join(__dirname, '..'));
|
|
5
|
+
|
|
6
|
+
var defaults = {
|
|
7
|
+
secret: 'ffdnZY17Fw+sup2+lhOOt6PW++/RkLTXRaLL3RJsjzE='
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
process.chdir(path.join(__dirname, 'workspace'));
|
|
11
|
+
|
|
12
|
+
describe('raven', function() {
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
it('read', function(done) {
|
|
16
|
+
|
|
17
|
+
done();
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('write', function(done) {
|
|
21
|
+
done();
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('encrypt/decrypt', function(done) {
|
|
25
|
+
var jdf = new JSONDataFile({secret: defaults.secret, data: {test: true}});
|
|
26
|
+
var result = jdf.encrypt(JSON.stringify(jdf.data));
|
|
27
|
+
result = JSON.parse(jdf.decrypt(result));
|
|
28
|
+
assert.equal(result.test, true);
|
|
29
|
+
|
|
30
|
+
done();
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"test": true}
|
package/usage.txt
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
raven
|
|
3
|
+
|
|
4
|
+
This is a bin that is intended to make it easy to hide environment variables
|
|
5
|
+
so that they aren't clear text. It should be able to also encrypt and decrypt
|
|
6
|
+
data of other sorts too.
|
|
7
|
+
|
|
8
|
+
Synopsis
|
|
9
|
+
|
|
10
|
+
The original inspiration was from what rails does with their secret/credentials.
|
|
11
|
+
I don't know if it is implemented the same way or not but it is intended to be
|
|
12
|
+
for the same use.
|
|
13
|
+
|
|
14
|
+
Usage
|
|
15
|
+
|
|
16
|
+
raven command [options]
|
|
17
|
+
|
|
18
|
+
Commands
|
|
19
|
+
|
|
20
|
+
init prepares new project with config/secret.json
|
|
21
|
+
generate overwrites old secret
|
|
22
|
+
expose decrypts encrypted file in place
|
|
23
|
+
conceal encrypts file
|
|
24
|
+
encrypt encrypts data passed into the bin
|
|
25
|
+
decrypt decrypts data passed into the bin
|
|
26
|
+
secret generates a secret to use to encrypt data
|
|
27
|
+
show shows the current secret
|
|
28
|
+
usage prints out this information
|
|
29
|
+
|
|
30
|
+
Description
|
|
31
|
+
|
|
32
|
+
-f, --file this is the path to the file that your are trying to encrypt or decrypt.
|
|
33
|
+
if you use this flag it will read the file and populate the data
|
|
34
|
+
attribute.
|
|
35
|
+
|
|
36
|
+
-s, --secret this is the secret that is used to encrypt the data
|
|
37
|
+
|
|
38
|
+
-d, --data this is how you pass in the data
|
|
39
|
+
|
|
40
|
+
-j, --json this is supposed to be a way to make the output
|
|
41
|
+
by in json so that a machine can read it.
|
|
42
|
+
|
|
43
|
+
-l, --location this lets you decide which directory you are running the command in
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|