Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions loops/issue-dev-loop/scripts/lib/github-identity.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,21 @@ function authorizedPushBranches(authorization) {

export function assertGitCommandPolicy(role, args, { authorization = null } = {}) {
const subcommand = gitSubcommand(args)
const restoreCleanlinessProbe =
role === 'automation' &&
(sameArguments(args, ['ls-files', '-v', '-z']) ||
sameArguments(args, [
'-c',
'core.fileMode=true',
'diff',
'--quiet',
'--no-ext-diff',
'--no-textconv',
'HEAD',
'--',
]))
if (restoreCleanlinessProbe) return

if (subcommand.name === 'push') {
if (role === 'reviewer') throw new Error('reviewer identity cannot run git push')
const claimBranch = authorization?.issue?.branch
Expand Down
38 changes: 37 additions & 1 deletion loops/issue-dev-loop/tests/github-identity-routing.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ import {
canonicalFinalizationRecord,
finalizationRecordDigest,
} from '../scripts/lib/finalization-proof.mjs'
import { resolveExecutable } from '../scripts/lib/github-identity.mjs'
import {
assertGitCommandPolicy,
resolveExecutable,
} from '../scripts/lib/github-identity.mjs'

const execFileAsync = promisify(execFile)
const testDirectory = path.dirname(fileURLToPath(import.meta.url))
Expand Down Expand Up @@ -2324,6 +2327,39 @@ test('authenticated remote Git accepts only exact origin and authorized ref shap
}
})

test('automation allows only the exact restore cleanliness Git probes', () => {
for (const args of [
['ls-files', '-v', '-z'],
[
'-c',
'core.fileMode=true',
'diff',
'--quiet',
'--no-ext-diff',
'--no-textconv',
'HEAD',
'--',
],
]) {
assert.doesNotThrow(() => assertGitCommandPolicy('automation', args))
assert.throws(
() => assertGitCommandPolicy('reviewer', args),
/outside the authenticated reviewer command tree/,
)
}
for (const args of [
['ls-files', '-v'],
['ls-files', '-v', '-z', '--others'],
['-c', 'core.fileMode=false', 'diff', '--quiet', 'HEAD', '--'],
['-c', 'core.fileMode=true', 'diff', 'HEAD', '--'],
]) {
assert.throws(
() => assertGitCommandPolicy('automation', args),
/outside the authenticated automation command tree/,
)
}
})

test('authenticated real Git ignores local execution hooks and configured diff helpers', async () => {
const fixture = await createFixture({ realGit: true })
const realGit = await resolveExecutable('git', process.env)
Expand Down