CVE-2019-10807
Description
CVE-2019-10807 is a command injection vulnerability in the blamer npm package (<1.0.1) that allows arbitrary command execution via crafted arguments.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
CVE-2019-10807 is a command injection vulnerability in the blamer npm package (<1.0.1) that allows arbitrary command execution via crafted arguments.
CVE-2019-10807 describes a command injection vulnerability in the blamer npm package, which is a tool for retrieving author information from version control systems (Git and Subversion). The vulnerability exists in versions prior to 1.0.1. According to the official description and the Snyk advisory [3], the package fails to properly sanitize user-supplied arguments, allowing attackers to inject arbitrary commands as part of these arguments.
Exploitation
The attack does not require authentication and can be triggered by providing a malicious string containing shell metacharacters (e.g., & touch vulnerable &) as the VCS type or path argument when creating a Blamer instance or calling its methods [3]. A proof-of-concept demonstrates this by passing '& touch vulnerable &' as the second parameter to the Blamer constructor, which causes the injected command to be executed on the host system [3]. The vulnerability can be exploited over the network if an application accepts user input for these parameters and passes it directly to blamer.
Impact
Successful exploitation allows an attacker to execute arbitrary commands on the system with the privileges of the process running blamer. This could lead to full compromise of the application or server, including data exfiltration, installation of malware, or further lateral movement within the network. The CVSS base score is not explicitly provided in the references, but the high severity rating from Snyk [3] underscores the critical nature of the flaw.
Mitigation
The vulnerability was fixed in version 1.0.1. Users should upgrade blamer to version 1.0.1 or later to mitigate the risk [1][3]. The fix involved rewriting the project with TypeScript and implementing proper input sanitization [1]. No workarounds are mentioned, and the package maintainer has addressed the issue via a committed patch.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
blamernpm | < 1.0.1 | 1.0.1 |
Affected products
2- Blamer/Blamerdescription
Patches
15fada8c9b698fix(root): Rewrite the project with typescript and fix issue with security of script execution
30 files changed · +5037 −466
.coveralls.yml+0 −1 removed@@ -1 +0,0 @@ -repo_token: gcYF7m4NJRGb8i1U6SvzFqn20MTROxISb \ No newline at end of file
.github/CONTRIBUTING.md+3 −0 added@@ -0,0 +1,3 @@ +# Example Contributing Guidelines + +This is an example of GitHub's contributing guidelines file. Check out GitHub's [CONTRIBUTING.md help center article](https://help.github.com/articles/setting-guidelines-for-repository-contributors/) for more information.
.github/ISSUE_TEMPLATE.md+11 −0 added@@ -0,0 +1,11 @@ +* **I'm submitting a ...** +[ ] bug report +[ ] feature request +[ ] question about the decisions made in the repository +[ ] question about how to use this project + +* **Summary** + + + +* **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)
.github/PULL_REQUEST_TEMPLATE.md+13 −0 added@@ -0,0 +1,13 @@ +* **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) + + + +* **What is the current behavior?** (You can also link to an open issue here) + + + +* **What is the new behavior (if this is a feature change)?** + + + +* **Other information**:
.gitignore+8 −30 modified@@ -1,33 +1,11 @@ - -######################## -# node.js / npm -######################## -lib-cov -*.seed -*.log -*.csv -*.dat -*.out -*.pid -*.gz - -pids -logs -results -.tmp -coverage node_modules +build +test +src/**.js +.idea/* -npm-debug.log - +coverage +.nyc_output +*.log -######################## -# misc / editors -######################## -*~ -*# -.DS_STORE -.netbeans -nbproject -.idea -atlassian-ide-plugin.xml \ No newline at end of file +package-lock.json \ No newline at end of file
LICENSE+2 −2 modified@@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Andrey Kucherenko +Copyright (c) 2014-2020 Andrey Kucherenko Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE.
.npmignore+12 −7 modified@@ -1,9 +1,14 @@ -.tmp +src +test +tsconfig.json +tsconfig.module.json +tslint.json .travis.yml -.coveralls.yml -.git -.gitignore -npm-debug.log -.idea +.github +.prettierignore +.vscode +build/docs +**/*.spec.* coverage -atlassian-ide-plugin.xml \ No newline at end of file +.nyc_output +*.log
package.json+97 −30 modified@@ -1,38 +1,105 @@ { "name": "blamer", - "version": "0.1.13", + "version": "1.0.0", "description": "blamer is a tool for getting information about author of code from version control system", - "main": "src/index.js", + "main": "build/main/index.js", + "typings": "build/main/index.d.ts", + "module": "build/module/index.js", + "repository": "https://github.com/kucherenko/blamer", + "license": "MIT", + "keywords": [], "scripts": { - "test": "mocha $(find test -name '*.js')", - "ci": "COVERAGE=true ./node_modules/mocha/bin/mocha -R mocha-phantom-coverage-reporter $(find test -name '*.js')", - "developing": "./node_modules/mocha/bin/mocha --watch $(find test -name '*.js')" - }, - "repository": { - "type": "git", - "url": "https://github.com/kucherenko/blamer.git" - }, - "keywords": [ - "blame", - "vcs", - "git", - "svn" - ], + "describe": "npm-scripts-info", + "build": "run-s clean && run-p build:*", + "build:main": "tsc -p tsconfig.json", + "build:module": "tsc -p tsconfig.module.json", + "fix": "run-s fix:*", + "fix:prettier": "prettier \"src/**/*.ts\" --write", + "fix:tslint": "tslint --fix --project .", + "test": "run-s build test:*", + "test:lint": "tslint --project . && prettier \"src/**/*.ts\" --list-different", + "test:unit": "nyc ava --verbose", + "watch": "run-s clean build:main && run-p \"build:main -- -w\" \"test:unit -- --watch\"", + "cov": "run-s build test:unit cov:html && open-cli coverage/index.html", + "cov:html": "nyc report --reporter=html", + "cov:send": "nyc report --reporter=lcov && codecov", + "cov:check": "nyc report && nyc check-coverage --lines 100 --functions 100 --branches 100", + "doc": "run-s doc:html && open-cli build/docs/index.html", + "doc:html": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --out build/docs", + "doc:json": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --json build/docs/typedoc.json", + "doc:publish": "gh-pages -m \"[ci skip] Updates\" -d build/docs", + "version": "standard-version", + "reset": "git clean -dfx && git reset --hard && npm i", + "clean": "trash build test", + "prepare-release": "run-s reset test cov:check doc:html version doc:publish", + "preinstall": "node -e \"if(process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('blamer must be installed with Yarn: https://yarnpkg.com/')\"" + }, + "scripts-info": { + "info": "Display information about the package scripts", + "build": "Clean and rebuild the project", + "fix": "Try to automatically fix any linting problems", + "test": "Lint and unit test the project", + "watch": "Watch and rebuild the project on save, then rerun relevant tests", + "cov": "Rebuild, run tests, then create and open the coverage report", + "doc": "Generate HTML API documentation and open it in a browser", + "doc:json": "Generate API documentation in typedoc JSON format", + "version": "Bump package.json version, update CHANGELOG.md, tag release", + "reset": "Delete all untracked files and reset the repo to the last commit", + "prepare-release": "One-step: clean, build, test, publish docs, and prep a release" + }, + "engines": { + "node": ">=8.9" + }, "dependencies": { - "bluebird": "~2.3.x", - "xml2js": "~0.4.x" + "execa": "^4.0.0", + "which": "^2.0.2", + "xml2js": "^0.4.23" }, "devDependencies": { - "mocha": "~2.0.x", - "chai": "~1.9.x", - "sinon": "~1.10.x", - "sinon-chai": "~2.6.x", - "proxyquire": "~1.0.x", - "coveralls": "~2.11.x", - "jscoverage": "~0.5.x", - "mocha-phantom-coverage-reporter": "~0.1.x" - }, - "readmeFilename": "README.md", - "author": "Andrey Kucherenko", - "license": "MIT" + "@bitjson/npm-scripts-info": "^1.0.0", + "@bitjson/typedoc": "^0.15.0-0", + "@istanbuljs/nyc-config-typescript": "^0.1.3", + "@types/sinon": "^7.5.2", + "@types/which": "^1.3.2", + "@types/xml2js": "^0.4.5", + "ava": "^3.5.0", + "codecov": "^3.5.0", + "cz-conventional-changelog": "^2.1.0", + "gh-pages": "^2.0.1", + "npm-run-all": "^4.1.5", + "nyc": "^14.1.1", + "open-cli": "^5.0.0", + "prettier": "^1.18.2", + "proxyquire": "^2.1.3", + "sinon": "^9.0.0", + "standard-version": "^6.0.1", + "trash-cli": "^3.0.0", + "tslint": "^5.18.0", + "tslint-config-prettier": "^1.18.0", + "tslint-immutable": "^6.0.1", + "typescript": "^3.5.3" + }, + "ava": { + "failFast": true, + "files": [ + "build/main/**/*.spec.js" + ], + "ignoredByWatcher": [ + "build/main/**/*.js" + ] + }, + "config": { + "commitizen": { + "path": "cz-conventional-changelog" + } + }, + "prettier": { + "singleQuote": true + }, + "nyc": { + "extends": "@istanbuljs/nyc-config-typescript", + "exclude": [ + "**/*.spec.js" + ] + } }
.prettierignore+2 −0 added@@ -0,0 +1,2 @@ +# package.json is formatted by package managers, so we ignore it here +package.json \ No newline at end of file
src/blame-result.interface.ts+12 −0 added@@ -0,0 +1,12 @@ +export interface BlamedLine { + rev: string; + author: string; + date: string; + line: string; +} + +export interface BlameResult { + [path: string]: { + [line: string]: BlamedLine; + }; +}
src/Blamer.js+0 −22 removed@@ -1,22 +0,0 @@ -var blamerVCS = { - 'git': require("./vcs/git"), - 'svn': require("./vcs/svn") -}; -var Blamer = function Blamer(type, args) { - this.args = args; - this.type = type || 'git'; -}; - -Blamer.prototype.blameByFile = function blameByFile(file, args) { - if (!blamerVCS.hasOwnProperty(this.type)) { - throw new Error('VCS "' + this.type + '" don\'t supported'); - } - args = typeof args === 'string' ? args : this.args; - return blamerVCS[this.type](file, args); -}; - -Blamer.prototype.getType = function () { - return this.type; -}; - -module.exports = Blamer;
src/blamer.spec.ts+32 −0 added@@ -0,0 +1,32 @@ +const proxyquire = require('proxyquire'); +import test, { ExecutionContext } from 'ava'; +import sinon from 'sinon'; +import { BlameResult } from './blame-result.interface'; + +const stub = sinon.stub(); +const path = '/path/to/file'; +const mockedResult: BlameResult = { + [path]: { + '1': { + author: 'author', + date: '2020-03-03 10:00:00', + line: '1', + rev: '123' + } + } +}; +stub.returns(mockedResult); +const { Blamer } = proxyquire('./blamer', { + './vcs/git': { git: stub }, + './vcs/svn': { svn: stub } +}); + +test('should show authors of code in file under git', async (t: ExecutionContext) => { + const blamer = new Blamer(); + t.deepEqual(await blamer.blameByFile(path), mockedResult); +}); + +test('should show authors of code in file under svn', async (t: ExecutionContext) => { + const blamer = new Blamer('svn'); + t.deepEqual(await blamer.blameByFile(path), mockedResult); +});
src/blamer.ts+24 −0 added@@ -0,0 +1,24 @@ +import { BlameResult } from './blame-result.interface'; +import { git } from './vcs/git'; +import { svn } from './vcs/svn'; + +type VCSType = 'git' | 'svn'; + +export class Blamer { + private readonly type: VCSType = 'git'; + + constructor(type: VCSType = 'git') { + this.type = type; + } + + public async blameByFile(path: string): Promise<BlameResult> { + return this.getVCSBlamer()(path); + } + + private getVCSBlamer(): (path: string) => {} { + if (this.type === 'svn') { + return svn; + } + return git; + } +}
src/index.js+0 −3 removed@@ -1,3 +0,0 @@ -var Blamer = require('./Blamer'); - -module.exports = Blamer; \ No newline at end of file
src/index.ts+4 −0 added@@ -0,0 +1,4 @@ +import { Blamer } from './blamer'; + +export * from './blame-result.interface'; +export default Blamer;
src/vcs/git.js+0 −49 removed@@ -1,49 +0,0 @@ -var exec = require('child_process').exec; -var path = require('path'); -var Promise = require('bluebird'); - -function convertStringToObject(line) { - var commit = {}; - var matches = line.match(/(.+)\s+\((.+)\s+(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} (\+|\-)\d{4})\s+(\d+)\)(.*)/); - if (!matches) { - console.log('Wrong format'); - } - commit.rev = matches[1]; - commit.author = matches[2]; - commit.date = matches[3]; - commit.line = matches[5]; - - return commit; -} - -module.exports = function(file, args) { - args = typeof args === 'string' ? args : '-w'; - var realFile = path.basename(file); - var cwd = path.dirname(file); - return new Promise(function(resolve, reject) { - exec('git blame ' + args + ' ' + realFile, { - cwd: cwd, - maxBuffer: 1024 * 1024 - }, function(error, stdout, stderr) { - var result = {}, - lines, res = {}; - if (error) { - reject({ - error: error, - message: stderr - }); - } else { - lines = stdout.split("\n"); - lines.forEach(function(line) { - if (line !== '') { - line = convertStringToObject(line); - result[line.line] = line; - } - }); - res[file] = result; - resolve(res); - } - }); - }); - -};
src/vcs/git.spec.ts+31 −0 added@@ -0,0 +1,31 @@ +const proxyquire = require('proxyquire'); +import test, { ExecutionContext } from 'ava'; +import sinon from 'sinon'; + +const pathToGit: string = '/path/to/git'; +const pathToFile: string = '/path/to/file'; +const stubExeca = { + sync: sinon.stub().returns({ + stdout: + '68ca373e (Andrii Kucherenko 2018-08-14 12:02:47 +0300 1) # editorconfig.org\n' + + 'sadsaf //should be skipped from result\n' + }) +}; +const stubWhich = sinon.stub().returns(pathToGit); +const { git } = proxyquire('./git', { + execa: stubExeca, + which: stubWhich +}); + +test('should parse git blame command output', async (t: ExecutionContext) => { + t.deepEqual(await git(pathToFile), { + [pathToFile]: { + '1': { + author: 'Andrii Kucherenko', + date: '2018-08-14 12:02:47 +0300', + line: '1', + rev: '68ca373e' + } + } + }); +});
src/vcs/git.ts+33 −0 added@@ -0,0 +1,33 @@ +import execa from 'execa'; +import which from 'which'; +import { BlamedLine, BlameResult } from '../blame-result.interface'; + +const convertStringToObject = (sourceLine: string): BlamedLine => { + const matches = sourceLine.match( + /(.+)\s+\((.+)\s+(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} (\+|\-)\d{4})\s+(\d+)\)(.*)/ + ); + const [, rev, author, date, , line] = matches ? [...matches] : []; + return { + author, + date, + line, + rev + }; +}; + +export async function git(path: string): Promise<BlameResult> { + const blamedLines: { [line: string]: BlamedLine } = {}; + const pathToGit: string = await which('git'); + const result = execa.sync(pathToGit, ['blame', '-w', path]); + result.stdout.split('\n').forEach(line => { + if (line !== '') { + const blamedLine = convertStringToObject(line); + if (blamedLine.line) { + blamedLines[blamedLine.line] = blamedLine; + } + } + }); + return { + [path]: blamedLines + }; +}
src/vcs/svn.js+0 −53 removed@@ -1,53 +0,0 @@ -var exec = require('child_process').exec; -var path = require('path'); -var Promise = require('bluebird'); -var xml2js = require("xml2js"); - - -module.exports = function(file, args) { - args = typeof args === 'string' ? args : '--xml'; - var realFile = path.basename(file); - var cwd = path.dirname(file); - - function parseResult(buffer, resolve, reject) { - xml2js.parseString(buffer, function(err, result) { - if (err) { - reject({ - error: err - }); - } else { - var json = {}, - res = {}; - if (result.blame.target[0].entry) { - result.blame.target[0].entry.forEach(function(line) { - json[line.$['line-number']] = { - rev: line.commit[0].$.revision, - author: line.commit[0].author[0], - date: line.commit[0].date[0], - line: line.$['line-number'] - }; - }); - } - res[file] = json; - resolve(res); - } - }); - } - - return new Promise(function(resolve, reject) { - exec('svn blame ' + realFile + ' ' + args, { - cwd: cwd, - maxBuffer: 1024 * 1024 - }, function(error, stdout, stderr) { - if (error) { - reject({ - error: error, - message: stderr - }); - } else { - parseResult(stdout, resolve, reject); - } - }); - }); - -};
src/vcs/svn.spec.ts+42 −0 added@@ -0,0 +1,42 @@ +const proxyquire = require('proxyquire'); +import test, { ExecutionContext } from 'ava'; +import sinon from 'sinon'; + +const pathToSvn: string = '/path/to/svn'; +const pathToFile: string = '/path/to/file'; +const stubExeca = { + sync: sinon.stub().returns({ + stdout: + '<?xml version="1.0"?>\n' + + '<blame>\n' + + '<target path="' + + pathToFile + + '">\n' + + '<entry line-number="1">\n' + + '<commit revision="3">\n' + + '<author>sally</author>\n' + + '<date>2008-05-25T19:12:31.428953Z</date>\n' + + '</commit>\n' + + '</entry>\n' + + '</target>\n' + + '</blame>' + }) +}; +const stubWhich = sinon.stub().returns(pathToSvn); +const { svn } = proxyquire('./svn', { + execa: stubExeca, + which: stubWhich +}); + +test('should parse svn blame command', async (t: ExecutionContext) => { + t.deepEqual(await svn(pathToFile), { + [pathToFile]: { + '1': { + author: 'sally', + date: '2008-05-25T19:12:31.428953Z', + line: '1', + rev: '3' + } + } + }); +});
src/vcs/svn.ts+28 −0 added@@ -0,0 +1,28 @@ +import execa from 'execa'; +import which from 'which'; +import { parseStringPromise } from 'xml2js'; +import { BlamedLine, BlameResult } from '../blame-result.interface'; + +const parseResult = async ( + buffer: string +): Promise<{ [line: string]: BlamedLine }> => { + const result = await parseStringPromise(buffer); + const json: { [line: string]: BlamedLine } = {}; + result.blame.target[0].entry.forEach((line: any) => { + json[line.$['line-number']] = { + author: line.commit[0].author[0], + date: line.commit[0].date[0], + line: line.$['line-number'], + rev: line.commit[0].$.revision + }; + }); + return json; +}; + +export async function svn(path: string): Promise<BlameResult> { + const pathToSvn: string = await which('svn'); + const result = execa.sync(pathToSvn, ['blame', '--xml', path]); + return { + [path]: await parseResult(result.stdout) + }; +}
test/BlamerSpec.js+0 −46 removed@@ -1,46 +0,0 @@ -describe("Blamer", function(){ - var sut, file, git, Blamer; - - beforeEach(function () { - file = 'zzz'; - git = env.stub(); - git.withArgs(file, '--o').returns('promise (constructor args)'); - git.withArgs(file, '--a').returns('promise (call args)'); - git.withArgs(file).returns('promise (default args)'); - Blamer = proxyquire(sourcePath + 'Blamer',{ - "./vcs/git": git - }); - sut = new Blamer(); - }); - - - it('should set type of vcs', function () { - var blamer = new Blamer('git'); - blamer.getType().should.equal('git'); - }); - - it('should set type git as default vcs', function () { - sut.getType().should.equal('git'); - }); - - it('should get data from git with default args', function (){ - sut.blameByFile(file).should.equal('promise (default args)'); - }); - - it('should get data from git with args passed to constructor', function (){ - var blamer = new Blamer('git', '--o'); - blamer.blameByFile(file).should.equal('promise (constructor args)'); - }); - - it('should get data from git with args passed to method call', function (){ - sut.blameByFile(file, '--a').should.equal('promise (call args)'); - }); - - it('should throw exception in type of vcs dont supported', function (){ - sut.type = "test"; - chai.expect(function() { - sut.blameByFile(file) - }).to.throw('VCS "test" don\'t supported'); - }); - -});
test/_bootstrap.js+0 −24 removed@@ -1,24 +0,0 @@ -if (process.env['COVERAGE']) { - console.log('COVERAGE mode is on'); - global.sourcePath = __dirname + '/../.tmp/'; -} else { - global.sourcePath = __dirname + '/../src/' -} - -var chai = require('chai'); -var sinon = require('sinon'); -var proxyquire = require('proxyquire') - -chai.use(require('sinon-chai')); -chai.should(); - -beforeEach(function () { - global.chai = chai; - global.proxyquire = proxyquire; - global.sinon = sinon; - global.env = sinon.sandbox.create(); -}); - -afterEach(function () { - global.env.restore(); -});
test/vcs/gitSpec.js+0 −97 removed@@ -1,97 +0,0 @@ -var Promise = require('bluebird'); -describe('Git', function() { - var sut, exec, file, cwd, - callback, dirname, basename, - fileRaw, blameString; - - function initializeSut() { - sut = proxyquire(sourcePath + 'vcs/git', { - "child_process": { - exec: exec - }, - "path": { - dirname: dirname, - basename: basename - } - }); - } - - beforeEach(function() { - blameString = 'rev (author name 2014-10-18 17:51:36 +0300 1) A\n'; - - callback = env.stub(); - - exec = env.stub().callsArgWith(2, null, blameString, ''); - - cwd = 'cwd'; - - fileRaw = './README.md'; - file = __dirname + '/../README.md'; - - dirname = env.stub().withArgs(fileRaw).returns(cwd); - basename = env.stub().withArgs(fileRaw).returns(file); - initializeSut(); - }); - - it('should exec git blame', function() { - sut(fileRaw); - exec.should.have.been.calledWith( - 'git blame -w ' + file, { - cwd: cwd, - maxBuffer: 1024 * 1024 - }, - sinon.match.any - ); - }); - - it('should exec git blame with args', function() { - sut(fileRaw, '-M -w'); - exec.should.have.been.calledWith( - 'git blame -M -w ' + file, { - cwd: cwd, - maxBuffer: 1024 * 1024 - }, - sinon.match.any - ); - }); - - it('should exec git blame without args', function() { - sut(fileRaw, ''); - exec.should.have.been.calledWith( - 'git blame ' + file, { - cwd: cwd, - maxBuffer: 1024 * 1024 - }, - sinon.match.any - ); - }); - - it('should parse result of blame', function(done) { - sut(fileRaw).then(callback).finally(function() { - var res = {}; - res[fileRaw] = { - "1": { - "rev": "rev", - "author": "author name", - "date": "2014-10-18 17:51:36 +0300", - "line": "1" - } - }; - callback.should.have.been.calledWith(res); - done(); - }); - }); - - it('should reject promise on error', function() { - exec = env.stub().callsArgWith(2, 111, blameString, 'ololo'); - initializeSut(); - sut(fileRaw).error(callback).finally(function() { - callback.should.have.been.calledWith({ - error: 111, - message: 'ololo' - }); - }); - }); - - -});
test/vcs/svnSpec.js+0 −96 removed@@ -1,96 +0,0 @@ -var Promise = require('bluebird'); -describe('SVN', function() { - var sut, exec, file, cwd, - callback, dirname, basename, - fileRaw, blameString; - - function initializeSut() { - sut = proxyquire(sourcePath + 'vcs/svn', { - "child_process": { - exec: exec - }, - "path": { - dirname: dirname, - basename: basename - } - }); - } - - beforeEach(function() { - blameString = - '<?xml version="1.0" encoding="UTF-8"?><blame><target path="file"><entry line-number="1"><commit revision="rev"><author>author</author><date>2014-10-15T12:33:31.675393Z</date></commit></entry></target></blame>'; - - callback = env.stub(); - - exec = env.stub().callsArgWith(2, null, blameString, ''); - - cwd = 'cwd'; - - fileRaw = './README.md'; - file = __dirname + '/../README.md'; - - dirname = env.stub().withArgs(fileRaw).returns(cwd); - basename = env.stub().withArgs(fileRaw).returns(file); - initializeSut(); - }); - - it('should exec svn blame', function() { - sut(fileRaw); - exec.should.have.been.calledWith( - 'svn blame ' + file + ' --xml', { - cwd: cwd, - maxBuffer: 1024 * 1024 - }, - sinon.match.any - ); - }); - - it('should exec svn blame with args', function() { - sut(fileRaw, '--xml --verbose'); - exec.should.have.been.calledWith( - 'svn blame ' + file + ' --xml --verbose', { - cwd: cwd, - maxBuffer: 1024 * 1024 - }, - sinon.match.any - ); - }); - - it('should exec svn blame without args', function() { - sut(fileRaw, ''); - exec.should.have.been.calledWith( - 'svn blame ' + file + ' ', { - cwd: cwd, - maxBuffer: 1024 * 1024 - }, - sinon.match.any - ); - }); - - it('should parse result of blame', function(done) { - sut(fileRaw).then(callback).finally(function() { - var res = {}; - res[fileRaw] = { - "1": { - "rev": "rev", - "author": "author", - "date": "2014-10-15T12:33:31.675393Z", - "line": "1" - } - }; - callback.should.have.been.calledWith(res); - done(); - }); - }); - - it('should reject promise on error', function() { - exec = env.stub().callsArgWith(2, 111, blameString, 'ololo'); - initializeSut(); - sut(fileRaw).error(callback).finally(function() { - callback.should.have.been.calledWith({ - error: 111, - message: 'ololo' - }); - }); - }); -});
.travis.yml+15 −6 modified@@ -1,9 +1,18 @@ +sudo: false language: node_js - +env: + CODECOV_TOKEN="967cc3b5-600b-4b03-a6b2-6939a817c70e" node_js: - - '0.10' - + - 8 + - 9 + - 10 +# keep the npm cache to speed up installs +cache: + directories: + - "$HOME/.npm" script: - - ./node_modules/jscoverage/bin/jscoverage src .tmp - - npm run ci - - cat ./coverage/coverage.lcov | ./node_modules/coveralls/bin/coveralls.js \ No newline at end of file + - npm run all + - npm run build +after_success: + - npx nyc report --reporter=lcov | npx codecov + - npm run cov:check
tsconfig.json+45 −0 added@@ -0,0 +1,45 @@ +{ + "compilerOptions": { + "target": "es2017", + "outDir": "build/main", + "rootDir": "src", + "moduleResolution": "node", + "module": "commonjs", + "declaration": true, + "inlineSourceMap": true, + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + + "strict": true /* Enable all strict type-checking options. */, + + /* Strict Type-Checking Options */ + // "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, + // "strictNullChecks": true /* Enable strict null checks. */, + // "strictFunctionTypes": true /* Enable strict checking of function types. */, + // "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, + // "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, + // "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, + + /* Additional Checks */ + "noUnusedLocals": true /* Report errors on unused locals. */, + "noUnusedParameters": true /* Report errors on unused parameters. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, + + /* Debugging Options */ + "traceResolution": false /* Report module resolution log messages. */, + "listEmittedFiles": false /* Print names of generated files part of the compilation. */, + "listFiles": false /* Print names of files part of the compilation. */, + "pretty": true /* Stylize errors and messages using color and context. */, + + /* Experimental Options */ + // "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + // "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */, + + "lib": ["es2017"], + "types": ["node"], + "typeRoots": ["node_modules/@types", "src/types"] + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules/**"], + "compileOnSave": false +}
tsconfig.module.json+11 −0 added@@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "target": "esnext", + "outDir": "build/module", + "module": "esnext" + }, + "exclude": [ + "node_modules/**" + ] +}
tslint.json+20 −0 added@@ -0,0 +1,20 @@ +{ + "extends": [ + "tslint:latest", + "tslint-config-prettier" + ], + "rules": { + "interface-name": [ + true, + "never-prefix" + ], + // TODO: allow devDependencies only in **/*.spec.ts files: + // waiting on https://github.com/palantir/tslint/pull/3708 + "no-implicit-dependencies": [ + true, + "dev" + ], + "no-var-requires": false, + "no-console": false + } +}
yarn.lock+4592 −0 added
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-8cxp-cjm8-fj36ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-10807ghsaADVISORY
- github.com/kucherenko/blamer/commit/5fada8c9b6986ecd28942b724fa682e77ce1e11cghsaWEB
- github.com/kucherenko/blamer/commit/5fada8c9b6986ecd28942b724fa682e77ce1e11c%2Cmitrex_refsource_MISC
- snyk.io/vuln/SNYK-JS-BLAMER-559541ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.