forked from todogroup/repolinter
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add files-not-hash rule for files detections.
- Loading branch information
Neil Zhao
committed
Sep 26, 2022
1 parent
2e48a85
commit d2f262a
Showing
3 changed files
with
206 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"$schema": "http://json-schema.org/draft-07/schema", | ||
"$id": "https://raw.githubusercontent.com/todogroup/repolinter/master/rules/file-not-hash-config.json", | ||
"type": "object", | ||
"properties": { | ||
"globsAll": { | ||
"type": "array", | ||
"items": { "type": "string" } | ||
}, | ||
"nocase": { | ||
"type": "boolean", | ||
"default": false | ||
}, | ||
"algorithm": { | ||
"type": "string", | ||
"default": "sha256" | ||
}, | ||
"hashes": { | ||
"type": "array", | ||
"items": { "type": "string" } | ||
} | ||
}, | ||
"required": ["hashes"], | ||
"oneOf": [{ "required": ["globsAny"] }, { "required": ["files"] }] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
const Result = require('../lib/result') | ||
const crypto = require('crypto') | ||
// eslint-disable-next-line no-unused-vars | ||
const FileSystem = require('../lib/file_system') | ||
|
||
/** | ||
* Check files not include a list of certain cryptographic hashes. | ||
* | ||
* @param {FileSystem} fs A filesystem object configured with filter paths and target directories | ||
* @param {object} options The rule configuration | ||
* @returns {Promise<Result>} The lint rule result | ||
*/ | ||
async function filesNotHash(fs, options) { | ||
const fileList = options.globsAll || options.files | ||
const files = await fs.findAllFiles(fileList, !!options.nocase) | ||
|
||
if (files.length === 0) { | ||
return new Result( | ||
'Did not find file matching the specified patterns', | ||
fileList.map(f => { | ||
return { passed: false, pattern: f } | ||
}), | ||
!options['fail-on-non-existent'] | ||
) | ||
} | ||
|
||
let algorithm = options.algorithm | ||
if (algorithm === undefined) { | ||
algorithm = 'sha256' | ||
} | ||
|
||
const resultsList = await Promise.all( | ||
options.hashes.map(async hash => { | ||
const singleHashResults = ( | ||
await Promise.all( | ||
files.map(async file => { | ||
const digester = crypto.createHash(algorithm) | ||
let fileContents = await fs.getFileContents(file) | ||
if (fileContents === undefined) { | ||
fileContents = '' | ||
} | ||
digester.update(fileContents) | ||
const fileHash = digester.digest('hex') | ||
const passed = fileHash !== hash | ||
const message = passed ? "Doesn't Matches hash" : 'Match hash' | ||
|
||
return { | ||
passed, | ||
path: file, | ||
message | ||
} | ||
}) | ||
) | ||
).filter(result => !result.passed) | ||
return singleHashResults | ||
}) | ||
) | ||
|
||
const results = [] | ||
resultsList.map(singleHashResults => { | ||
for (const result of singleHashResults) { | ||
results.push(result) | ||
} | ||
}) | ||
|
||
const passed = results.length === 0 | ||
|
||
if (passed) { | ||
return new Result('No file matching hash found', results, passed) | ||
} | ||
return new Result('File matching has found', results, passed) | ||
} | ||
|
||
module.exports = filesNotHash |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// Copyright 2022 TODO Group. All rights reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
const chai = require('chai') | ||
const expect = chai.expect | ||
const filesNotHash = require('../../rules/files-not-hash') | ||
|
||
describe('rule', () => { | ||
describe('files_not_hash', () => { | ||
it('returns pass if requested files not matches the hashes', async () => { | ||
/** @type {any} */ | ||
const mockfs = { | ||
findAllFiles() { | ||
return ['README.md'] | ||
}, | ||
getFileContents() { | ||
return 'foo' | ||
}, | ||
targetDir: '.' | ||
} | ||
|
||
const ruleopts = { | ||
globsAll: ['README.md'], | ||
hashes: ['notAValidHash'] | ||
} | ||
|
||
const actual = await filesNotHash(mockfs, ruleopts) | ||
expect(actual.passed).to.equal(true) | ||
}) | ||
|
||
it('returns failure if requested files matches the hash', async () => { | ||
/** @type {any} */ | ||
const mockfs = { | ||
findAllFiles() { | ||
return ['README.md'] | ||
}, | ||
getFileContents() { | ||
return 'foo' | ||
}, | ||
targetDir: '.' | ||
} | ||
|
||
const ruleopts = { | ||
globsAll: ['README.md'], | ||
hashes: [ | ||
'2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae' | ||
] | ||
} | ||
|
||
const actual = await filesNotHash(mockfs, ruleopts) | ||
expect(actual.passed).to.equal(false) | ||
expect(actual.targets).to.have.length(1) | ||
expect(actual.targets[0]).to.deep.include({ | ||
passed: false, | ||
path: 'README.md' | ||
}) | ||
}) | ||
|
||
it('returns failed if requested file contents exists different algorithm', async () => { | ||
/** @type {any} */ | ||
const mockfs = { | ||
findAllFiles() { | ||
return ['README.md'] | ||
}, | ||
getFileContents() { | ||
return 'foo' | ||
}, | ||
targetDir: '.' | ||
} | ||
|
||
const ruleopts = { | ||
globsAll: ['README.md'], | ||
algorithm: 'sha512', | ||
hashes: [ | ||
'f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7' | ||
] | ||
} | ||
|
||
const actual = await filesNotHash(mockfs, ruleopts) | ||
expect(actual.passed).to.equal(false) | ||
expect(actual.targets).to.have.length(1) | ||
expect(actual.targets[0]).to.deep.include({ | ||
passed: false, | ||
path: 'README.md' | ||
}) | ||
}) | ||
|
||
it('returns success if requested file does not exist', async () => { | ||
/** @type {any} */ | ||
const mockfs = { | ||
findAllFiles() { | ||
return [] | ||
}, | ||
getFileContents() {}, | ||
targetDir: '.' | ||
} | ||
|
||
const ruleopts = { | ||
globsAll: ['README.md'], | ||
content: 'foo' | ||
} | ||
|
||
const actual = await filesNotHash(mockfs, ruleopts) | ||
expect(actual.passed).to.equal(true) | ||
}) | ||
}) | ||
}) |