@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 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
+
@@ -0,0 +1,3 @@
1
+ {
2
+ "secret": "LqnSD01cIYesAZEjLc3qIVvnVt2jcSg6DObMHyeWcGA="
3
+ }
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
+ }
@@ -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
+ })
@@ -0,0 +1,3 @@
1
+ {
2
+ "some": "sort of data"
3
+ }
File without changes
@@ -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,3 @@
1
+ {
2
+ "secret": "RH5L+pLR1icf65Dhl+Jl6ZM7HuT8ASov7OdjlajZyJ4="
3
+ }
@@ -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
+