Skip to content

Commit aa2090a

Browse files
committed
fix(loop): reject flow-style job permissions
1 parent 49ec2e9 commit aa2090a

2 files changed

Lines changed: 28 additions & 2 deletions

File tree

loops/issue-dev-loop/scripts/lib/validation.mjs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,16 @@ function yamlMapping(line) {
6666

6767
function historicalWorkflowIsLowPrivilege(source) {
6868
const lines = activeYamlLines(source)
69-
if (lines.some((line) => line.includes('\t') || /^\s*<<\s*:/.test(line))) return false
69+
if (
70+
lines.some(
71+
(line) =>
72+
line.includes('\t') ||
73+
/^\s*<<\s*:/.test(line) ||
74+
/^\s*[?:]\s/.test(line),
75+
)
76+
) {
77+
return false
78+
}
7079
const mappings = lines.map((line, index) => ({
7180
index,
7281
mapping: yamlMapping(line),
@@ -129,7 +138,20 @@ function historicalWorkflowIsLowPrivilege(source) {
129138
}
130139
permissions.set(mapping.key, mapping.value)
131140
}
132-
return permissions.get('contents') === 'read'
141+
if (permissions.get('contents') !== 'read') return false
142+
143+
const jobsEntries = topLevelMappings.filter(({ mapping }) => mapping.key === 'jobs')
144+
if (jobsEntries.length !== 1 || jobsEntries[0].mapping.value) return false
145+
const jobDeclarations = blockLines(jobsEntries[0]).filter(
146+
(line) => (line.match(/^\s*/)?.[0].length ?? 0) <= 2,
147+
)
148+
return (
149+
jobDeclarations.length > 0 &&
150+
jobDeclarations.every((line) => {
151+
const mapping = yamlMapping(line)
152+
return mapping?.indent === 2 && !mapping.value
153+
})
154+
)
133155
}
134156

135157
async function validateLoopMode({

loops/issue-dev-loop/tests/runtime.test.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5088,6 +5088,10 @@ test('historical active-run targets can validate without newer trusted runtime f
50885088
'permissions:\n contents: read\npermissions:\n contents: read\n',
50895089
),
50905090
historicalWorkflow.replace('permissions:\n', 'permissions: &shared_permissions\n'),
5091+
historicalWorkflow.replace(
5092+
'jobs:\n',
5093+
'jobs:\n unsafe: {permissions: write-all, runs-on: ubuntu-latest, steps: []}\n',
5094+
),
50915095
]
50925096
for (const unsafeWorkflow of unsafeWorkflows) {
50935097
await writeFile(workflowPath, unsafeWorkflow, 'utf8')

0 commit comments

Comments
 (0)