Skip to content
This repository was archived by the owner on Jun 15, 2021. It is now read-only.

Commit 2cc08f5

Browse files
committed
feat: report errors on invalid 'uses' values (#50)
Fixes #6
1 parent 1e0fc49 commit 2cc08f5

File tree

3 files changed

+122
-1
lines changed

3 files changed

+122
-1
lines changed

src/analysis/properties-analyzer.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,54 @@
44

55
import { BoundNodeVisitor } from "../binding/bound-node-visitor";
66
import { DiagnosticBag } from "../util/diagnostics";
7-
import { BoundDocument, BoundResolves, BoundSecrets, BoundOn, BoundEnv, BoundNeeds } from "../binding/bound-nodes";
7+
import {
8+
BoundDocument,
9+
BoundResolves,
10+
BoundSecrets,
11+
BoundOn,
12+
BoundEnv,
13+
BoundNeeds,
14+
BoundUses,
15+
} from "../binding/bound-nodes";
816
import { MAXIMUM_SUPPORTED_SECRETS } from "../util/constants";
917
import * as webhooks from "@octokit/webhooks-definitions";
1018

1119
export function analyzeProperties(document: BoundDocument, actions: ReadonlySet<string>, bag: DiagnosticBag): void {
1220
new PropertiesAnalyzer(document, actions, bag);
1321
}
22+
/*
23+
24+
fragment ALPHANUM : [a-zA-Z0-9];
25+
fragment HEX : [0-9a-fA-F]+;
26+
INTEGER : [0-9]+;
27+
28+
29+
*/
30+
31+
module USES_REGEX {
32+
const ALPHA_NUM = `[a-zA-Z0-9]`;
33+
const ALPHA_NUM_DASH = `[a-zA-Z0-9-]`;
34+
35+
const DOCKER_HOST_COMPONENT = `(${ALPHA_NUM}|(${ALPHA_NUM}${ALPHA_NUM_DASH}*${ALPHA_NUM}))`;
36+
const DOCKER_REGISTRY = `(${DOCKER_HOST_COMPONENT}(\\.${DOCKER_HOST_COMPONENT})*(:[0-9]+)?\\/)`;
37+
const DOCKER_PATH_COMPONENT = `(${ALPHA_NUM}+([._-]${ALPHA_NUM}+)*)`;
38+
const DOCKER_TAG = `(:[a-zA-Z0-9_]+)`;
39+
const DOCKER_DIGEST_ALGORITHM = `[A-Za-z]${ALPHA_NUM}*`;
40+
const DOCKER_DIGEST = `(@${DOCKER_DIGEST_ALGORITHM}([+.-_]${DOCKER_DIGEST_ALGORITHM})*:[a-fA-F0-9]+)`;
41+
const DOCKER_USES = `docker:\\/\\/${DOCKER_REGISTRY}?${DOCKER_PATH_COMPONENT}(\\/${DOCKER_PATH_COMPONENT})*(${DOCKER_TAG}|${DOCKER_DIGEST})?`;
42+
43+
const LOCAL_USES = `\\.\\/.*`;
44+
45+
const REMOTE_OWNER = `${ALPHA_NUM}+(${ALPHA_NUM_DASH}${ALPHA_NUM}+)*`;
46+
const REMOTE_REPO = `[a-zA-Z0-9-_.]+`;
47+
const REMOTE_USES = `${REMOTE_OWNER}\\/${REMOTE_REPO}\\/.*@.+`;
48+
49+
const COMBINED = new RegExp(`^(${DOCKER_USES})|(${LOCAL_USES})|(${REMOTE_USES})$`);
50+
51+
export function test(value: string): boolean {
52+
return COMBINED.test(value);
53+
}
54+
}
1455

1556
class PropertiesAnalyzer extends BoundNodeVisitor {
1657
private readonly secrets = new Set<string>();
@@ -90,4 +131,12 @@ class PropertiesAnalyzer extends BoundNodeVisitor {
90131
}
91132
}
92133
}
134+
135+
protected visitUses(node: BoundUses): void {
136+
if (node.value) {
137+
if (!USES_REGEX.test(node.value.value)) {
138+
this.bag.invalidUses(node.value.syntax.range);
139+
}
140+
}
141+
}
93142
}

src/util/diagnostics.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export enum DiagnosticCode {
3939
DuplicateActions,
4040
ReservedEnvironmentVariable,
4141
UnrecognizedEvent,
42+
InvalidUses,
4243
}
4344

4445
export interface Diagnostic {
@@ -253,4 +254,12 @@ export class DiagnosticBag {
253254
message: `The event '${event}' is not a known event type.`,
254255
});
255256
}
257+
258+
public invalidUses(range: TextRange): void {
259+
this.items.push({
260+
range,
261+
code: DiagnosticCode.InvalidUses,
262+
message: `The 'uses' property must be a path, a Docker image, or an owner/repo@ref remote.`,
263+
});
264+
}
256265
}

test/properties-analysis.spec.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,69 @@ ERROR: This property has duplicate 'b' actions.
224224
10 | ]
225225
11 | }
226226
"
227+
`);
228+
});
229+
230+
it("reports an error on invalid local 'uses' value", () => {
231+
expectDiagnostics(`
232+
action "a" {
233+
uses = "./ci"
234+
}
235+
action "b" {
236+
uses = "ci"
237+
}
238+
`).toMatchInlineSnapshot(`
239+
"
240+
ERROR: The 'uses' property must be a path, a Docker image, or an owner/repo@ref remote.
241+
4 | }
242+
5 | action \\"b\\" {
243+
6 | uses = \\"ci\\"
244+
| ^^^^
245+
7 | }
246+
8 |
247+
"
248+
`);
249+
});
250+
251+
it("reports an error on invalid remote 'uses' value", () => {
252+
expectDiagnostics(`
253+
action "a" {
254+
uses = "owner/repo/path@ref"
255+
}
256+
action "b" {
257+
uses = "owner/repo"
258+
}
259+
`).toMatchInlineSnapshot(`
260+
"
261+
ERROR: The 'uses' property must be a path, a Docker image, or an owner/repo@ref remote.
262+
4 | }
263+
5 | action \\"b\\" {
264+
6 | uses = \\"owner/repo\\"
265+
| ^^^^^^^^^^^^
266+
7 | }
267+
8 |
268+
"
269+
`);
270+
});
271+
272+
it("reports an error on invalid docker 'uses' value", () => {
273+
expectDiagnostics(`
274+
action "a" {
275+
uses = "docker://image"
276+
}
277+
action "b" {
278+
uses = "docker://?"
279+
}
280+
`).toMatchInlineSnapshot(`
281+
"
282+
ERROR: The 'uses' property must be a path, a Docker image, or an owner/repo@ref remote.
283+
4 | }
284+
5 | action \\"b\\" {
285+
6 | uses = \\"docker://?\\"
286+
| ^^^^^^^^^^^^
287+
7 | }
288+
8 |
289+
"
227290
`);
228291
});
229292
});

0 commit comments

Comments
 (0)