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
10 changes: 8 additions & 2 deletions .github/workflows/live-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ jobs:
permissions:
contents: read
env:
GH_TOKEN: ${{ github.token }}
NO_COLOR: '1'
steps:
- name: Checkout
Expand All @@ -55,6 +54,10 @@ jobs:
run: npm run build

- name: Walk the credential-free fixture chain
# The trusted driver reads a public GitHub issue through gh. Repository
# commands run as separate children and strip GH_TOKEN before execution.
env:
GH_TOKEN: ${{ github.token }}
run: node scripts/e2e-validation.mjs no-key

- name: Upload sanitized no-key evidence
Expand All @@ -78,7 +81,6 @@ jobs:
permissions:
contents: read
env:
GH_TOKEN: ${{ github.token }}
NO_COLOR: '1'
# The image and engine the product's repository runner must use. The proof step
# below preloads and measures this same digest; a repository command never runs
Expand Down Expand Up @@ -204,7 +206,11 @@ jobs:
run: npm run check

- name: Run the bounded real-model chain
# Only this trusted driver can use gh for GitHub API reads. All install,
# build, audit, deterministic tests and sandbox proofs run token-free.
# Workspace-facing children are given a stripped environment by the driver.
env:
GH_TOKEN: ${{ github.token }}
BHARATCODE_API_KEY: ${{ secrets.BHARATCODE_API_KEY }}
BHARATCODE_MODEL: ${{ inputs.model }}
# Below the transport's own ceiling, not above it. Node's fetch gives up at
Expand Down
34 changes: 33 additions & 1 deletion tests/security/live-validation-workflow-policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,39 @@ describe('the controlled live-validation workflow', () => {
expect(WORKFLOW).not.toMatch(/NPM_TOKEN|NODE_AUTH_TOKEN|GITHUB_TOKEN:\s*\$\{\{\s*secrets/);
});

it('uses the ephemeral GitHub token only for MergeSutra-owned reads', () => {
it('never gives a GitHub token to job-wide install, build, test or audit commands', () => {
// Two token references are not enough: the previous policy passed even
// when both were at job scope, exposing them to npm ci and npm run check.
const scopes = [
{
body: job('no-key-chain', 'live-model'),
allowedStep: 'Walk the credential-free fixture chain',
},
{
body: job('live-model'),
allowedStep: 'Run the bounded real-model chain',
},
];

for (const { body, allowedStep } of scopes) {
const stepsStart = body.indexOf(' steps:\n');
expect(stepsStart, 'the job has no steps boundary').toBeGreaterThanOrEqual(0);
const jobHeader = body.slice(0, stepsStart);
expect(jobHeader, 'GH_TOKEN must not be defined for an entire job').not.toMatch(
/^\s*GH_TOKEN:/m,
);

const sections = body.split(/^ {6}- name: /m).slice(1);
const exposed = sections
.filter((section) => /^ {10}GH_TOKEN:/m.test(section))
.map((section) => section.split('\n')[0]);
expect(exposed, 'only the trusted E2E driver may receive the token').toEqual([allowedStep]);

const driver = sections.find((section) => section.split('\n')[0] === allowedStep);
expect(driver, 'the driver step is missing').toBeDefined();
expect(driver).toMatch(/^ {8}env:\n {10}GH_TOKEN: \$\{\{ github\.token \}\}\n/m);
}

expect(WORKFLOW.match(/GH_TOKEN:\s*\$\{\{ github\.token \}\}/g) ?? []).toHaveLength(2);
expect(WORKFLOW).not.toMatch(/GITHUB_TOKEN:/);
});
Expand Down
Loading