├── .eslintrc.js ├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── bin └── git-secure-tag ├── lib ├── git-secure-tag.js └── git-secure-tag │ ├── api.js │ ├── batch.js │ ├── common.js │ ├── hash.js │ └── legacy-hash.js ├── package-lock.json ├── package.json └── test ├── api-test.js ├── ev-tag-test.js ├── fixtures ├── .gitkeep └── index.js └── scenario-test.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "rules": { 10 | "indent": [ 11 | "error", 12 | 2 13 | ], 14 | "linebreak-style": [ 15 | "error", 16 | "unix" 17 | ], 18 | "quotes": [ 19 | "error", 20 | "single" 21 | ], 22 | "semi": [ 23 | "error", 24 | "always" 25 | ] 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | test/fixtures/scenario/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 4 4 | - 5 5 | - 6 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SRC_FILES= 2 | SRC_FILES+= lib/*.js 3 | SRC_FILES+= lib/**/*.js 4 | 5 | SRC_FILES+= test/*.js 6 | 7 | lint: 8 | eslint $(SRC_FILES) 9 | 10 | format: 11 | eslint --fix $(SRC_FILES) 12 | 13 | .PHONY: lint format 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # git-secure-tag 2 | [![NPM version](https://badge.fury.io/js/git-secure-tag.svg)](http://badge.fury.io/js/git-secure-tag) 3 | [![Build Status](https://secure.travis-ci.org/indutny/git-secure-tag.svg)](http://travis-ci.org/indutny/git-secure-tag) 4 | 5 | ## Why? 6 | 7 | `git` uses SHA-1 hashes when signing tag. SHA-1 is generally deprecated and is 8 | not a collision-safe anymore (though, ~~collisions are yet to come~~ pre-image attack is yet to come). 9 | 10 | ## How? 11 | 12 | `git-secure-tag` runs `cat-file` recursively for each 13 | entry (sorted alphabetically), enters submodules (if present), and hashes 14 | file/directory names, file contents, and submodules (recursively again) into a 15 | resulting `Git-EVTag-v0-SHA512: ...` SHA512 digest. 16 | 17 | ## Installation 18 | 19 | ```bash 20 | npm install -g git-secure-tag 21 | ``` 22 | 23 | ## Usage 24 | 25 | ```bash 26 | # Sign 27 | git secure-tag v1.2.7 -m "My tag annotation" 28 | 29 | # Verify 30 | git secure-tag -v v1.2.7 31 | ``` 32 | 33 | ## Original evtag implementation 34 | 35 | Largely inspired by: 36 | 37 | https://github.com/cgwalters/git-evtag 38 | 39 | ## LICENSE 40 | 41 | This software is licensed under the MIT License. 42 | 43 | Copyright Fedor Indutny, 2016. 44 | 45 | Permission is hereby granted, free of charge, to any person obtaining a 46 | copy of this software and associated documentation files (the 47 | "Software"), to deal in the Software without restriction, including 48 | without limitation the rights to use, copy, modify, merge, publish, 49 | distribute, sublicense, and/or sell copies of the Software, and to permit 50 | persons to whom the Software is furnished to do so, subject to the 51 | following conditions: 52 | 53 | The above copyright notice and this permission notice shall be included 54 | in all copies or substantial portions of the Software. 55 | 56 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 57 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 58 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 59 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 60 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 61 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 62 | USE OR OTHER DEALINGS IN THE SOFTWARE. 63 | -------------------------------------------------------------------------------- /bin/git-secure-tag: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const yargs = require('yargs') 3 | .usage('Usage: git secure-tag [-s | -u ] [-m ] \n' + 4 | ' [ | ]') 5 | .command('hash', 'print hash of repository contents') 6 | .alias('v', 'verify') 7 | .boolean('v') 8 | .describe('v', 'Verify the gpg signature of a given tag') 9 | .boolean('s') 10 | .boolean('f') 11 | .describe('insecure', 'Do not sign the tag') 12 | .boolean('insecure') 13 | .boolean('legacy') 14 | .alias('h', 'help') 15 | .help() 16 | const argv = yargs.argv; 17 | 18 | const gst = require('../'); 19 | 20 | if (argv._[0] === 'hash') { 21 | const hash = new gst.Hash(argv); 22 | 23 | hash.calculate(process.cwd(), argv._[1], (err, hash) => { 24 | if (err) 25 | throw err; 26 | console.log(hash); 27 | }); 28 | return; 29 | } 30 | 31 | if (argv._.length < 1) { 32 | process.stderr.write('Please specify tag\n'); 33 | yargs.showHelp(); 34 | process.exit(1); 35 | } 36 | 37 | const api = new gst.API(process.cwd()); 38 | if (argv.v) 39 | api.verify(argv._[0], argv); 40 | else 41 | api.sign(argv._[0], argv._[1], argv); 42 | -------------------------------------------------------------------------------- /lib/git-secure-tag.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | let GIT = process.env.GIT_SSH_COMMAND || process.env.GIT_SSH || 'git'; 6 | 7 | if (process.env.GIT_EXEC_PATH) { 8 | GIT = path.join(process.env.GIT_EXEC_PATH, 'git'); 9 | } 10 | 11 | exports.GIT = GIT; 12 | exports.common = require('./git-secure-tag/common'); 13 | exports.Batch = require('./git-secure-tag/batch'); 14 | exports.Hash = require('./git-secure-tag/hash'); 15 | exports.LegacyHash = require('./git-secure-tag/legacy-hash'); 16 | exports.API = require('./git-secure-tag/api'); 17 | -------------------------------------------------------------------------------- /lib/git-secure-tag/api.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const spawn = require('child_process').spawn; 4 | 5 | const async = require('async'); 6 | 7 | const gst = require('../git-secure-tag'); 8 | 9 | function API(dir) { 10 | this.dir = dir; 11 | } 12 | module.exports = API; 13 | 14 | const TAG_RE = 15 | /(?:[\r\n]|^)Git-(EVTag-v0-SHA512|Secure-Tag-V0)\s*:\s*([^\r\n]*)([\r\n]|$)/i; 16 | 17 | function buffer(stream) { 18 | let chunks = ''; 19 | let done = false; 20 | 21 | stream.on('data', chunk => chunks += chunk); 22 | stream.on('end', () => done = true); 23 | 24 | return (callback) => { 25 | if (done) 26 | return callback(null, chunks); 27 | else 28 | stream.once('end', () => callback(null, chunks)); 29 | }; 30 | } 31 | 32 | function git(args, stdio, cwd, env, callback) { 33 | const p = spawn(gst.GIT, args, { 34 | stdio: stdio, 35 | cwd: cwd, 36 | env: env 37 | }); 38 | 39 | const stdout = p.stdout ? buffer(p.stdout) : (cb) => cb(null, null); 40 | const stderr = p.stderr ? buffer(p.stderr) : (cb) => cb(null, null); 41 | 42 | async.parallel({ 43 | status: (callback) => { 44 | p.on('exit', (status) => callback(null, status)); 45 | }, 46 | stdout: stdout, 47 | stderr: stderr 48 | }, callback); 49 | } 50 | 51 | API.prototype.verify = function verify(tag, options, callback) { 52 | if (!options) 53 | options = {}; 54 | 55 | const args = options.insecure ? 56 | [ 'show', tag ] : [ 'verify-tag', '-v', tag ]; 57 | 58 | async.waterfall([ 59 | (callback) => { 60 | git(args, [ 'inherit', 'pipe', 'pipe' ], this.dir, options.env, callback); 61 | }, 62 | (p, callback) => { 63 | if (p.status !== 0) { 64 | if (callback) 65 | return callback(new Error(p.stderr.toString()), false); 66 | 67 | process.stdout.write(p.stdout.toString()); 68 | process.stderr.write(p.stderr.toString()); 69 | process.exit(1); 70 | return; 71 | } 72 | 73 | const stdout = p.stdout.toString(); 74 | 75 | const match = stdout.match(TAG_RE); 76 | if (match === null) { 77 | const msg = 'error: No `Git-EVTag-v0-SHA512: ...`, nor ' + 78 | '`Git-Secure-Tag-V0` found in tag description'; 79 | if (callback) 80 | return callback(new Error(msg), false); 81 | 82 | process.stderr.write(msg + '\n'); 83 | process.exit(1); 84 | return; 85 | } 86 | 87 | const isLegacy = match[1].toLowerCase() === 'secure-tag-v0'; 88 | 89 | const hash = new (isLegacy ? gst.LegacyHash : gst.Hash)(options); 90 | hash.calculate(this.dir, tag, (err, out) => callback(err, out, match, p)); 91 | }, 92 | (hash, match, p, callback) => { 93 | if (hash === match[2]) { 94 | callback(null, match[1], p.stderr.toString()); 95 | return; 96 | } 97 | 98 | const msg = `error: Git-${match[1]} hash mismatch\n` + 99 | `error: Expected: ${hash}\n` + 100 | `error: Found: ${match[2]}`; 101 | callback(new Error(msg), false); 102 | } 103 | ], (err, type, stderr) => { 104 | if (err) { 105 | if (callback) 106 | return callback(err, false); 107 | 108 | process.stderr.write('error: ' + err.stack + '\n'); 109 | process.exit(1); 110 | return; 111 | } 112 | 113 | if (callback) 114 | return callback(null, true); 115 | 116 | process.stderr.write(stderr); 117 | process.stderr.write(`Good Git-${type} hash\n`); 118 | process.exit(0); 119 | }); 120 | }; 121 | 122 | API.prototype.sign = function sign(tag, ref, options, callback) { 123 | // TODO(indutny): support multiple refs? 124 | if (!options) 125 | options = {}; 126 | 127 | async.waterfall([ 128 | (callback) => { 129 | const hash = new (options.legacy ? gst.LegacyHash : gst.Hash)(options); 130 | hash.calculate(this.dir, ref, callback); 131 | }, 132 | (hash, callback) => { 133 | const name = 134 | options.legacy ? 'Git-Secure-Tag-V0' : 'Git-EVTag-v0-SHA512'; 135 | const msg = (options.m || (tag + '\n')) + '\n' + 136 | `${name}: ${hash}\n`; 137 | 138 | let args = [ 'tag' ]; 139 | args.push('-m', msg); 140 | if (options.u) 141 | args.push('-u', options.u); 142 | else if (!options.insecure) 143 | args.push('-s'); 144 | if (options.f) 145 | args.push('-f'); 146 | args.push(tag); 147 | if (ref !== undefined) 148 | args.push(ref); 149 | 150 | git(args, 'inherit', this.dir, options.env, callback); 151 | }, 152 | (proc, callback) => { 153 | callback(proc.status === 0 ? null : new Error('git failure')); 154 | } 155 | ], (err) => { 156 | if (err) { 157 | if (callback) 158 | return callback(err); 159 | 160 | process.stderr.write(err.stack + '\n'); 161 | process.exit(1); 162 | return; 163 | } 164 | 165 | if (callback) 166 | return callback(null); 167 | 168 | process.exit(0); 169 | }); 170 | }; 171 | -------------------------------------------------------------------------------- /lib/git-secure-tag/batch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const stream = require('stream'); 4 | const spawn = require('child_process').spawn; 5 | 6 | const gst = require('../git-secure-tag'); 7 | 8 | function Batch(dir) { 9 | this.dir = dir; 10 | // TODO(indutny): symlinks? 11 | this.proc = spawn(gst.GIT, [ 'cat-file', '--batch' ], { 12 | stdio: [ 'pipe', 'pipe', null ], 13 | cwd: this.dir 14 | }); 15 | 16 | this.proc.on('exit', code => this.onExit(code)); 17 | this.proc.stdout.on('data', data => this.onData(data)); 18 | 19 | this.queue = []; 20 | this.state = 'header'; 21 | this.header = ''; 22 | this.body = null; 23 | } 24 | module.exports = Batch; 25 | 26 | Batch.prototype.destroy = function destroy() { 27 | this.proc.kill(); 28 | }; 29 | 30 | Batch.prototype.onExit = function onExit(code) { 31 | const err = new Error(`git cat-file --batch (cwd: ${this.dir}) exit ${code}`); 32 | 33 | const q = this.queue; 34 | this.queue = null; 35 | q.forEach((entry) => { 36 | entry.stream.emit('error', err); 37 | }); 38 | }; 39 | 40 | Batch.prototype.onData = function onData(data) { 41 | while (data.length !== 0) { 42 | if (this.state === 'header') { 43 | let i; 44 | for (i = 0; i < data.length; i++) 45 | if (data[i] === 0x0a /* '\n' */) 46 | break; 47 | this.header += data.slice(0, i); 48 | 49 | if (i === data.length) 50 | break; 51 | 52 | // Skip '\n' 53 | data = data.slice(i + 1); 54 | const header = this.header; 55 | this.header = ''; 56 | this.onHeader(header); 57 | continue; 58 | } else if (this.state === 'body' && data.length !== 0) { 59 | data = this.onBody(data); 60 | continue; 61 | } else if (this.state === 'body-end' && data.length !== 0) { 62 | // Skip '\n' 63 | data = data.slice(1); 64 | this.state = 'header'; 65 | continue; 66 | } 67 | } 68 | }; 69 | 70 | Batch.prototype.onHeader = function onHeader(header) { 71 | const parts = header.split(/\s/g); 72 | if (parts[1] === 'missing') 73 | this.onMissing(parts[0]); 74 | else if (/^[a-z0-9]{40}$/.test(parts[0])) 75 | this.onEntry(parts[0], parts[1], parts[2]); 76 | else 77 | throw new Error(`Unexpected header ${header}`); 78 | }; 79 | 80 | Batch.prototype.getEntry = function getEntry(object) { 81 | const entry = this.queue[0]; 82 | if (!entry) 83 | throw new Error(`Unexpected reply for ${object}`); 84 | 85 | return entry; 86 | }; 87 | 88 | Batch.prototype.onMissing = function onMissing(object) { 89 | const entry = this.getEntry(object); 90 | this.queue.shift(); 91 | entry.stream.emit('error', new Error('missing ' + object)); 92 | }; 93 | 94 | Batch.prototype.onEntry = function onEntry(object, type, size) { 95 | const entry = this.getEntry(object); 96 | 97 | entry.stream.emit('header', type, size); 98 | 99 | this.state = 'body'; 100 | // floats store 52 bits of integer precision 101 | this.waiting = parseFloat(size); 102 | }; 103 | 104 | Batch.prototype.onBody = function onBody(chunk) { 105 | const entry = this.queue[0]; 106 | 107 | const rest = chunk.slice(this.waiting); 108 | entry.stream.push(chunk.slice(0, this.waiting)); 109 | this.waiting -= chunk.length; 110 | if (this.waiting > 0) 111 | return rest; 112 | 113 | this.state = 'body-end'; 114 | this.waiting = 0; 115 | this.queue.shift(); 116 | entry.stream.push(null); 117 | 118 | return rest; 119 | }; 120 | 121 | Batch.prototype.query = function query(object) { 122 | const out = new stream.PassThrough(); 123 | this.queue.push({ object: object, stream: out }); 124 | 125 | this.proc.stdin.write(object + '\n'); 126 | 127 | return out; 128 | }; 129 | -------------------------------------------------------------------------------- /lib/git-secure-tag/common.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function parseTree(buf) { 4 | let off = 0; 5 | let entries = []; 6 | while (off < buf.length) { 7 | let i; 8 | for (i = off; i < buf.length; i++) 9 | if (buf[i] === 0x00) 10 | break; 11 | if (i + 1 >= buf.length) 12 | throw new Error('Tree entry\'s name not found'); 13 | 14 | const name = buf.slice(off, i).toString(); 15 | off = i + 1; 16 | 17 | if (off + 20 > buf.length) 18 | throw new Error('Not enough space for tree entry\'s hash'); 19 | 20 | const hash = buf.slice(off, off + 20).toString('hex'); 21 | off += 20; 22 | 23 | const parts = name.split(' '); 24 | entries.push({ 25 | mode: parts[0], 26 | name: parts[1], 27 | hash: hash 28 | }); 29 | } 30 | return entries; 31 | } 32 | exports.parseTree = parseTree; 33 | -------------------------------------------------------------------------------- /lib/git-secure-tag/hash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const path = require('path'); 5 | const crypto = require('crypto'); 6 | const Buffer = require('buffer').Buffer; 7 | 8 | const async = require('async'); 9 | 10 | const gst = require('../git-secure-tag'); 11 | 12 | function Hash(options) { 13 | this.options = options || {}; 14 | } 15 | module.exports = Hash; 16 | 17 | function State(digest, root, dir, batch) { 18 | this.digest = digest; 19 | this.root = root; 20 | this.dir = dir; 21 | this.batch = batch; 22 | } 23 | 24 | State.prototype.query = function query(hash) { 25 | return this.batch.query(hash); 26 | }; 27 | 28 | State.prototype.update = function update(data) { 29 | this.digest.update(data); 30 | }; 31 | 32 | State.prototype.enter = function enter(part) { 33 | const subdir = this.dir === '' ? part : this.dir + '/' + part; 34 | return new State(this.digest, this.root, subdir, this.batch); 35 | }; 36 | 37 | Hash.prototype._visit = function _visit(state, hash, callback) { 38 | const stream = state.query(hash); 39 | 40 | stream.on('header', (type, size) => { 41 | if (type !== 'tag') 42 | state.update(type + ' ' + size +'\0'); 43 | 44 | const chunks = []; 45 | stream.on('data', (data) => { 46 | if (type !== 'tag') 47 | state.update(data); 48 | 49 | if (type === 'blob') 50 | return; 51 | 52 | chunks.push(data); 53 | }); 54 | 55 | stream.on('end', () => { 56 | if (type === 'blob') 57 | return callback(null); 58 | 59 | const content = Buffer.concat(chunks); 60 | 61 | if (type === 'tree') 62 | this._visitTree(state, content, callback); 63 | else if (type === 'commit') 64 | this._visitCommit(state, hash, content, callback); 65 | else if (type === 'tag') 66 | this._visitTag(state, hash, content, callback); 67 | else 68 | throw new Error(`Unexpected type ${type}`); 69 | }); 70 | }); 71 | 72 | stream.on('error', (err) => callback(err)); 73 | }; 74 | 75 | Hash.prototype._visitTree = function _visitTree(state, content, callback) { 76 | const tree = gst.common.parseTree(content); 77 | 78 | async.forEachSeries(tree, (node, callback) => { 79 | const subState = state.enter(node.name); 80 | 81 | if (node.mode === '160000') 82 | return this._visitSubmodule(subState, node.hash, callback); 83 | 84 | this._visit(subState, node.hash, callback); 85 | }, (err) => { 86 | if (err && !/while loading/.test(err.message)) 87 | err.message += `\nwhile loading tree at ${state.dir || '.'}`; 88 | callback(err); 89 | }); 90 | }; 91 | 92 | Hash.prototype._visitCommit = function _visitCommit(state, hash, content, 93 | callback) { 94 | content = content.toString(); 95 | 96 | // Root commit 97 | assert.equal(state.dir, '', 'Unexpected commit in a tree!'); 98 | 99 | const match = content.match(/(?:^|[\r\n])tree ([a-z0-9]{40})/); 100 | assert(match !== null, 'Commit without `tree`'); 101 | 102 | this._visit(state, match[1], (err) => { 103 | if (err && !/while loading/.test(err.message)) 104 | err.message += `\nwhile loading commit ${hash}`; 105 | 106 | callback(err); 107 | }); 108 | }; 109 | 110 | Hash.prototype._visitTag = function _visitTag(state, ref, content, callback) { 111 | content = content.toString(); 112 | const match = content.match(/(?:^|[\r\n])object ([a-z0-9]{40})/); 113 | assert(match !== null, 'Tag without `object`'); 114 | 115 | this._visit(state, match[1], (err) => { 116 | if (err && !/while loading/.test(err.message)) 117 | err.message += `\nwhile loading tag ${ref}`; 118 | 119 | callback(err); 120 | }); 121 | }; 122 | 123 | Hash.prototype._visitSubmodule = function _visitSubmodule(state, hash, 124 | callback) { 125 | // TODO(indutny): windows-style paths? 126 | const moduleRoot = path.join(state.root, state.dir); 127 | const batch = new gst.Batch(moduleRoot); 128 | const moduleState = new State(state.digest, moduleRoot, '', batch); 129 | 130 | this._visit(moduleState, hash, (err) => { 131 | if (err && !/while loading/.test(err.message)) { 132 | err.message += 133 | '\nwhile loading submodule at ' + 134 | `${path.relative(state.root, moduleRoot)}`; 135 | } 136 | 137 | batch.destroy(); 138 | callback(err); 139 | }); 140 | }; 141 | 142 | Hash.prototype.calculate = function calculate(dir, hash, callback) { 143 | const digest = crypto.createHash('sha512'); 144 | const batch = new gst.Batch(dir); 145 | const state = new State(digest, dir, '', batch); 146 | 147 | this._visit(state, hash || 'HEAD', (err) => { 148 | batch.destroy(); 149 | 150 | if (err) 151 | return callback(err); 152 | 153 | callback(null, digest.digest('hex')); 154 | }); 155 | }; 156 | -------------------------------------------------------------------------------- /lib/git-secure-tag/legacy-hash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const path = require('path'); 5 | const crypto = require('crypto'); 6 | const Buffer = require('buffer').Buffer; 7 | 8 | const async = require('async'); 9 | 10 | const gst = require('../git-secure-tag'); 11 | 12 | function Hash(options) { 13 | this.options = options || {}; 14 | this.cwd = null; 15 | } 16 | module.exports = Hash; 17 | 18 | function compareEntries(a, b) { 19 | return a.name > b.name ? 1 : a.name < b.name ? -1 : 0; 20 | } 21 | 22 | Hash.prototype.hashBlob = function hashBlob(batch, blob, callback) { 23 | const hash = crypto.createHash('sha512'); 24 | 25 | const stream = batch.query(blob); 26 | 27 | stream.pipe(hash); 28 | stream.on('error', err => callback(err)); 29 | 30 | let content = []; 31 | hash.on('data', chunk => content.push(chunk)); 32 | hash.on('end', () => { 33 | content = Buffer.concat(content); 34 | callback(null, content.toString('hex')); 35 | }); 36 | }; 37 | 38 | Hash.prototype.hashSubmodule = function hashSubmodule(dir, ref, callback) { 39 | const hash = new Hash(this.options); 40 | hash._calculate(ref, dir, path.join(this.cwd, dir), callback); 41 | }; 42 | 43 | Hash.prototype.flattenTree = function flattenTree(batch, dir, tree, callback) { 44 | const stream = batch.query(tree); 45 | 46 | let content = []; 47 | stream.on('data', chunk => content.push(chunk)); 48 | stream.on('error', err => callback(err)); 49 | 50 | const recurse = (entry, callback) => { 51 | const fullPath = dir === '' ? entry.name : dir + '/' + entry.name; 52 | 53 | function next(err, content) { 54 | entry.content = content; 55 | callback(err, entry); 56 | } 57 | 58 | if (entry.mode === '40000') { 59 | return this.flattenTree(batch, fullPath, entry.hash, next); 60 | } else if (entry.mode === '160000') { 61 | return this.hashSubmodule(fullPath, entry.hash, next); 62 | } 63 | 64 | return this.hashBlob(batch, entry.hash, next); 65 | }; 66 | 67 | stream.on('end', () => { 68 | content = Buffer.concat(content); 69 | content = gst.common.parseTree(content); 70 | content.sort(compareEntries); 71 | 72 | async.map(content, recurse, callback); 73 | }); 74 | }; 75 | 76 | Hash.prototype.digest = function digest(hash, line) { 77 | hash.update(line + '\n'); 78 | if (this.options.verbose) 79 | process.stdout.write(line + '\n'); 80 | }; 81 | 82 | Hash.prototype.hashTree = function hashTree(tree, digest) { 83 | tree.forEach((entry) => { 84 | const line = entry.mode + ' ' + entry.name + ' ' + entry.hash; 85 | if (Array.isArray(entry.content)) { 86 | this.digest(digest, line + ' ' + entry.content.length); 87 | this.hashTree(entry.content, digest); 88 | } else { 89 | this.digest(digest, line + ' ' + entry.content); 90 | } 91 | }); 92 | }; 93 | 94 | Hash.prototype.getTree = function getTree(batch, ref, callback) { 95 | const stream = batch.query(ref); 96 | 97 | stream.on('header', (type) => { 98 | let content = ''; 99 | stream.on('data', chunk => content += chunk); 100 | stream.once('end', () => { 101 | if (type === 'tag') { 102 | const match = content.match(/(?:^|[\r\n])object ([a-z0-9]{40})/); 103 | assert(match !== null, 'Tag without `object`'); 104 | return this.getTree(batch, match[1], callback); 105 | } 106 | assert.equal(type, 'commit'); 107 | 108 | const match = content.match(/(?:^|[\r\n])tree ([a-z0-9]{40})/); 109 | assert(match !== null, 'Commit without `tree`'); 110 | return callback(null, match[1]); 111 | }); 112 | }); 113 | }; 114 | 115 | Hash.prototype._calculate = function _calculate(hash, relative, dir, callback) { 116 | const digest = crypto.createHash('sha512'); 117 | const batch = new gst.Batch(dir); 118 | 119 | this.cwd = dir; 120 | 121 | // `update-index`? 122 | async.waterfall([ 123 | (callback) => { 124 | this.getTree(batch, hash || 'HEAD', callback); 125 | }, 126 | (hash, callback) => { 127 | this.flattenTree(batch, relative, hash, 128 | (err, tree) => callback(err, tree, hash)); 129 | } 130 | ], (err, tree, hash) => { 131 | if (err) 132 | return callback(err); 133 | 134 | batch.destroy(); 135 | this.digest(digest, 'write-tree ' + hash + ' ' + tree.length); 136 | this.hashTree(tree, digest); 137 | callback(null, digest.digest('hex')); 138 | }); 139 | }; 140 | 141 | Hash.prototype.calculate = function calculate(dir, hash, callback) { 142 | this._calculate(hash, '', dir, callback); 143 | }; 144 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "git-secure-tag", 3 | "version": "2.3.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "acorn": { 8 | "version": "5.7.3", 9 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", 10 | "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", 11 | "dev": true 12 | }, 13 | "acorn-jsx": { 14 | "version": "3.0.1", 15 | "resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", 16 | "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", 17 | "dev": true, 18 | "requires": { 19 | "acorn": "^3.0.4" 20 | }, 21 | "dependencies": { 22 | "acorn": { 23 | "version": "3.3.0", 24 | "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", 25 | "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", 26 | "dev": true 27 | } 28 | } 29 | }, 30 | "ajv": { 31 | "version": "4.11.8", 32 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", 33 | "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", 34 | "dev": true, 35 | "requires": { 36 | "co": "^4.6.0", 37 | "json-stable-stringify": "^1.0.1" 38 | } 39 | }, 40 | "ajv-keywords": { 41 | "version": "1.5.1", 42 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", 43 | "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", 44 | "dev": true 45 | }, 46 | "ansi-escapes": { 47 | "version": "1.4.0", 48 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", 49 | "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", 50 | "dev": true 51 | }, 52 | "ansi-regex": { 53 | "version": "2.1.1", 54 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 55 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 56 | }, 57 | "ansi-styles": { 58 | "version": "2.2.1", 59 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 60 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", 61 | "dev": true 62 | }, 63 | "argparse": { 64 | "version": "1.0.10", 65 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 66 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 67 | "dev": true, 68 | "requires": { 69 | "sprintf-js": "~1.0.2" 70 | } 71 | }, 72 | "array-union": { 73 | "version": "1.0.2", 74 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 75 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", 76 | "dev": true, 77 | "requires": { 78 | "array-uniq": "^1.0.1" 79 | } 80 | }, 81 | "array-uniq": { 82 | "version": "1.0.3", 83 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 84 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", 85 | "dev": true 86 | }, 87 | "arrify": { 88 | "version": "1.0.1", 89 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 90 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", 91 | "dev": true 92 | }, 93 | "async": { 94 | "version": "2.6.1", 95 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", 96 | "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", 97 | "requires": { 98 | "lodash": "^4.17.10" 99 | } 100 | }, 101 | "babel-code-frame": { 102 | "version": "6.26.0", 103 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", 104 | "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", 105 | "dev": true, 106 | "requires": { 107 | "chalk": "^1.1.3", 108 | "esutils": "^2.0.2", 109 | "js-tokens": "^3.0.2" 110 | } 111 | }, 112 | "balanced-match": { 113 | "version": "1.0.0", 114 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 115 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 116 | "dev": true 117 | }, 118 | "brace-expansion": { 119 | "version": "1.1.11", 120 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 121 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 122 | "dev": true, 123 | "requires": { 124 | "balanced-match": "^1.0.0", 125 | "concat-map": "0.0.1" 126 | } 127 | }, 128 | "buffer-from": { 129 | "version": "1.1.1", 130 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 131 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 132 | "dev": true 133 | }, 134 | "builtin-modules": { 135 | "version": "1.1.1", 136 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 137 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" 138 | }, 139 | "caller-path": { 140 | "version": "0.1.0", 141 | "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", 142 | "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", 143 | "dev": true, 144 | "requires": { 145 | "callsites": "^0.2.0" 146 | } 147 | }, 148 | "callsites": { 149 | "version": "0.2.0", 150 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", 151 | "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", 152 | "dev": true 153 | }, 154 | "camelcase": { 155 | "version": "3.0.0", 156 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", 157 | "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" 158 | }, 159 | "chalk": { 160 | "version": "1.1.3", 161 | "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 162 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 163 | "dev": true, 164 | "requires": { 165 | "ansi-styles": "^2.2.1", 166 | "escape-string-regexp": "^1.0.2", 167 | "has-ansi": "^2.0.0", 168 | "strip-ansi": "^3.0.0", 169 | "supports-color": "^2.0.0" 170 | } 171 | }, 172 | "circular-json": { 173 | "version": "0.3.3", 174 | "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", 175 | "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", 176 | "dev": true 177 | }, 178 | "cli-cursor": { 179 | "version": "1.0.2", 180 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", 181 | "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", 182 | "dev": true, 183 | "requires": { 184 | "restore-cursor": "^1.0.1" 185 | } 186 | }, 187 | "cli-width": { 188 | "version": "2.2.0", 189 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", 190 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", 191 | "dev": true 192 | }, 193 | "cliui": { 194 | "version": "3.2.0", 195 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", 196 | "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", 197 | "requires": { 198 | "string-width": "^1.0.1", 199 | "strip-ansi": "^3.0.1", 200 | "wrap-ansi": "^2.0.0" 201 | } 202 | }, 203 | "co": { 204 | "version": "4.6.0", 205 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 206 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", 207 | "dev": true 208 | }, 209 | "code-point-at": { 210 | "version": "1.1.0", 211 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 212 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 213 | }, 214 | "concat-map": { 215 | "version": "0.0.1", 216 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 217 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 218 | "dev": true 219 | }, 220 | "concat-stream": { 221 | "version": "1.6.2", 222 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 223 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 224 | "dev": true, 225 | "requires": { 226 | "buffer-from": "^1.0.0", 227 | "inherits": "^2.0.3", 228 | "readable-stream": "^2.2.2", 229 | "typedarray": "^0.0.6" 230 | } 231 | }, 232 | "core-util-is": { 233 | "version": "1.0.2", 234 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 235 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 236 | "dev": true 237 | }, 238 | "d": { 239 | "version": "1.0.0", 240 | "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", 241 | "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", 242 | "dev": true, 243 | "requires": { 244 | "es5-ext": "^0.10.9" 245 | } 246 | }, 247 | "debug": { 248 | "version": "2.6.9", 249 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 250 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 251 | "dev": true, 252 | "requires": { 253 | "ms": "2.0.0" 254 | } 255 | }, 256 | "decamelize": { 257 | "version": "1.2.0", 258 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 259 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 260 | }, 261 | "deep-equal": { 262 | "version": "1.0.1", 263 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", 264 | "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", 265 | "dev": true 266 | }, 267 | "deep-is": { 268 | "version": "0.1.3", 269 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 270 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 271 | "dev": true 272 | }, 273 | "define-properties": { 274 | "version": "1.1.3", 275 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 276 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 277 | "dev": true, 278 | "requires": { 279 | "object-keys": "^1.0.12" 280 | } 281 | }, 282 | "defined": { 283 | "version": "1.0.0", 284 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", 285 | "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", 286 | "dev": true 287 | }, 288 | "del": { 289 | "version": "2.2.2", 290 | "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", 291 | "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", 292 | "dev": true, 293 | "requires": { 294 | "globby": "^5.0.0", 295 | "is-path-cwd": "^1.0.0", 296 | "is-path-in-cwd": "^1.0.0", 297 | "object-assign": "^4.0.1", 298 | "pify": "^2.0.0", 299 | "pinkie-promise": "^2.0.0", 300 | "rimraf": "^2.2.8" 301 | } 302 | }, 303 | "doctrine": { 304 | "version": "2.1.0", 305 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", 306 | "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", 307 | "dev": true, 308 | "requires": { 309 | "esutils": "^2.0.2" 310 | } 311 | }, 312 | "error-ex": { 313 | "version": "1.3.2", 314 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 315 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 316 | "requires": { 317 | "is-arrayish": "^0.2.1" 318 | } 319 | }, 320 | "es-abstract": { 321 | "version": "1.12.0", 322 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", 323 | "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", 324 | "dev": true, 325 | "requires": { 326 | "es-to-primitive": "^1.1.1", 327 | "function-bind": "^1.1.1", 328 | "has": "^1.0.1", 329 | "is-callable": "^1.1.3", 330 | "is-regex": "^1.0.4" 331 | } 332 | }, 333 | "es-to-primitive": { 334 | "version": "1.2.0", 335 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", 336 | "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", 337 | "dev": true, 338 | "requires": { 339 | "is-callable": "^1.1.4", 340 | "is-date-object": "^1.0.1", 341 | "is-symbol": "^1.0.2" 342 | } 343 | }, 344 | "es5-ext": { 345 | "version": "0.10.46", 346 | "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", 347 | "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", 348 | "dev": true, 349 | "requires": { 350 | "es6-iterator": "~2.0.3", 351 | "es6-symbol": "~3.1.1", 352 | "next-tick": "1" 353 | } 354 | }, 355 | "es6-iterator": { 356 | "version": "2.0.3", 357 | "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", 358 | "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", 359 | "dev": true, 360 | "requires": { 361 | "d": "1", 362 | "es5-ext": "^0.10.35", 363 | "es6-symbol": "^3.1.1" 364 | } 365 | }, 366 | "es6-map": { 367 | "version": "0.1.5", 368 | "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", 369 | "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", 370 | "dev": true, 371 | "requires": { 372 | "d": "1", 373 | "es5-ext": "~0.10.14", 374 | "es6-iterator": "~2.0.1", 375 | "es6-set": "~0.1.5", 376 | "es6-symbol": "~3.1.1", 377 | "event-emitter": "~0.3.5" 378 | } 379 | }, 380 | "es6-set": { 381 | "version": "0.1.5", 382 | "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", 383 | "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", 384 | "dev": true, 385 | "requires": { 386 | "d": "1", 387 | "es5-ext": "~0.10.14", 388 | "es6-iterator": "~2.0.1", 389 | "es6-symbol": "3.1.1", 390 | "event-emitter": "~0.3.5" 391 | } 392 | }, 393 | "es6-symbol": { 394 | "version": "3.1.1", 395 | "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", 396 | "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", 397 | "dev": true, 398 | "requires": { 399 | "d": "1", 400 | "es5-ext": "~0.10.14" 401 | } 402 | }, 403 | "es6-weak-map": { 404 | "version": "2.0.2", 405 | "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", 406 | "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", 407 | "dev": true, 408 | "requires": { 409 | "d": "1", 410 | "es5-ext": "^0.10.14", 411 | "es6-iterator": "^2.0.1", 412 | "es6-symbol": "^3.1.1" 413 | } 414 | }, 415 | "escape-string-regexp": { 416 | "version": "1.0.5", 417 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 418 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 419 | "dev": true 420 | }, 421 | "escope": { 422 | "version": "3.6.0", 423 | "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", 424 | "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", 425 | "dev": true, 426 | "requires": { 427 | "es6-map": "^0.1.3", 428 | "es6-weak-map": "^2.0.1", 429 | "esrecurse": "^4.1.0", 430 | "estraverse": "^4.1.1" 431 | } 432 | }, 433 | "eslint": { 434 | "version": "3.19.0", 435 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", 436 | "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", 437 | "dev": true, 438 | "requires": { 439 | "babel-code-frame": "^6.16.0", 440 | "chalk": "^1.1.3", 441 | "concat-stream": "^1.5.2", 442 | "debug": "^2.1.1", 443 | "doctrine": "^2.0.0", 444 | "escope": "^3.6.0", 445 | "espree": "^3.4.0", 446 | "esquery": "^1.0.0", 447 | "estraverse": "^4.2.0", 448 | "esutils": "^2.0.2", 449 | "file-entry-cache": "^2.0.0", 450 | "glob": "^7.0.3", 451 | "globals": "^9.14.0", 452 | "ignore": "^3.2.0", 453 | "imurmurhash": "^0.1.4", 454 | "inquirer": "^0.12.0", 455 | "is-my-json-valid": "^2.10.0", 456 | "is-resolvable": "^1.0.0", 457 | "js-yaml": "^3.5.1", 458 | "json-stable-stringify": "^1.0.0", 459 | "levn": "^0.3.0", 460 | "lodash": "^4.0.0", 461 | "mkdirp": "^0.5.0", 462 | "natural-compare": "^1.4.0", 463 | "optionator": "^0.8.2", 464 | "path-is-inside": "^1.0.1", 465 | "pluralize": "^1.2.1", 466 | "progress": "^1.1.8", 467 | "require-uncached": "^1.0.2", 468 | "shelljs": "^0.7.5", 469 | "strip-bom": "^3.0.0", 470 | "strip-json-comments": "~2.0.1", 471 | "table": "^3.7.8", 472 | "text-table": "~0.2.0", 473 | "user-home": "^2.0.0" 474 | }, 475 | "dependencies": { 476 | "strip-bom": { 477 | "version": "3.0.0", 478 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 479 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", 480 | "dev": true 481 | } 482 | } 483 | }, 484 | "espree": { 485 | "version": "3.5.4", 486 | "resolved": "http://registry.npmjs.org/espree/-/espree-3.5.4.tgz", 487 | "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", 488 | "dev": true, 489 | "requires": { 490 | "acorn": "^5.5.0", 491 | "acorn-jsx": "^3.0.0" 492 | } 493 | }, 494 | "esprima": { 495 | "version": "4.0.1", 496 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 497 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 498 | "dev": true 499 | }, 500 | "esquery": { 501 | "version": "1.0.1", 502 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", 503 | "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", 504 | "dev": true, 505 | "requires": { 506 | "estraverse": "^4.0.0" 507 | } 508 | }, 509 | "esrecurse": { 510 | "version": "4.2.1", 511 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", 512 | "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", 513 | "dev": true, 514 | "requires": { 515 | "estraverse": "^4.1.0" 516 | } 517 | }, 518 | "estraverse": { 519 | "version": "4.2.0", 520 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", 521 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", 522 | "dev": true 523 | }, 524 | "esutils": { 525 | "version": "2.0.2", 526 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 527 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 528 | "dev": true 529 | }, 530 | "event-emitter": { 531 | "version": "0.3.5", 532 | "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", 533 | "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", 534 | "dev": true, 535 | "requires": { 536 | "d": "1", 537 | "es5-ext": "~0.10.14" 538 | } 539 | }, 540 | "exit-hook": { 541 | "version": "1.1.1", 542 | "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", 543 | "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", 544 | "dev": true 545 | }, 546 | "fast-levenshtein": { 547 | "version": "2.0.6", 548 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 549 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 550 | "dev": true 551 | }, 552 | "figures": { 553 | "version": "1.7.0", 554 | "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", 555 | "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", 556 | "dev": true, 557 | "requires": { 558 | "escape-string-regexp": "^1.0.5", 559 | "object-assign": "^4.1.0" 560 | } 561 | }, 562 | "file-entry-cache": { 563 | "version": "2.0.0", 564 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", 565 | "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", 566 | "dev": true, 567 | "requires": { 568 | "flat-cache": "^1.2.1", 569 | "object-assign": "^4.0.1" 570 | } 571 | }, 572 | "find-up": { 573 | "version": "1.1.2", 574 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", 575 | "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", 576 | "requires": { 577 | "path-exists": "^2.0.0", 578 | "pinkie-promise": "^2.0.0" 579 | } 580 | }, 581 | "flat-cache": { 582 | "version": "1.3.0", 583 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", 584 | "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", 585 | "dev": true, 586 | "requires": { 587 | "circular-json": "^0.3.1", 588 | "del": "^2.0.2", 589 | "graceful-fs": "^4.1.2", 590 | "write": "^0.2.1" 591 | } 592 | }, 593 | "for-each": { 594 | "version": "0.3.3", 595 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", 596 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", 597 | "dev": true, 598 | "requires": { 599 | "is-callable": "^1.1.3" 600 | } 601 | }, 602 | "fs.realpath": { 603 | "version": "1.0.0", 604 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 605 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 606 | "dev": true 607 | }, 608 | "function-bind": { 609 | "version": "1.1.1", 610 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 611 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 612 | "dev": true 613 | }, 614 | "generate-function": { 615 | "version": "2.3.1", 616 | "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", 617 | "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", 618 | "dev": true, 619 | "requires": { 620 | "is-property": "^1.0.2" 621 | } 622 | }, 623 | "generate-object-property": { 624 | "version": "1.2.0", 625 | "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", 626 | "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", 627 | "dev": true, 628 | "requires": { 629 | "is-property": "^1.0.0" 630 | } 631 | }, 632 | "get-caller-file": { 633 | "version": "1.0.3", 634 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", 635 | "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" 636 | }, 637 | "glob": { 638 | "version": "7.1.3", 639 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 640 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 641 | "dev": true, 642 | "requires": { 643 | "fs.realpath": "^1.0.0", 644 | "inflight": "^1.0.4", 645 | "inherits": "2", 646 | "minimatch": "^3.0.4", 647 | "once": "^1.3.0", 648 | "path-is-absolute": "^1.0.0" 649 | } 650 | }, 651 | "globals": { 652 | "version": "9.18.0", 653 | "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", 654 | "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", 655 | "dev": true 656 | }, 657 | "globby": { 658 | "version": "5.0.0", 659 | "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", 660 | "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", 661 | "dev": true, 662 | "requires": { 663 | "array-union": "^1.0.1", 664 | "arrify": "^1.0.0", 665 | "glob": "^7.0.3", 666 | "object-assign": "^4.0.1", 667 | "pify": "^2.0.0", 668 | "pinkie-promise": "^2.0.0" 669 | } 670 | }, 671 | "graceful-fs": { 672 | "version": "4.1.11", 673 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 674 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" 675 | }, 676 | "has": { 677 | "version": "1.0.3", 678 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 679 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 680 | "dev": true, 681 | "requires": { 682 | "function-bind": "^1.1.1" 683 | } 684 | }, 685 | "has-ansi": { 686 | "version": "2.0.0", 687 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 688 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 689 | "dev": true, 690 | "requires": { 691 | "ansi-regex": "^2.0.0" 692 | } 693 | }, 694 | "has-symbols": { 695 | "version": "1.0.0", 696 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", 697 | "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", 698 | "dev": true 699 | }, 700 | "hosted-git-info": { 701 | "version": "2.7.1", 702 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", 703 | "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" 704 | }, 705 | "ignore": { 706 | "version": "3.3.10", 707 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", 708 | "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", 709 | "dev": true 710 | }, 711 | "imurmurhash": { 712 | "version": "0.1.4", 713 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 714 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 715 | "dev": true 716 | }, 717 | "inflight": { 718 | "version": "1.0.6", 719 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 720 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 721 | "dev": true, 722 | "requires": { 723 | "once": "^1.3.0", 724 | "wrappy": "1" 725 | } 726 | }, 727 | "inherits": { 728 | "version": "2.0.3", 729 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 730 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 731 | "dev": true 732 | }, 733 | "inquirer": { 734 | "version": "0.12.0", 735 | "resolved": "http://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", 736 | "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", 737 | "dev": true, 738 | "requires": { 739 | "ansi-escapes": "^1.1.0", 740 | "ansi-regex": "^2.0.0", 741 | "chalk": "^1.0.0", 742 | "cli-cursor": "^1.0.1", 743 | "cli-width": "^2.0.0", 744 | "figures": "^1.3.5", 745 | "lodash": "^4.3.0", 746 | "readline2": "^1.0.1", 747 | "run-async": "^0.1.0", 748 | "rx-lite": "^3.1.2", 749 | "string-width": "^1.0.1", 750 | "strip-ansi": "^3.0.0", 751 | "through": "^2.3.6" 752 | } 753 | }, 754 | "interpret": { 755 | "version": "1.1.0", 756 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", 757 | "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", 758 | "dev": true 759 | }, 760 | "invert-kv": { 761 | "version": "1.0.0", 762 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", 763 | "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" 764 | }, 765 | "is-arrayish": { 766 | "version": "0.2.1", 767 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 768 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" 769 | }, 770 | "is-builtin-module": { 771 | "version": "1.0.0", 772 | "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", 773 | "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", 774 | "requires": { 775 | "builtin-modules": "^1.0.0" 776 | } 777 | }, 778 | "is-callable": { 779 | "version": "1.1.4", 780 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", 781 | "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", 782 | "dev": true 783 | }, 784 | "is-date-object": { 785 | "version": "1.0.1", 786 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", 787 | "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", 788 | "dev": true 789 | }, 790 | "is-fullwidth-code-point": { 791 | "version": "1.0.0", 792 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 793 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 794 | "requires": { 795 | "number-is-nan": "^1.0.0" 796 | } 797 | }, 798 | "is-my-ip-valid": { 799 | "version": "1.0.0", 800 | "resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz", 801 | "integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==", 802 | "dev": true 803 | }, 804 | "is-my-json-valid": { 805 | "version": "2.19.0", 806 | "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz", 807 | "integrity": "sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q==", 808 | "dev": true, 809 | "requires": { 810 | "generate-function": "^2.0.0", 811 | "generate-object-property": "^1.1.0", 812 | "is-my-ip-valid": "^1.0.0", 813 | "jsonpointer": "^4.0.0", 814 | "xtend": "^4.0.0" 815 | } 816 | }, 817 | "is-path-cwd": { 818 | "version": "1.0.0", 819 | "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", 820 | "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", 821 | "dev": true 822 | }, 823 | "is-path-in-cwd": { 824 | "version": "1.0.1", 825 | "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", 826 | "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", 827 | "dev": true, 828 | "requires": { 829 | "is-path-inside": "^1.0.0" 830 | } 831 | }, 832 | "is-path-inside": { 833 | "version": "1.0.1", 834 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", 835 | "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", 836 | "dev": true, 837 | "requires": { 838 | "path-is-inside": "^1.0.1" 839 | } 840 | }, 841 | "is-property": { 842 | "version": "1.0.2", 843 | "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", 844 | "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", 845 | "dev": true 846 | }, 847 | "is-regex": { 848 | "version": "1.0.4", 849 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", 850 | "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", 851 | "dev": true, 852 | "requires": { 853 | "has": "^1.0.1" 854 | } 855 | }, 856 | "is-resolvable": { 857 | "version": "1.1.0", 858 | "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", 859 | "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", 860 | "dev": true 861 | }, 862 | "is-symbol": { 863 | "version": "1.0.2", 864 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", 865 | "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", 866 | "dev": true, 867 | "requires": { 868 | "has-symbols": "^1.0.0" 869 | } 870 | }, 871 | "is-utf8": { 872 | "version": "0.2.1", 873 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", 874 | "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" 875 | }, 876 | "isarray": { 877 | "version": "1.0.0", 878 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 879 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 880 | "dev": true 881 | }, 882 | "js-tokens": { 883 | "version": "3.0.2", 884 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", 885 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", 886 | "dev": true 887 | }, 888 | "js-yaml": { 889 | "version": "3.12.0", 890 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", 891 | "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", 892 | "dev": true, 893 | "requires": { 894 | "argparse": "^1.0.7", 895 | "esprima": "^4.0.0" 896 | } 897 | }, 898 | "json-stable-stringify": { 899 | "version": "1.0.1", 900 | "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", 901 | "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", 902 | "dev": true, 903 | "requires": { 904 | "jsonify": "~0.0.0" 905 | } 906 | }, 907 | "jsonify": { 908 | "version": "0.0.0", 909 | "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", 910 | "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", 911 | "dev": true 912 | }, 913 | "jsonpointer": { 914 | "version": "4.0.1", 915 | "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", 916 | "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", 917 | "dev": true 918 | }, 919 | "lcid": { 920 | "version": "1.0.0", 921 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", 922 | "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", 923 | "requires": { 924 | "invert-kv": "^1.0.0" 925 | } 926 | }, 927 | "levn": { 928 | "version": "0.3.0", 929 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 930 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 931 | "dev": true, 932 | "requires": { 933 | "prelude-ls": "~1.1.2", 934 | "type-check": "~0.3.2" 935 | } 936 | }, 937 | "load-json-file": { 938 | "version": "1.1.0", 939 | "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", 940 | "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", 941 | "requires": { 942 | "graceful-fs": "^4.1.2", 943 | "parse-json": "^2.2.0", 944 | "pify": "^2.0.0", 945 | "pinkie-promise": "^2.0.0", 946 | "strip-bom": "^2.0.0" 947 | } 948 | }, 949 | "lodash": { 950 | "version": "4.17.11", 951 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 952 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" 953 | }, 954 | "lodash.assign": { 955 | "version": "4.2.0", 956 | "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", 957 | "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" 958 | }, 959 | "minimatch": { 960 | "version": "3.0.4", 961 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 962 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 963 | "dev": true, 964 | "requires": { 965 | "brace-expansion": "^1.1.7" 966 | } 967 | }, 968 | "minimist": { 969 | "version": "0.0.8", 970 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 971 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 972 | "dev": true 973 | }, 974 | "mkdirp": { 975 | "version": "0.5.1", 976 | "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 977 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 978 | "dev": true, 979 | "requires": { 980 | "minimist": "0.0.8" 981 | } 982 | }, 983 | "ms": { 984 | "version": "2.0.0", 985 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 986 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 987 | "dev": true 988 | }, 989 | "mute-stream": { 990 | "version": "0.0.5", 991 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", 992 | "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", 993 | "dev": true 994 | }, 995 | "natural-compare": { 996 | "version": "1.4.0", 997 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 998 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 999 | "dev": true 1000 | }, 1001 | "next-tick": { 1002 | "version": "1.0.0", 1003 | "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", 1004 | "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", 1005 | "dev": true 1006 | }, 1007 | "normalize-package-data": { 1008 | "version": "2.4.0", 1009 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", 1010 | "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", 1011 | "requires": { 1012 | "hosted-git-info": "^2.1.4", 1013 | "is-builtin-module": "^1.0.0", 1014 | "semver": "2 || 3 || 4 || 5", 1015 | "validate-npm-package-license": "^3.0.1" 1016 | } 1017 | }, 1018 | "number-is-nan": { 1019 | "version": "1.0.1", 1020 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 1021 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 1022 | }, 1023 | "object-assign": { 1024 | "version": "4.1.1", 1025 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1026 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", 1027 | "dev": true 1028 | }, 1029 | "object-inspect": { 1030 | "version": "1.6.0", 1031 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", 1032 | "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", 1033 | "dev": true 1034 | }, 1035 | "object-keys": { 1036 | "version": "1.0.12", 1037 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", 1038 | "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", 1039 | "dev": true 1040 | }, 1041 | "once": { 1042 | "version": "1.4.0", 1043 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1044 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1045 | "dev": true, 1046 | "requires": { 1047 | "wrappy": "1" 1048 | } 1049 | }, 1050 | "onetime": { 1051 | "version": "1.1.0", 1052 | "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", 1053 | "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", 1054 | "dev": true 1055 | }, 1056 | "optionator": { 1057 | "version": "0.8.2", 1058 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 1059 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 1060 | "dev": true, 1061 | "requires": { 1062 | "deep-is": "~0.1.3", 1063 | "fast-levenshtein": "~2.0.4", 1064 | "levn": "~0.3.0", 1065 | "prelude-ls": "~1.1.2", 1066 | "type-check": "~0.3.2", 1067 | "wordwrap": "~1.0.0" 1068 | } 1069 | }, 1070 | "os-homedir": { 1071 | "version": "1.0.2", 1072 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 1073 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", 1074 | "dev": true 1075 | }, 1076 | "os-locale": { 1077 | "version": "1.4.0", 1078 | "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", 1079 | "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", 1080 | "requires": { 1081 | "lcid": "^1.0.0" 1082 | } 1083 | }, 1084 | "parse-json": { 1085 | "version": "2.2.0", 1086 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 1087 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 1088 | "requires": { 1089 | "error-ex": "^1.2.0" 1090 | } 1091 | }, 1092 | "path-exists": { 1093 | "version": "2.1.0", 1094 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", 1095 | "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", 1096 | "requires": { 1097 | "pinkie-promise": "^2.0.0" 1098 | } 1099 | }, 1100 | "path-is-absolute": { 1101 | "version": "1.0.1", 1102 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1103 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1104 | "dev": true 1105 | }, 1106 | "path-is-inside": { 1107 | "version": "1.0.2", 1108 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 1109 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", 1110 | "dev": true 1111 | }, 1112 | "path-parse": { 1113 | "version": "1.0.6", 1114 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 1115 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 1116 | "dev": true 1117 | }, 1118 | "path-type": { 1119 | "version": "1.1.0", 1120 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", 1121 | "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", 1122 | "requires": { 1123 | "graceful-fs": "^4.1.2", 1124 | "pify": "^2.0.0", 1125 | "pinkie-promise": "^2.0.0" 1126 | } 1127 | }, 1128 | "pify": { 1129 | "version": "2.3.0", 1130 | "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1131 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" 1132 | }, 1133 | "pinkie": { 1134 | "version": "2.0.4", 1135 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 1136 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" 1137 | }, 1138 | "pinkie-promise": { 1139 | "version": "2.0.1", 1140 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 1141 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 1142 | "requires": { 1143 | "pinkie": "^2.0.0" 1144 | } 1145 | }, 1146 | "pluralize": { 1147 | "version": "1.2.1", 1148 | "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", 1149 | "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", 1150 | "dev": true 1151 | }, 1152 | "prelude-ls": { 1153 | "version": "1.1.2", 1154 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 1155 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 1156 | "dev": true 1157 | }, 1158 | "process-nextick-args": { 1159 | "version": "2.0.0", 1160 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 1161 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", 1162 | "dev": true 1163 | }, 1164 | "progress": { 1165 | "version": "1.1.8", 1166 | "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", 1167 | "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", 1168 | "dev": true 1169 | }, 1170 | "read-pkg": { 1171 | "version": "1.1.0", 1172 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", 1173 | "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", 1174 | "requires": { 1175 | "load-json-file": "^1.0.0", 1176 | "normalize-package-data": "^2.3.2", 1177 | "path-type": "^1.0.0" 1178 | } 1179 | }, 1180 | "read-pkg-up": { 1181 | "version": "1.0.1", 1182 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", 1183 | "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", 1184 | "requires": { 1185 | "find-up": "^1.0.0", 1186 | "read-pkg": "^1.0.0" 1187 | } 1188 | }, 1189 | "readable-stream": { 1190 | "version": "2.3.6", 1191 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 1192 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 1193 | "dev": true, 1194 | "requires": { 1195 | "core-util-is": "~1.0.0", 1196 | "inherits": "~2.0.3", 1197 | "isarray": "~1.0.0", 1198 | "process-nextick-args": "~2.0.0", 1199 | "safe-buffer": "~5.1.1", 1200 | "string_decoder": "~1.1.1", 1201 | "util-deprecate": "~1.0.1" 1202 | } 1203 | }, 1204 | "readline2": { 1205 | "version": "1.0.1", 1206 | "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", 1207 | "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", 1208 | "dev": true, 1209 | "requires": { 1210 | "code-point-at": "^1.0.0", 1211 | "is-fullwidth-code-point": "^1.0.0", 1212 | "mute-stream": "0.0.5" 1213 | } 1214 | }, 1215 | "rechoir": { 1216 | "version": "0.6.2", 1217 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", 1218 | "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", 1219 | "dev": true, 1220 | "requires": { 1221 | "resolve": "^1.1.6" 1222 | } 1223 | }, 1224 | "require-directory": { 1225 | "version": "2.1.1", 1226 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1227 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" 1228 | }, 1229 | "require-main-filename": { 1230 | "version": "1.0.1", 1231 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", 1232 | "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" 1233 | }, 1234 | "require-uncached": { 1235 | "version": "1.0.3", 1236 | "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", 1237 | "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", 1238 | "dev": true, 1239 | "requires": { 1240 | "caller-path": "^0.1.0", 1241 | "resolve-from": "^1.0.0" 1242 | } 1243 | }, 1244 | "resolve": { 1245 | "version": "1.8.1", 1246 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", 1247 | "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", 1248 | "dev": true, 1249 | "requires": { 1250 | "path-parse": "^1.0.5" 1251 | } 1252 | }, 1253 | "resolve-from": { 1254 | "version": "1.0.1", 1255 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", 1256 | "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", 1257 | "dev": true 1258 | }, 1259 | "restore-cursor": { 1260 | "version": "1.0.1", 1261 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", 1262 | "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", 1263 | "dev": true, 1264 | "requires": { 1265 | "exit-hook": "^1.0.0", 1266 | "onetime": "^1.0.0" 1267 | } 1268 | }, 1269 | "resumer": { 1270 | "version": "0.0.0", 1271 | "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", 1272 | "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", 1273 | "dev": true, 1274 | "requires": { 1275 | "through": "~2.3.4" 1276 | } 1277 | }, 1278 | "rimraf": { 1279 | "version": "2.6.2", 1280 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 1281 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 1282 | "dev": true, 1283 | "requires": { 1284 | "glob": "^7.0.5" 1285 | } 1286 | }, 1287 | "run-async": { 1288 | "version": "0.1.0", 1289 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", 1290 | "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", 1291 | "dev": true, 1292 | "requires": { 1293 | "once": "^1.3.0" 1294 | } 1295 | }, 1296 | "rx-lite": { 1297 | "version": "3.1.2", 1298 | "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", 1299 | "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", 1300 | "dev": true 1301 | }, 1302 | "safe-buffer": { 1303 | "version": "5.1.2", 1304 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1305 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 1306 | "dev": true 1307 | }, 1308 | "semver": { 1309 | "version": "5.6.0", 1310 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", 1311 | "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" 1312 | }, 1313 | "set-blocking": { 1314 | "version": "2.0.0", 1315 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1316 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 1317 | }, 1318 | "shelljs": { 1319 | "version": "0.7.8", 1320 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", 1321 | "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", 1322 | "dev": true, 1323 | "requires": { 1324 | "glob": "^7.0.0", 1325 | "interpret": "^1.0.0", 1326 | "rechoir": "^0.6.2" 1327 | } 1328 | }, 1329 | "slice-ansi": { 1330 | "version": "0.0.4", 1331 | "resolved": "http://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", 1332 | "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", 1333 | "dev": true 1334 | }, 1335 | "spdx-correct": { 1336 | "version": "3.0.2", 1337 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", 1338 | "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==", 1339 | "requires": { 1340 | "spdx-expression-parse": "^3.0.0", 1341 | "spdx-license-ids": "^3.0.0" 1342 | } 1343 | }, 1344 | "spdx-exceptions": { 1345 | "version": "2.2.0", 1346 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 1347 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" 1348 | }, 1349 | "spdx-expression-parse": { 1350 | "version": "3.0.0", 1351 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 1352 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 1353 | "requires": { 1354 | "spdx-exceptions": "^2.1.0", 1355 | "spdx-license-ids": "^3.0.0" 1356 | } 1357 | }, 1358 | "spdx-license-ids": { 1359 | "version": "3.0.1", 1360 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz", 1361 | "integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==" 1362 | }, 1363 | "sprintf-js": { 1364 | "version": "1.0.3", 1365 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1366 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1367 | "dev": true 1368 | }, 1369 | "string-width": { 1370 | "version": "1.0.2", 1371 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1372 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1373 | "requires": { 1374 | "code-point-at": "^1.0.0", 1375 | "is-fullwidth-code-point": "^1.0.0", 1376 | "strip-ansi": "^3.0.0" 1377 | } 1378 | }, 1379 | "string.prototype.trim": { 1380 | "version": "1.1.2", 1381 | "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", 1382 | "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", 1383 | "dev": true, 1384 | "requires": { 1385 | "define-properties": "^1.1.2", 1386 | "es-abstract": "^1.5.0", 1387 | "function-bind": "^1.0.2" 1388 | } 1389 | }, 1390 | "string_decoder": { 1391 | "version": "1.1.1", 1392 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1393 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1394 | "dev": true, 1395 | "requires": { 1396 | "safe-buffer": "~5.1.0" 1397 | } 1398 | }, 1399 | "strip-ansi": { 1400 | "version": "3.0.1", 1401 | "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1402 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1403 | "requires": { 1404 | "ansi-regex": "^2.0.0" 1405 | } 1406 | }, 1407 | "strip-bom": { 1408 | "version": "2.0.0", 1409 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", 1410 | "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", 1411 | "requires": { 1412 | "is-utf8": "^0.2.0" 1413 | } 1414 | }, 1415 | "strip-json-comments": { 1416 | "version": "2.0.1", 1417 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1418 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 1419 | "dev": true 1420 | }, 1421 | "supports-color": { 1422 | "version": "2.0.0", 1423 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 1424 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", 1425 | "dev": true 1426 | }, 1427 | "table": { 1428 | "version": "3.8.3", 1429 | "resolved": "http://registry.npmjs.org/table/-/table-3.8.3.tgz", 1430 | "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", 1431 | "dev": true, 1432 | "requires": { 1433 | "ajv": "^4.7.0", 1434 | "ajv-keywords": "^1.0.0", 1435 | "chalk": "^1.1.1", 1436 | "lodash": "^4.0.0", 1437 | "slice-ansi": "0.0.4", 1438 | "string-width": "^2.0.0" 1439 | }, 1440 | "dependencies": { 1441 | "ansi-regex": { 1442 | "version": "3.0.0", 1443 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 1444 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 1445 | "dev": true 1446 | }, 1447 | "is-fullwidth-code-point": { 1448 | "version": "2.0.0", 1449 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1450 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 1451 | "dev": true 1452 | }, 1453 | "string-width": { 1454 | "version": "2.1.1", 1455 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1456 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1457 | "dev": true, 1458 | "requires": { 1459 | "is-fullwidth-code-point": "^2.0.0", 1460 | "strip-ansi": "^4.0.0" 1461 | } 1462 | }, 1463 | "strip-ansi": { 1464 | "version": "4.0.0", 1465 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1466 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1467 | "dev": true, 1468 | "requires": { 1469 | "ansi-regex": "^3.0.0" 1470 | } 1471 | } 1472 | } 1473 | }, 1474 | "tape": { 1475 | "version": "4.9.1", 1476 | "resolved": "https://registry.npmjs.org/tape/-/tape-4.9.1.tgz", 1477 | "integrity": "sha512-6fKIXknLpoe/Jp4rzHKFPpJUHDHDqn8jus99IfPnHIjyz78HYlefTGD3b5EkbQzuLfaEvmfPK3IolLgq2xT3kw==", 1478 | "dev": true, 1479 | "requires": { 1480 | "deep-equal": "~1.0.1", 1481 | "defined": "~1.0.0", 1482 | "for-each": "~0.3.3", 1483 | "function-bind": "~1.1.1", 1484 | "glob": "~7.1.2", 1485 | "has": "~1.0.3", 1486 | "inherits": "~2.0.3", 1487 | "minimist": "~1.2.0", 1488 | "object-inspect": "~1.6.0", 1489 | "resolve": "~1.7.1", 1490 | "resumer": "~0.0.0", 1491 | "string.prototype.trim": "~1.1.2", 1492 | "through": "~2.3.8" 1493 | }, 1494 | "dependencies": { 1495 | "minimist": { 1496 | "version": "1.2.0", 1497 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 1498 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", 1499 | "dev": true 1500 | }, 1501 | "resolve": { 1502 | "version": "1.7.1", 1503 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", 1504 | "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", 1505 | "dev": true, 1506 | "requires": { 1507 | "path-parse": "^1.0.5" 1508 | } 1509 | } 1510 | } 1511 | }, 1512 | "text-table": { 1513 | "version": "0.2.0", 1514 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1515 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1516 | "dev": true 1517 | }, 1518 | "through": { 1519 | "version": "2.3.8", 1520 | "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", 1521 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 1522 | "dev": true 1523 | }, 1524 | "type-check": { 1525 | "version": "0.3.2", 1526 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1527 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1528 | "dev": true, 1529 | "requires": { 1530 | "prelude-ls": "~1.1.2" 1531 | } 1532 | }, 1533 | "typedarray": { 1534 | "version": "0.0.6", 1535 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1536 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", 1537 | "dev": true 1538 | }, 1539 | "user-home": { 1540 | "version": "2.0.0", 1541 | "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", 1542 | "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", 1543 | "dev": true, 1544 | "requires": { 1545 | "os-homedir": "^1.0.0" 1546 | } 1547 | }, 1548 | "util-deprecate": { 1549 | "version": "1.0.2", 1550 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1551 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 1552 | "dev": true 1553 | }, 1554 | "validate-npm-package-license": { 1555 | "version": "3.0.4", 1556 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 1557 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 1558 | "requires": { 1559 | "spdx-correct": "^3.0.0", 1560 | "spdx-expression-parse": "^3.0.0" 1561 | } 1562 | }, 1563 | "which-module": { 1564 | "version": "1.0.0", 1565 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", 1566 | "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" 1567 | }, 1568 | "window-size": { 1569 | "version": "0.2.0", 1570 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", 1571 | "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" 1572 | }, 1573 | "wordwrap": { 1574 | "version": "1.0.0", 1575 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 1576 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 1577 | "dev": true 1578 | }, 1579 | "wrap-ansi": { 1580 | "version": "2.1.0", 1581 | "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", 1582 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", 1583 | "requires": { 1584 | "string-width": "^1.0.1", 1585 | "strip-ansi": "^3.0.1" 1586 | } 1587 | }, 1588 | "wrappy": { 1589 | "version": "1.0.2", 1590 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1591 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1592 | "dev": true 1593 | }, 1594 | "write": { 1595 | "version": "0.2.1", 1596 | "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", 1597 | "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", 1598 | "dev": true, 1599 | "requires": { 1600 | "mkdirp": "^0.5.1" 1601 | } 1602 | }, 1603 | "xtend": { 1604 | "version": "4.0.1", 1605 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 1606 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", 1607 | "dev": true 1608 | }, 1609 | "y18n": { 1610 | "version": "3.2.1", 1611 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", 1612 | "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" 1613 | }, 1614 | "yargs": { 1615 | "version": "4.8.1", 1616 | "resolved": "http://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", 1617 | "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", 1618 | "requires": { 1619 | "cliui": "^3.2.0", 1620 | "decamelize": "^1.1.1", 1621 | "get-caller-file": "^1.0.1", 1622 | "lodash.assign": "^4.0.3", 1623 | "os-locale": "^1.4.0", 1624 | "read-pkg-up": "^1.0.1", 1625 | "require-directory": "^2.1.1", 1626 | "require-main-filename": "^1.0.1", 1627 | "set-blocking": "^2.0.0", 1628 | "string-width": "^1.0.1", 1629 | "which-module": "^1.0.0", 1630 | "window-size": "^0.2.0", 1631 | "y18n": "^3.2.1", 1632 | "yargs-parser": "^2.4.1" 1633 | } 1634 | }, 1635 | "yargs-parser": { 1636 | "version": "2.4.1", 1637 | "resolved": "http://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", 1638 | "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", 1639 | "requires": { 1640 | "camelcase": "^3.0.0", 1641 | "lodash.assign": "^4.0.6" 1642 | } 1643 | } 1644 | } 1645 | } 1646 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "git-secure-tag", 3 | "version": "2.3.1", 4 | "description": "Git tags with higher level of security", 5 | "main": "lib/git-secure-tag.js", 6 | "bin": { 7 | "git-secure-tag": "bin/git-secure-tag" 8 | }, 9 | "scripts": { 10 | "lint": "make lint", 11 | "format": "make format", 12 | "test": "tape test/*-test.js && npm run lint" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+ssh://git@github.com/indutny/git-secure-tag.git" 17 | }, 18 | "keywords": [ 19 | "git", 20 | "secure", 21 | "tag", 22 | "sha256", 23 | "gpg", 24 | "signature", 25 | "verify", 26 | "sign" 27 | ], 28 | "author": "Fedor Indutny ", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/indutny/git-secure-tag/issues" 32 | }, 33 | "homepage": "https://github.com/indutny/git-secure-tag#readme", 34 | "dependencies": { 35 | "async": "^2.0.0-rc.6", 36 | "yargs": "^4.7.1" 37 | }, 38 | "devDependencies": { 39 | "eslint": "^3.0.1", 40 | "rimraf": "^2.5.3", 41 | "tape": "^4.6.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/api-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const async = require('async'); 4 | const tape = require('tape'); 5 | 6 | const gst = require('../'); 7 | 8 | const fixtures = require('./fixtures'); 9 | 10 | tape('js api', (t) => { 11 | fixtures.init(); 12 | 13 | // Verify tags 14 | const api = new gst.API(fixtures.repo); 15 | 16 | const tags = [ 17 | { name: 'tag-latest', ref: 'HEAD' }, 18 | { name: 'tag-middle', ref: 'HEAD^' }, 19 | { name: 'tag-first', ref: 'HEAD^^', legacy: true } 20 | ]; 21 | 22 | async.waterfall([ 23 | (callback) => { 24 | async.forEachSeries(tags, (tag, callback) => { 25 | api.sign(tag.name, tag.ref, { legacy: tag.legacy, insecure: true }, 26 | callback); 27 | }, callback); 28 | }, 29 | (callback) => { 30 | async.mapSeries(tags, (tag, callback) => { 31 | api.verify(tag.name, { insecure: true }, callback); 32 | }, callback); 33 | }, 34 | (results, callback) => { 35 | t.deepEqual(results, [ true, true, true ], 'sign results'); 36 | 37 | const invalidTags = [ 'invalid-1', 'invalid-2', 'invalid-3' ]; 38 | async.mapSeries(invalidTags, (tag, callback) => { 39 | api.verify(tag, { insecure: true }, (err, result) => { 40 | callback(null, { err: err, result: result }); 41 | }); 42 | }, callback); 43 | }, 44 | (results, callback) => { 45 | t.ok(/EVTag.*mismatch/.test(results[0].err.message), 46 | 'invalid #1'); 47 | t.ok(/Secure-Tag.*mismatch/.test(results[1].err.message), 'invalid #2'); 48 | t.ok(/No.*found/.test(results[2].err.message), 'invalid #3'); 49 | t.ok(!results[0].result, 'invalid #1 result'); 50 | t.ok(!results[1].result, 'invalid #2 result'); 51 | t.ok(!results[2].result, 'invalid #3 result'); 52 | 53 | callback(null); 54 | } 55 | ], (err) => { 56 | fixtures.destroy(); 57 | t.end(err); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /test/ev-tag-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const tape = require('tape'); 4 | 5 | const fixtures = require('./fixtures'); 6 | 7 | const cli = fixtures.cli; 8 | const cmd = fixtures.cmd; 9 | 10 | tape('evtag interop', (t) => { 11 | const repos = [ 12 | 'git://github.com/cgwalters/git-evtag.git', 13 | 'git://github.com/ostreedev/ostree.git', 14 | 'git://github.com/GNOME/gnome-terminal.git', 15 | 'https://gitlab.com/fidencio/libosinfo.git' 16 | ]; 17 | 18 | repos.forEach((url) => { 19 | fixtures.clone(url); 20 | 21 | const tags = fixtures.tags(); 22 | 23 | // Verify tags 24 | const node = process.execPath; 25 | tags.forEach((tag) => { 26 | t.doesNotThrow(() => cmd(node, [ cli, '--insecure', '-v', tag ]), 27 | `tag ${tag} of ${url} should validate`); 28 | }); 29 | 30 | fixtures.destroy(); 31 | }); 32 | t.end(); 33 | }); 34 | -------------------------------------------------------------------------------- /test/fixtures/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indutny/git-secure-tag/efde119a963a657ff320ec189818aba16bdd28d6/test/fixtures/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const crypto = require('crypto'); 5 | const path = require('path'); 6 | const spawnSync = require('child_process').spawnSync; 7 | 8 | const rimraf = require('rimraf'); 9 | 10 | const gst = require('../../'); 11 | const GIT = gst.GIT; 12 | 13 | const cli = path.join(__dirname, '..', '..', 'bin', 'git-secure-tag'); 14 | exports.cli = cli; 15 | 16 | const repo = path.join(__dirname, 'repo'); 17 | exports.repo = repo; 18 | 19 | function cmd(name, args, noCwd) { 20 | const p = spawnSync(name, args, { 21 | stdio: [ null, 'pipe', 'pipe' ], 22 | cwd: noCwd ? undefined : repo 23 | }); 24 | if (p.status !== 0) { 25 | const msg = `${name} ${args.join(' ')} failed`; 26 | throw new Error(msg + '\n' + p.stdout + '\n' + p.stderr); 27 | } 28 | return p.stdout.toString(); 29 | } 30 | exports.cmd = cmd; 31 | 32 | function write(file, content) { 33 | fs.writeFileSync(path.join(repo, file), content); 34 | } 35 | exports.write = write; 36 | 37 | exports.tags = function tags() { 38 | const lines = cmd(GIT, [ 'tag', '-n9999' ]).split('\n'); 39 | 40 | const result = []; 41 | 42 | let tag; 43 | for (let i = 0; i < lines.length; i++) { 44 | const line = lines[i]; 45 | if (/^\S/.test(line)) { 46 | tag = line.split(/\s/)[0]; 47 | continue; 48 | } 49 | 50 | if (/Git-(EVTag-v0-SHA512|Secure-Tag-V0)/.test(line)) 51 | result.push(tag); 52 | } 53 | 54 | return result; 55 | }; 56 | 57 | // Initialize repo 58 | exports.init = function init() { 59 | rimraf.sync(repo); 60 | fs.mkdirSync(repo); 61 | 62 | cmd(GIT, [ 'init' ]); 63 | cmd(GIT, [ 'config', 'user.email', 'john@doe.org' ]); 64 | cmd(GIT, [ 'config', 'user.name', 'John Doe' ]); 65 | 66 | write('file.txt', 'hello'); 67 | cmd(GIT, [ 'add', 'file.txt' ]); 68 | cmd(GIT, [ 'commit', '-m', 'first' ]); 69 | 70 | write('file.txt', 'world'); 71 | cmd(GIT, [ 'add', 'file.txt' ]); 72 | cmd(GIT, [ 'commit', '-m', 'second' ]); 73 | 74 | write('file.txt', '!'); 75 | cmd(GIT, [ 'add', 'file.txt' ]); 76 | cmd(GIT, [ 'commit', '-m', 'third' ]); 77 | 78 | // Create invalid tags 79 | const hash = crypto.createHash('sha512').update('invalid').digest('hex'); 80 | cmd(GIT, [ 'tag', '-m', `Git-EVTag-v0-SHA512: ${hash}`, 'invalid-1' ]); 81 | cmd(GIT, [ 'tag', '-m', `Git-Secure-Tag-v0: ${hash}`, 'invalid-2' ]); 82 | cmd(GIT, [ 'tag', '-m', `Random-Hash: ${hash}`, 'invalid-3' ]); 83 | 84 | }; 85 | 86 | // Clone repo 87 | exports.clone = function clone(url) { 88 | rimraf.sync(repo); 89 | cmd(GIT, [ 'clone', '--recursive', url, repo ], true); 90 | }; 91 | 92 | exports.destroy = function destroy() { 93 | rimraf.sync(repo); 94 | }; 95 | -------------------------------------------------------------------------------- /test/scenario-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const tape = require('tape'); 4 | 5 | const fixtures = require('./fixtures'); 6 | 7 | const cli = fixtures.cli; 8 | const cmd = fixtures.cmd; 9 | 10 | tape('git secure tag', (t) => { 11 | fixtures.init(); 12 | 13 | // Create tags 14 | const node = process.execPath; 15 | cmd(node, [ cli, '--insecure', 'tag-latest' ]); 16 | cmd(node, [ cli, '--insecure', 'tag-middle', 'HEAD^' ]); 17 | cmd(node, [ cli, '--insecure', '--legacy', 'tag-legacy', 'HEAD^^' ]); 18 | 19 | // Verify tags 20 | t.doesNotThrow(() => cmd(node, [ cli, '--insecure', '-v', 'tag-latest' ]), 21 | 'valid HEAD evtag'); 22 | t.doesNotThrow(() => cmd(node, [ cli, '--insecure', '-v', 'tag-middle' ]), 23 | 'valid non-HEAD evtag'); 24 | t.doesNotThrow(() => cmd(node, [ cli, '--insecure', '-v', 'tag-legacy' ]), 25 | 'valid legacy hash'); 26 | 27 | // Fail to verify invalid tags 28 | t.throws(() => cmd(node, [ cli, '--insecure', '-v', 'invalid-1' ]), 29 | /EVTag.*mismatch/, 30 | 'invalid evtag hash'); 31 | t.throws(() => cmd(node, [ cli, '--insecure', '-v', 'invalid-2' ]), 32 | /Secure-Tag.*mismatch/, 33 | 'invalid legacy hash'); 34 | t.throws(() => cmd(node, [ cli, '--insecure', '-v', 'invalid-3' ]), 35 | /No.*found/, 36 | 'no hash at all'); 37 | 38 | fixtures.destroy(); 39 | t.end(); 40 | }); 41 | --------------------------------------------------------------------------------