Skip to content
Open
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
3 changes: 0 additions & 3 deletions .github/workflows/staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ jobs:
BOS_STORAGE_ORIGIN: ${{ vars.BOS_STORAGE_ORIGIN || 'https://upload.everything.dev' }}
BOS_STORAGE_API_KEY: ${{ secrets.BOS_STAGING_STORAGE_API_KEY }}
RAILWAY_TOKEN: ${{ secrets.RAILWAY_STAGING_TOKEN }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to keep the workspace token out of staging. One side effect: without it the deploy leg can't repoint the service source, so staging falls back to a plain redeploy with the "service source was not repointed" notice (the run before c768e66 shows it). The source is still pinned to rc.29 from last night's run, so when the rc.30 bump lands, staging will publish rc.30 config but keep pulling the rc.29 image until someone repoints it in Railway. Could you note that in the PR, or add "repoint staging's source by hand" to the upgrade steps? cc @itexpert120 since he added these lines yesterday.

RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_STAGING_API_TOKEN }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RAILWAY_STAGING_API_TOKEN secret is still in the repo; last night's run shows it's set. Worth deleting it, and revoking the token in Railway, so nothing can pick it up again.

RAILWAY_PROJECT_ID: ${{ vars.RAILWAY_STAGING_PROJECT_ID }}
RAILWAY_ENVIRONMENT_ID: ${{ vars.RAILWAY_STAGING_ENVIRONMENT_ID }}
steps:
- name: Checkout code
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 (Node 24)
Expand Down
13 changes: 13 additions & 0 deletions scripts/check-workflows.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@ test("a staging workflow on production's Railway token is refused", () => {
]);
});

test("a staging workflow with a workspace-scoped Railway token is refused", () => {
const directory = workflows({
"staging.yml": `env:
RAILWAY_TOKEN: \${{ secrets.RAILWAY_STAGING_TOKEN }}
RAILWAY_API_TOKEN: \${{ secrets.RAILWAY_STAGING_API_TOKEN }}
RAILWAY_PROJECT_ID: \${{ vars.RAILWAY_STAGING_PROJECT_ID }}
`,
});
assert.deepEqual(checkWorkflows(directory), [
`staging.yml: sets RAILWAY_API_TOKEN, a workspace-scoped Railway token that reaches every project in the workspace, production included`,
]);
});

test("our workflows pass", () => {
const directory = workflows({
"staging.yml": `env:
Expand Down
7 changes: 6 additions & 1 deletion scripts/check-workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ export function checkWorkflows(directory: string): string[] {
if (file === "staging.yml" && /secrets\.RAILWAY_TOKEN\b/.test(text)) {
problems.push(`${file}: reads secrets.RAILWAY_TOKEN, production's Railway token`);
}
if (file === "staging.yml" && /^\s*RAILWAY_API_TOKEN\s*:/m.test(text)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only catches the unquoted RAILWAY_API_TOKEN: key. "RAILWAY_API_TOKEN": …, echo "RAILWAY_API_TOKEN=${{ secrets.RAILWAY_STAGING_API_TOKEN }}" >> $GITHUB_ENV, or an inline RAILWAY_API_TOKEN=… pnpm run bos deploy would all get through. Maybe also match the secret, like the secrets.RAILWAY_TOKEN rule above, e.g. /secrets\.RAILWAY_\w*API_TOKEN\b|\bRAILWAY_API_TOKEN\b/, with a test for the = form?

problems.push(
`${file}: sets RAILWAY_API_TOKEN, a workspace-scoped Railway token that reaches every project in the workspace, production included`,
);
}
for (const match of text.matchAll(
/^\s*uses:\s*([^\s#]+\/\.github\/workflows\/[^\s#@]+)@([^\s#]+)/gm,
)) {
Expand All @@ -33,6 +38,6 @@ if (import.meta.url === `file://${resolve(process.argv[1] ?? "")}`) {
for (const problem of problems) console.error(problem);
if (problems.length > 0) process.exit(1);
console.log(
"Workflows keep staging on its own token, pass no inherited secrets, and pin by SHA.",
"Workflows keep staging on its own project token, pass no inherited secrets, and pin by SHA.",
);
}
Loading