Skip to content

Commit

Permalink
Merge pull request #595 from oceanprotocol/issue-589-code-hash-git
Browse files Browse the repository at this point in the history
use only git tracked files for computing code hash
  • Loading branch information
alexcos20 authored Aug 14, 2024
2 parents 4febd84 + 1f11730 commit f3dd129
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 8 deletions.
10 changes: 8 additions & 2 deletions src/test/integration/logs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,17 @@ describe('LogDatabase CRUD', () => {
await new Promise((resolve) => setTimeout(resolve, 1000)) // Delay to allow log to be processed

// Define the time frame for the log retrieval
const startTime = new Date(Date.now() - 5000) // 5 seconds ago
const startTime = new Date(Date.now() - 3000) // 3 seconds ago
const endTime = new Date() // current time

// Retrieve the latest log entries
let logs = await database.logs.retrieveMultipleLogs(startTime, endTime, 300)
let logs = await database.logs.retrieveMultipleLogs(
startTime,
endTime,
200,
LOGGER_MODULE_NAMES.HTTP,
LOG_LEVELS_STR.LEVEL_DEBUG
)
logs = logs.filter((log) => log.message === newLogEntry.message)

expect(logs?.length).to.equal(1)
Expand Down
33 changes: 33 additions & 0 deletions src/test/unit/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ import { sleep, getEventFromTx, sanitizeServiceFiles } from '../../utils/util.js
import { URLUtils } from '../../utils/url.js'
import { validateConsumerParameters } from '../../utils/validators.js'
import { ConsumerParameter } from '../../@types/DDO/ConsumerParameter.js'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
import { computeCodebaseHash } from '../../utils/attestation.js'
import { existsSync, rmSync, writeFileSync } from 'node:fs'

describe('Utilities Functions', () => {
const fileName = 'hashFile.previous'

describe('sleep function', () => {
it('should resolve after specified time', async () => {
const startTime = new Date().getTime()
Expand Down Expand Up @@ -270,4 +276,31 @@ describe('Utilities Functions', () => {
expect(result.valid).to.equal(false)
expect(result.reason).includes('parameter is required')
})

it('should check code hash integrity', async () => {
try {
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const codeHashBefore = await computeCodebaseHash(__dirname)
console.log(`compute code hash before`, codeHashBefore)
if (existsSync(fileName)) {
rmSync(fileName)
} else {
writeFileSync(fileName, codeHashBefore)
const codeHashAfter = await computeCodebaseHash(__dirname)
console.log(`compute code hash after`, codeHashAfter)
expect(codeHashBefore).to.equal(codeHashAfter)
}
} catch (error) {
console.error(error)
}
})

after(() => {
if (existsSync(fileName)) {
try {
rmSync(fileName)
} catch (err) {}
}
})
})
58 changes: 52 additions & 6 deletions src/utils/attestation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as fs from 'fs'
import * as crypto from 'crypto'
import * as path from 'path'
import exec from 'child_process'

// Recursively get all files in a directory and its subdirectories
function getAllFiles(directory: string): string[] {
Expand All @@ -19,19 +20,64 @@ function getAllFiles(directory: string): string[] {
return files
}

// execute some command from terminal (could depend on OS)
// for now only used for git commands
function executeCommand(command: string): Promise<string> {
return new Promise((resolve) => {
try {
// eslint-disable-next-line security/detect-child-process
exec.exec(command, (_error: any, stdout: any) => {
resolve(stdout)
})
} catch (err) {
resolve(null)
}
})
}
// Compute the SHA-256 hash for a single file
async function computeFileHash(filePath: string): Promise<string> {
const data = fs.readFileSync(filePath)
const hashBuffer = await crypto.subtle.digest('SHA-256', data)
const hashArray = Array.from(new Uint8Array(hashBuffer))
const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join('')
return hashHex
try {
// eslint-disable-next-line security/detect-non-literal-fs-filename
const data = fs.readFileSync(filePath)
const hashBuffer = await crypto.subtle.digest('SHA-256', data)
const hashArray = Array.from(new Uint8Array(hashBuffer))
const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join('')
return hashHex
} catch (err) {
// sometimes the file doesn't exist anymore (specially in dashboard stuff, silently ignore it)
return ''
}
}

// Compute the hash for all files in the codebase
export async function computeCodebaseHash(directory: string): Promise<string> {
const files = getAllFiles(directory)
const hashes = await Promise.all(files.map(computeFileHash))
// get git root folder
let gitRootDir = await executeCommand('git rev-parse --show-toplevel')
if (gitRootDir) {
// clear line breaks
gitRootDir = gitRootDir.trim().replace(/[\r\n]+/g, '')
}
// try get all git tracked files
const gitFiles = await executeCommand('git ls-files')

let hashes = ['']
// use git files when possible
if (gitFiles && gitRootDir) {
const toBeHashed = gitFiles
.trim()
.replace(/[\r\n]+/g, '|') // clear line breaks
.split('|')
hashes = await Promise.all(
toBeHashed.map((fileName) => {
return computeFileHash(gitRootDir + '/' + fileName)
})
)
} else {
// otherwise use old way (prone to different hashes if we have local different files, even if not tracked on repo)
hashes = await Promise.all(files.map(computeFileHash))
}

const allHashes = hashes.join('') // Combine all hashes into a single string
const hashBuffer = await crypto.subtle.digest(
'SHA-256',
Expand Down

0 comments on commit f3dd129

Please sign in to comment.