Skip to content

Commit

Permalink
feat: add files-not-hash rule for files detections.
Browse files Browse the repository at this point in the history
  • Loading branch information
Neil Zhao committed Sep 26, 2022
1 parent 2e48a85 commit d2f262a
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 0 deletions.
25 changes: 25 additions & 0 deletions rules/files-not-hash-config.json
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"] }]
}
74 changes: 74 additions & 0 deletions rules/files-not-hash.js
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
107 changes: 107 additions & 0 deletions tests/rules/files_not_hash_tests.js
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)
})
})
})

0 comments on commit d2f262a

Please sign in to comment.