@birdcc/linter 0.0.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.oxfmtrc.json +16 -0
- package/LICENSE +674 -0
- package/README.md +210 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/bgp.d.ts +5 -0
- package/dist/rules/bgp.d.ts.map +1 -0
- package/dist/rules/bgp.js +131 -0
- package/dist/rules/bgp.js.map +1 -0
- package/dist/rules/catalog.d.ts +14 -0
- package/dist/rules/catalog.d.ts.map +1 -0
- package/dist/rules/catalog.js +61 -0
- package/dist/rules/catalog.js.map +1 -0
- package/dist/rules/cfg.d.ts +5 -0
- package/dist/rules/cfg.d.ts.map +1 -0
- package/dist/rules/cfg.js +264 -0
- package/dist/rules/cfg.js.map +1 -0
- package/dist/rules/net.d.ts +5 -0
- package/dist/rules/net.d.ts.map +1 -0
- package/dist/rules/net.js +140 -0
- package/dist/rules/net.js.map +1 -0
- package/dist/rules/normalize.d.ts +6 -0
- package/dist/rules/normalize.d.ts.map +1 -0
- package/dist/rules/normalize.js +65 -0
- package/dist/rules/normalize.js.map +1 -0
- package/dist/rules/ospf.d.ts +5 -0
- package/dist/rules/ospf.d.ts.map +1 -0
- package/dist/rules/ospf.js +136 -0
- package/dist/rules/ospf.js.map +1 -0
- package/dist/rules/shared.d.ts +46 -0
- package/dist/rules/shared.d.ts.map +1 -0
- package/dist/rules/shared.js +184 -0
- package/dist/rules/shared.js.map +1 -0
- package/dist/rules/sym.d.ts +5 -0
- package/dist/rules/sym.d.ts.map +1 -0
- package/dist/rules/sym.js +188 -0
- package/dist/rules/sym.js.map +1 -0
- package/dist/rules/type.d.ts +5 -0
- package/dist/rules/type.d.ts.map +1 -0
- package/dist/rules/type.js +130 -0
- package/dist/rules/type.js.map +1 -0
- package/package.json +41 -0
- package/src/index.ts +155 -0
- package/src/rules/bgp.ts +239 -0
- package/src/rules/catalog.ts +80 -0
- package/src/rules/cfg.ts +562 -0
- package/src/rules/net.ts +262 -0
- package/src/rules/normalize.ts +90 -0
- package/src/rules/ospf.ts +221 -0
- package/src/rules/shared.ts +354 -0
- package/src/rules/sym.ts +342 -0
- package/src/rules/type.ts +210 -0
- package/test/linter.bgp-ospf.test.ts +129 -0
- package/test/linter.migration.test.ts +66 -0
- package/test/linter.net-type.test.ts +132 -0
- package/test/linter.sym-cfg.test.ts +224 -0
- package/test/linter.test.ts +21 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { BirdDiagnostic } from "@birdcc/core";
|
|
2
|
+
import type { FilterBodyStatement, SourceRange } from "@birdcc/parser";
|
|
3
|
+
import {
|
|
4
|
+
createRuleDiagnostic,
|
|
5
|
+
filterAndFunctionDeclarations,
|
|
6
|
+
pushUniqueDiagnostic,
|
|
7
|
+
scalarTypeOfExpression,
|
|
8
|
+
type BirdRule,
|
|
9
|
+
} from "./shared.js";
|
|
10
|
+
|
|
11
|
+
interface MatchPair {
|
|
12
|
+
left: string;
|
|
13
|
+
right: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const normalizeLeftExpression = (value: string): string =>
|
|
17
|
+
value.replace(/^\s*if\s+/i, "").trim();
|
|
18
|
+
|
|
19
|
+
const normalizeRightExpression = (value: string): string =>
|
|
20
|
+
value
|
|
21
|
+
.replace(/\bthen\b.*$/i, "")
|
|
22
|
+
.replace(/[;]+$/, "")
|
|
23
|
+
.trim();
|
|
24
|
+
|
|
25
|
+
const extractMatches = (text: string): MatchPair[] => {
|
|
26
|
+
const matches: MatchPair[] = [];
|
|
27
|
+
const pattern = /([^;\n]+?)\s*~\s*([^;\n]+)/g;
|
|
28
|
+
let current = pattern.exec(text);
|
|
29
|
+
|
|
30
|
+
while (current) {
|
|
31
|
+
const left = normalizeLeftExpression(current[1] ?? "");
|
|
32
|
+
const right = normalizeRightExpression(current[2] ?? "");
|
|
33
|
+
if (left && right) {
|
|
34
|
+
matches.push({ left, right });
|
|
35
|
+
}
|
|
36
|
+
current = pattern.exec(text);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return matches;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const splitSetItems = (setText: string): string[] => {
|
|
43
|
+
const inner = setText.trim().slice(1, -1).trim();
|
|
44
|
+
if (!inner) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return inner
|
|
49
|
+
.split(",")
|
|
50
|
+
.map((item) => item.trim())
|
|
51
|
+
.filter((item) => item.length > 0);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const statementText = (statement: FilterBodyStatement): string => {
|
|
55
|
+
if (statement.kind === "expression") {
|
|
56
|
+
return statement.expressionText;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (statement.kind === "other") {
|
|
60
|
+
return statement.text;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (statement.kind === "if") {
|
|
64
|
+
return statement.conditionText ?? "";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (statement.kind === "return") {
|
|
68
|
+
return statement.valueText ?? "";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return "";
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const declarationText = (source: string, range: SourceRange): string => {
|
|
75
|
+
const lines = source.split(/\r?\n/);
|
|
76
|
+
const startLine = Math.max(1, range.line);
|
|
77
|
+
const endLine = Math.max(startLine, range.endLine);
|
|
78
|
+
return lines.slice(startLine - 1, endLine).join("\n");
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const typeNotIterableRule: BirdRule = ({ parsed }) => {
|
|
82
|
+
const diagnostics: BirdDiagnostic[] = [];
|
|
83
|
+
const seen = new Set<string>();
|
|
84
|
+
|
|
85
|
+
for (const declaration of filterAndFunctionDeclarations(parsed)) {
|
|
86
|
+
for (const statement of declaration.statements) {
|
|
87
|
+
const text = statementText(statement);
|
|
88
|
+
if (!text) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const match of extractMatches(text)) {
|
|
93
|
+
if (match.right.startsWith("[") && match.right.endsWith("]")) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
pushUniqueDiagnostic(
|
|
98
|
+
diagnostics,
|
|
99
|
+
seen,
|
|
100
|
+
createRuleDiagnostic(
|
|
101
|
+
"type/not-iterable",
|
|
102
|
+
`Type '${match.right}' is not iterable in match expression '${match.left} ~ ${match.right}'`,
|
|
103
|
+
statement,
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
for (const match of declaration.matches) {
|
|
110
|
+
const right = normalizeRightExpression(match.right);
|
|
111
|
+
if (right.startsWith("[") && right.endsWith("]")) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
pushUniqueDiagnostic(
|
|
116
|
+
diagnostics,
|
|
117
|
+
seen,
|
|
118
|
+
createRuleDiagnostic(
|
|
119
|
+
"type/not-iterable",
|
|
120
|
+
`Type '${right}' is not iterable in match expression '${match.left} ${match.operator} ${match.right}'`,
|
|
121
|
+
match,
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return diagnostics;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const typeSetIncompatibleRule: BirdRule = ({ parsed, text }) => {
|
|
131
|
+
const diagnostics: BirdDiagnostic[] = [];
|
|
132
|
+
const seen = new Set<string>();
|
|
133
|
+
|
|
134
|
+
const evaluateSetCompatibility = (
|
|
135
|
+
left: string,
|
|
136
|
+
right: string,
|
|
137
|
+
range: SourceRange,
|
|
138
|
+
): void => {
|
|
139
|
+
const normalizedRight = normalizeRightExpression(right);
|
|
140
|
+
if (!normalizedRight.startsWith("[") || !normalizedRight.endsWith("]")) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const leftType = scalarTypeOfExpression(left);
|
|
145
|
+
if (leftType === "unknown") {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const items = splitSetItems(normalizedRight);
|
|
150
|
+
if (items.length === 0) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const firstType = scalarTypeOfExpression(items[0] ?? "");
|
|
155
|
+
if (firstType === "unknown" || firstType === leftType) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
pushUniqueDiagnostic(
|
|
160
|
+
diagnostics,
|
|
161
|
+
seen,
|
|
162
|
+
createRuleDiagnostic(
|
|
163
|
+
"type/set-incompatible",
|
|
164
|
+
`Set-incompatible type (${leftType}) for match set '${normalizedRight}'`,
|
|
165
|
+
range,
|
|
166
|
+
),
|
|
167
|
+
);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
for (const declaration of filterAndFunctionDeclarations(parsed)) {
|
|
171
|
+
for (const statement of declaration.statements) {
|
|
172
|
+
const text = statementText(statement);
|
|
173
|
+
if (!text) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
for (const match of extractMatches(text)) {
|
|
178
|
+
evaluateSetCompatibility(match.left, match.right, statement);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
for (const match of declaration.matches) {
|
|
183
|
+
evaluateSetCompatibility(match.left, match.right, match);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const hasTruncatedSetMatch = declaration.matches.some(
|
|
187
|
+
(match) => match.right.includes("[") && !match.right.includes("]"),
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
if (hasTruncatedSetMatch) {
|
|
191
|
+
const sourceText = declarationText(text, declaration);
|
|
192
|
+
for (const match of extractMatches(sourceText)) {
|
|
193
|
+
evaluateSetCompatibility(match.left, match.right, declaration);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return diagnostics;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export const typeRules: BirdRule[] = [
|
|
202
|
+
typeNotIterableRule,
|
|
203
|
+
typeSetIncompatibleRule,
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
export const collectTypeRuleDiagnostics = (
|
|
207
|
+
context: Parameters<BirdRule>[0],
|
|
208
|
+
): BirdDiagnostic[] => {
|
|
209
|
+
return typeRules.flatMap((rule) => rule(context));
|
|
210
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { lintBirdConfig } from "../src/index.js";
|
|
3
|
+
|
|
4
|
+
const codesOf = async (text: string): Promise<string[]> => {
|
|
5
|
+
const result = await lintBirdConfig(text);
|
|
6
|
+
return result.diagnostics.map((item) => item.code);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
describe("@birdcc/linter bgp+ospf rules", () => {
|
|
10
|
+
it("hits bgp/missing-local-as", async () => {
|
|
11
|
+
const codes = await codesOf(`
|
|
12
|
+
protocol bgp edge {
|
|
13
|
+
neighbor 192.0.2.1 as 65002;
|
|
14
|
+
}
|
|
15
|
+
`);
|
|
16
|
+
|
|
17
|
+
expect(codes).toContain("bgp/missing-local-as");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("hits bgp/missing-neighbor", async () => {
|
|
21
|
+
const codes = await codesOf(`
|
|
22
|
+
protocol bgp edge {
|
|
23
|
+
local as 65001;
|
|
24
|
+
}
|
|
25
|
+
`);
|
|
26
|
+
|
|
27
|
+
expect(codes).toContain("bgp/missing-neighbor");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("hits bgp/missing-remote-as", async () => {
|
|
31
|
+
const codes = await codesOf(`
|
|
32
|
+
protocol bgp edge {
|
|
33
|
+
local as 65001;
|
|
34
|
+
neighbor 192.0.2.1;
|
|
35
|
+
}
|
|
36
|
+
`);
|
|
37
|
+
|
|
38
|
+
expect(codes).toContain("bgp/missing-remote-as");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("hits bgp/as-mismatch", async () => {
|
|
42
|
+
const codes = await codesOf(`
|
|
43
|
+
protocol bgp edge {
|
|
44
|
+
local as 65001;
|
|
45
|
+
neighbor 192.0.2.9 as internal;
|
|
46
|
+
neighbor 192.0.2.1 as 65002;
|
|
47
|
+
}
|
|
48
|
+
`);
|
|
49
|
+
|
|
50
|
+
expect(codes).toContain("bgp/as-mismatch");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("does not hit bgp/missing-remote-as for internal/external sessions", async () => {
|
|
54
|
+
const codes = await codesOf(`
|
|
55
|
+
protocol bgp edge {
|
|
56
|
+
local as 65001;
|
|
57
|
+
neighbor 192.0.2.1 as internal;
|
|
58
|
+
neighbor 192.0.2.2 as external;
|
|
59
|
+
}
|
|
60
|
+
`);
|
|
61
|
+
|
|
62
|
+
expect(codes).not.toContain("bgp/missing-remote-as");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("hits bgp/timer-invalid", async () => {
|
|
66
|
+
const codes = await codesOf(`
|
|
67
|
+
protocol bgp edge {
|
|
68
|
+
local as 65001;
|
|
69
|
+
neighbor 192.0.2.1 as 65001;
|
|
70
|
+
hold 2;
|
|
71
|
+
keepalive 10;
|
|
72
|
+
}
|
|
73
|
+
`);
|
|
74
|
+
|
|
75
|
+
expect(codes).toContain("bgp/timer-invalid");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("hits ospf/missing-area", async () => {
|
|
79
|
+
const codes = await codesOf(`
|
|
80
|
+
protocol ospf core {
|
|
81
|
+
}
|
|
82
|
+
`);
|
|
83
|
+
|
|
84
|
+
expect(codes).toContain("ospf/missing-area");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("hits ospf/backbone-stub", async () => {
|
|
88
|
+
const codes = await codesOf(`
|
|
89
|
+
protocol ospf core {
|
|
90
|
+
area 0 stub;
|
|
91
|
+
}
|
|
92
|
+
`);
|
|
93
|
+
|
|
94
|
+
expect(codes).toContain("ospf/backbone-stub");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("hits ospf/vlink-in-backbone", async () => {
|
|
98
|
+
const codes = await codesOf(`
|
|
99
|
+
protocol ospf core {
|
|
100
|
+
area 0 vlink 192.0.2.1;
|
|
101
|
+
}
|
|
102
|
+
`);
|
|
103
|
+
|
|
104
|
+
expect(codes).toContain("ospf/vlink-in-backbone");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("hits ospf/asbr-stub-area", async () => {
|
|
108
|
+
const codes = await codesOf(`
|
|
109
|
+
protocol ospf core {
|
|
110
|
+
area 1 {
|
|
111
|
+
stub;
|
|
112
|
+
asbr on;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
`);
|
|
116
|
+
|
|
117
|
+
expect(codes).toContain("ospf/asbr-stub-area");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("accepts area 0.0.0.0 as backbone area id", async () => {
|
|
121
|
+
const codes = await codesOf(`
|
|
122
|
+
protocol ospf core {
|
|
123
|
+
area 0.0.0.0 stub;
|
|
124
|
+
}
|
|
125
|
+
`);
|
|
126
|
+
|
|
127
|
+
expect(codes).toContain("ospf/backbone-stub");
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { lintBirdConfig } from "../src/index.js";
|
|
3
|
+
|
|
4
|
+
const LEGACY_PATTERNS = [
|
|
5
|
+
/^protocol\//,
|
|
6
|
+
/^security\//,
|
|
7
|
+
/^performance\//,
|
|
8
|
+
/^structure\//,
|
|
9
|
+
/^semantic\//,
|
|
10
|
+
/^parser\//,
|
|
11
|
+
/^syntax\//,
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
describe("@birdcc/linter migration", () => {
|
|
15
|
+
it("maps parser/core diagnostics to 32-rule taxonomy", async () => {
|
|
16
|
+
const result = await lintBirdConfig(`
|
|
17
|
+
filter dup_policy {
|
|
18
|
+
accept;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
filter dup_policy {
|
|
22
|
+
x = 1;
|
|
23
|
+
if net ~ [10.0.0.0/129] then accept;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
protocol bgp edge from missing_tpl {
|
|
27
|
+
local as 65001;
|
|
28
|
+
neighbor 192.0.2.1 as 65002;
|
|
29
|
+
}
|
|
30
|
+
`);
|
|
31
|
+
|
|
32
|
+
const codes = result.diagnostics.map((item) => item.code);
|
|
33
|
+
|
|
34
|
+
expect(codes).toContain("sym/duplicate");
|
|
35
|
+
expect(codes).toContain("sym/undefined");
|
|
36
|
+
expect(codes).toContain("sym/variable-scope");
|
|
37
|
+
expect(codes).toContain("net/invalid-prefix-length");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("maps parser recovery issues to cfg/syntax-error", async () => {
|
|
41
|
+
const result = await lintBirdConfig(`
|
|
42
|
+
protocol bgp edge {
|
|
43
|
+
local as 65001
|
|
44
|
+
`);
|
|
45
|
+
|
|
46
|
+
const codes = result.diagnostics.map((item) => item.code);
|
|
47
|
+
expect(codes).toContain("cfg/syntax-error");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("does not output legacy code categories", async () => {
|
|
51
|
+
const result = await lintBirdConfig(`
|
|
52
|
+
protocol bgp edge {
|
|
53
|
+
local as 65001;
|
|
54
|
+
neighbor 192.0.2.1 as 65002;
|
|
55
|
+
import filter missing_policy;
|
|
56
|
+
ipv4 {
|
|
57
|
+
max prefix 128;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
`);
|
|
61
|
+
|
|
62
|
+
for (const code of result.diagnostics.map((item) => item.code)) {
|
|
63
|
+
expect(LEGACY_PATTERNS.some((pattern) => pattern.test(code))).toBe(false);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { lintBirdConfig } from "../src/index.js";
|
|
3
|
+
|
|
4
|
+
const codesOf = async (text: string): Promise<string[]> => {
|
|
5
|
+
const result = await lintBirdConfig(text);
|
|
6
|
+
return result.diagnostics.map((item) => item.code);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
describe("@birdcc/linter net+type rules", () => {
|
|
10
|
+
it("hits net/invalid-prefix-length", async () => {
|
|
11
|
+
const codes = await codesOf(`
|
|
12
|
+
filter f1 {
|
|
13
|
+
if net ~ [10.0.0.0/129] then accept;
|
|
14
|
+
}
|
|
15
|
+
`);
|
|
16
|
+
|
|
17
|
+
expect(codes).toContain("net/invalid-prefix-length");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("hits net/invalid-ipv4-prefix", async () => {
|
|
21
|
+
const codes = await codesOf(`
|
|
22
|
+
filter f1 {
|
|
23
|
+
if net ~ [10.0.500.0/24] then accept;
|
|
24
|
+
}
|
|
25
|
+
`);
|
|
26
|
+
|
|
27
|
+
expect(codes).toContain("net/invalid-ipv4-prefix");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("hits net/invalid-ipv6-prefix", async () => {
|
|
31
|
+
const codes = await codesOf(`
|
|
32
|
+
filter f1 {
|
|
33
|
+
if net ~ [2001:db8::zz/64] then accept;
|
|
34
|
+
}
|
|
35
|
+
`);
|
|
36
|
+
|
|
37
|
+
expect(codes).toContain("net/invalid-ipv6-prefix");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("hits net/invalid-ipv4-prefix for unknown address token", async () => {
|
|
41
|
+
const codes = await codesOf(`
|
|
42
|
+
filter f1 {
|
|
43
|
+
if net ~ [example/24] then accept;
|
|
44
|
+
}
|
|
45
|
+
`);
|
|
46
|
+
|
|
47
|
+
expect(codes).toContain("net/invalid-ipv4-prefix");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("hits net/max-prefix-length", async () => {
|
|
51
|
+
const codes = await codesOf(`
|
|
52
|
+
filter f1 {
|
|
53
|
+
if net ~ [10.0.0.0/33] then accept;
|
|
54
|
+
}
|
|
55
|
+
`);
|
|
56
|
+
|
|
57
|
+
expect(codes).toContain("net/max-prefix-length");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("hits net/invalid-prefix-length for negative max prefix", async () => {
|
|
61
|
+
const codes = await codesOf(`
|
|
62
|
+
protocol bgp edge {
|
|
63
|
+
local as 65001;
|
|
64
|
+
neighbor 192.0.2.1 as 65002;
|
|
65
|
+
ipv4 {
|
|
66
|
+
max prefix -100;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
`);
|
|
70
|
+
|
|
71
|
+
expect(codes).toContain("net/invalid-prefix-length");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("hits net/max-prefix-length for ipv6 channel max prefix overflow", async () => {
|
|
75
|
+
const codes = await codesOf(`
|
|
76
|
+
protocol bgp edge {
|
|
77
|
+
local as 65001;
|
|
78
|
+
neighbor 2001:db8::1 as 65002;
|
|
79
|
+
ipv6 {
|
|
80
|
+
max prefix 129;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
`);
|
|
84
|
+
|
|
85
|
+
expect(codes).toContain("net/max-prefix-length");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("hits net/invalid-prefix-length for non-numeric max prefix token", async () => {
|
|
89
|
+
const codes = await codesOf(`
|
|
90
|
+
protocol bgp edge {
|
|
91
|
+
local as 65001;
|
|
92
|
+
neighbor 192.0.2.1 as 65002;
|
|
93
|
+
ipv4 {
|
|
94
|
+
max prefix unlimited;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
`);
|
|
98
|
+
|
|
99
|
+
expect(codes).toContain("net/invalid-prefix-length");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("hits type/mismatch", async () => {
|
|
103
|
+
const codes = await codesOf(`
|
|
104
|
+
filter f1 {
|
|
105
|
+
int x = "hello";
|
|
106
|
+
accept;
|
|
107
|
+
}
|
|
108
|
+
`);
|
|
109
|
+
|
|
110
|
+
expect(codes).toContain("type/mismatch");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("hits type/not-iterable", async () => {
|
|
114
|
+
const codes = await codesOf(`
|
|
115
|
+
filter f1 {
|
|
116
|
+
if net ~ 1 then accept;
|
|
117
|
+
}
|
|
118
|
+
`);
|
|
119
|
+
|
|
120
|
+
expect(codes).toContain("type/not-iterable");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("hits type/set-incompatible", async () => {
|
|
124
|
+
const codes = await codesOf(`
|
|
125
|
+
filter f1 {
|
|
126
|
+
if 10.0.0.1 ~ [1, 2, 3] then accept;
|
|
127
|
+
}
|
|
128
|
+
`);
|
|
129
|
+
|
|
130
|
+
expect(codes).toContain("type/set-incompatible");
|
|
131
|
+
});
|
|
132
|
+
});
|