@birdcc/linter 0.0.1-alpha.0 → 0.0.1-alpha.1
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/package.json +12 -5
- package/.oxfmtrc.json +0 -16
- package/src/index.ts +0 -155
- package/src/rules/bgp.ts +0 -239
- package/src/rules/catalog.ts +0 -80
- package/src/rules/cfg.ts +0 -562
- package/src/rules/net.ts +0 -262
- package/src/rules/normalize.ts +0 -90
- package/src/rules/ospf.ts +0 -221
- package/src/rules/shared.ts +0 -354
- package/src/rules/sym.ts +0 -342
- package/src/rules/type.ts +0 -210
- package/test/linter.bgp-ospf.test.ts +0 -129
- package/test/linter.migration.test.ts +0 -66
- package/test/linter.net-type.test.ts +0 -132
- package/test/linter.sym-cfg.test.ts +0 -224
- package/test/linter.test.ts +0 -21
- package/tsconfig.json +0 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@birdcc/linter",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0-only",
|
|
@@ -9,17 +9,24 @@
|
|
|
9
9
|
"email": "npm-dev@birdcc.link",
|
|
10
10
|
"url": "https://github.com/bird-chinese-community/"
|
|
11
11
|
},
|
|
12
|
+
"description": "Lint rule engine for BIRD2 configuration files.",
|
|
12
13
|
"main": "./dist/index.js",
|
|
13
|
-
"types": "./
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
14
15
|
"exports": {
|
|
15
16
|
".": {
|
|
16
|
-
"types": "./
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
17
18
|
"default": "./dist/index.js"
|
|
18
19
|
}
|
|
19
20
|
},
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"files": [
|
|
23
|
+
"dist/**",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
20
27
|
"dependencies": {
|
|
21
|
-
"@birdcc/core": "0.0.1-alpha.
|
|
22
|
-
"@birdcc/parser": "0.0.1-alpha.
|
|
28
|
+
"@birdcc/core": "0.0.1-alpha.1",
|
|
29
|
+
"@birdcc/parser": "0.0.1-alpha.1"
|
|
23
30
|
},
|
|
24
31
|
"repository": {
|
|
25
32
|
"type": "git",
|
package/.oxfmtrc.json
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "../../../node_modules/oxfmt/configuration_schema.json",
|
|
3
|
-
"ignorePatterns": [
|
|
4
|
-
"node_modules/*",
|
|
5
|
-
"package.json",
|
|
6
|
-
"dist/*",
|
|
7
|
-
".turbo/*",
|
|
8
|
-
"target/*",
|
|
9
|
-
"*.lock",
|
|
10
|
-
"pnpm-*.yaml",
|
|
11
|
-
"*-lock.json"
|
|
12
|
-
],
|
|
13
|
-
"printWidth": 80,
|
|
14
|
-
"tabWidth": 2,
|
|
15
|
-
"useTabs": false
|
|
16
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import type { CrossFileResolutionResult, SymbolTable } from "@birdcc/core";
|
|
2
|
-
import {
|
|
3
|
-
buildCoreSnapshotFromParsed,
|
|
4
|
-
type BirdDiagnostic,
|
|
5
|
-
type CoreSnapshot,
|
|
6
|
-
} from "@birdcc/core";
|
|
7
|
-
import { parseBirdConfig, type ParsedBirdDocument } from "@birdcc/parser";
|
|
8
|
-
import { collectBgpRuleDiagnostics } from "./rules/bgp.js";
|
|
9
|
-
import { collectCfgRuleDiagnostics } from "./rules/cfg.js";
|
|
10
|
-
import { collectNetRuleDiagnostics } from "./rules/net.js";
|
|
11
|
-
import { normalizeBaseDiagnostics } from "./rules/normalize.js";
|
|
12
|
-
import { collectOspfRuleDiagnostics } from "./rules/ospf.js";
|
|
13
|
-
import { collectSymRuleDiagnostics } from "./rules/sym.js";
|
|
14
|
-
import { type RuleContext } from "./rules/shared.js";
|
|
15
|
-
import { collectTypeRuleDiagnostics } from "./rules/type.js";
|
|
16
|
-
|
|
17
|
-
export interface LintResult {
|
|
18
|
-
parsed: ParsedBirdDocument;
|
|
19
|
-
core: CoreSnapshot;
|
|
20
|
-
diagnostics: BirdDiagnostic[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const dedupeDiagnostics = (diagnostics: BirdDiagnostic[]): BirdDiagnostic[] => {
|
|
24
|
-
const seen = new Set<string>();
|
|
25
|
-
const output: BirdDiagnostic[] = [];
|
|
26
|
-
|
|
27
|
-
for (const diagnostic of diagnostics) {
|
|
28
|
-
const key = [
|
|
29
|
-
diagnostic.code,
|
|
30
|
-
diagnostic.message,
|
|
31
|
-
diagnostic.uri ?? "",
|
|
32
|
-
diagnostic.range.line,
|
|
33
|
-
diagnostic.range.column,
|
|
34
|
-
diagnostic.range.endLine,
|
|
35
|
-
diagnostic.range.endColumn,
|
|
36
|
-
].join(":");
|
|
37
|
-
|
|
38
|
-
if (seen.has(key)) {
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
seen.add(key);
|
|
43
|
-
output.push(diagnostic);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return output;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const diagnosticsForUri = (
|
|
50
|
-
diagnostics: BirdDiagnostic[],
|
|
51
|
-
uri: string,
|
|
52
|
-
entryUri: string,
|
|
53
|
-
): BirdDiagnostic[] => {
|
|
54
|
-
return diagnostics.filter(
|
|
55
|
-
(diagnostic) => (diagnostic.uri ?? entryUri) === uri,
|
|
56
|
-
);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const createMergedCoreSnapshot = (
|
|
60
|
-
localCore: CoreSnapshot,
|
|
61
|
-
mergedSymbolTable: SymbolTable,
|
|
62
|
-
scopedDiagnostics: BirdDiagnostic[],
|
|
63
|
-
): CoreSnapshot => ({
|
|
64
|
-
...localCore,
|
|
65
|
-
symbols: mergedSymbolTable.definitions,
|
|
66
|
-
references: mergedSymbolTable.references,
|
|
67
|
-
symbolTable: mergedSymbolTable,
|
|
68
|
-
diagnostics: scopedDiagnostics,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
export interface LintBirdConfigOptions {
|
|
72
|
-
parsed?: ParsedBirdDocument;
|
|
73
|
-
core?: CoreSnapshot;
|
|
74
|
-
uri?: string;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface CrossFileLintResult {
|
|
78
|
-
diagnostics: BirdDiagnostic[];
|
|
79
|
-
byUri: Record<string, LintResult>;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** Runs parser + core + normalized 32-rule linter pipeline and returns merged diagnostics. */
|
|
83
|
-
export const lintBirdConfig = async (
|
|
84
|
-
text: string,
|
|
85
|
-
options: LintBirdConfigOptions = {},
|
|
86
|
-
): Promise<LintResult> => {
|
|
87
|
-
const parsed = options.parsed ?? (await parseBirdConfig(text));
|
|
88
|
-
const core =
|
|
89
|
-
options.core ?? buildCoreSnapshotFromParsed(parsed, { uri: options.uri });
|
|
90
|
-
|
|
91
|
-
const context: RuleContext = { text, parsed, core };
|
|
92
|
-
|
|
93
|
-
const normalizedBaseDiagnostics = normalizeBaseDiagnostics(
|
|
94
|
-
parsed,
|
|
95
|
-
core.diagnostics,
|
|
96
|
-
{
|
|
97
|
-
uri: options.uri,
|
|
98
|
-
},
|
|
99
|
-
);
|
|
100
|
-
const ruleDiagnostics: BirdDiagnostic[] = [
|
|
101
|
-
...collectSymRuleDiagnostics(context),
|
|
102
|
-
...collectCfgRuleDiagnostics(context),
|
|
103
|
-
...collectNetRuleDiagnostics(context),
|
|
104
|
-
...collectTypeRuleDiagnostics(context),
|
|
105
|
-
...collectBgpRuleDiagnostics(context),
|
|
106
|
-
...collectOspfRuleDiagnostics(context),
|
|
107
|
-
].map((diagnostic) => ({
|
|
108
|
-
...diagnostic,
|
|
109
|
-
uri: diagnostic.uri ?? options.uri,
|
|
110
|
-
}));
|
|
111
|
-
|
|
112
|
-
return {
|
|
113
|
-
parsed,
|
|
114
|
-
core,
|
|
115
|
-
diagnostics: dedupeDiagnostics([
|
|
116
|
-
...normalizedBaseDiagnostics,
|
|
117
|
-
...ruleDiagnostics,
|
|
118
|
-
]),
|
|
119
|
-
};
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
export const lintResolvedCrossFileGraph = async (
|
|
123
|
-
resolution: CrossFileResolutionResult,
|
|
124
|
-
): Promise<CrossFileLintResult> => {
|
|
125
|
-
const byUri: Record<string, LintResult> = {};
|
|
126
|
-
const diagnostics: BirdDiagnostic[] = [];
|
|
127
|
-
const uris =
|
|
128
|
-
resolution.visitedUris.length > 0
|
|
129
|
-
? resolution.visitedUris
|
|
130
|
-
: [resolution.entryUri];
|
|
131
|
-
|
|
132
|
-
for (const uri of uris) {
|
|
133
|
-
const text = resolution.documents[uri];
|
|
134
|
-
const localCore = resolution.snapshots[uri];
|
|
135
|
-
if (!text || !localCore) {
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const lintResult = await lintBirdConfig(text, {
|
|
140
|
-
uri,
|
|
141
|
-
core: createMergedCoreSnapshot(
|
|
142
|
-
localCore,
|
|
143
|
-
resolution.symbolTable,
|
|
144
|
-
diagnosticsForUri(resolution.diagnostics, uri, resolution.entryUri),
|
|
145
|
-
),
|
|
146
|
-
});
|
|
147
|
-
byUri[uri] = lintResult;
|
|
148
|
-
diagnostics.push(...lintResult.diagnostics);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
byUri,
|
|
153
|
-
diagnostics: dedupeDiagnostics(diagnostics),
|
|
154
|
-
};
|
|
155
|
-
};
|
package/src/rules/bgp.ts
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import type { BirdDiagnostic } from "@birdcc/core";
|
|
2
|
-
import {
|
|
3
|
-
createProtocolDiagnostic,
|
|
4
|
-
createRuleDiagnostic,
|
|
5
|
-
extractFirstNumberAfterKeyword,
|
|
6
|
-
isProtocolType,
|
|
7
|
-
numericValue,
|
|
8
|
-
protocolDeclarations,
|
|
9
|
-
type BirdRule,
|
|
10
|
-
} from "./shared.js";
|
|
11
|
-
|
|
12
|
-
const isInternalSession = (value: string | undefined): boolean =>
|
|
13
|
-
/^(internal|ibgp)$/i.test(value?.trim() ?? "");
|
|
14
|
-
|
|
15
|
-
const isExternalSession = (value: string | undefined): boolean =>
|
|
16
|
-
/^(external|ebgp)$/i.test(value?.trim() ?? "");
|
|
17
|
-
|
|
18
|
-
const bgpMissingLocalAsRule: BirdRule = ({ parsed }) => {
|
|
19
|
-
const diagnostics: BirdDiagnostic[] = [];
|
|
20
|
-
|
|
21
|
-
for (const declaration of protocolDeclarations(parsed)) {
|
|
22
|
-
if (!isProtocolType(declaration, "bgp")) {
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (
|
|
27
|
-
declaration.statements.some((statement) => statement.kind === "local-as")
|
|
28
|
-
) {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
diagnostics.push(
|
|
33
|
-
createProtocolDiagnostic(
|
|
34
|
-
"bgp/missing-local-as",
|
|
35
|
-
`BGP protocol '${declaration.name}' missing local AS number`,
|
|
36
|
-
declaration,
|
|
37
|
-
),
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return diagnostics;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const bgpMissingNeighborRule: BirdRule = ({ parsed }) => {
|
|
45
|
-
const diagnostics: BirdDiagnostic[] = [];
|
|
46
|
-
|
|
47
|
-
for (const declaration of protocolDeclarations(parsed)) {
|
|
48
|
-
if (!isProtocolType(declaration, "bgp")) {
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
declaration.statements.some((statement) => statement.kind === "neighbor")
|
|
54
|
-
) {
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
diagnostics.push(
|
|
59
|
-
createProtocolDiagnostic(
|
|
60
|
-
"bgp/missing-neighbor",
|
|
61
|
-
`BGP protocol '${declaration.name}' missing neighbor configuration`,
|
|
62
|
-
declaration,
|
|
63
|
-
),
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return diagnostics;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const bgpMissingRemoteAsRule: BirdRule = ({ parsed }) => {
|
|
71
|
-
const diagnostics: BirdDiagnostic[] = [];
|
|
72
|
-
|
|
73
|
-
for (const declaration of protocolDeclarations(parsed)) {
|
|
74
|
-
if (!isProtocolType(declaration, "bgp")) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
for (const statement of declaration.statements) {
|
|
79
|
-
if (statement.kind !== "neighbor") {
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
isInternalSession(statement.asn) ||
|
|
85
|
-
isExternalSession(statement.asn)
|
|
86
|
-
) {
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (statement.asn && numericValue(statement.asn) !== null) {
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
diagnostics.push(
|
|
95
|
-
createRuleDiagnostic(
|
|
96
|
-
"bgp/missing-remote-as",
|
|
97
|
-
`BGP protocol '${declaration.name}' neighbor '${statement.address}' missing remote AS`,
|
|
98
|
-
statement.asnRange ?? statement.addressRange,
|
|
99
|
-
),
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return diagnostics;
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const bgpAsMismatchRule: BirdRule = ({ parsed }) => {
|
|
108
|
-
const diagnostics: BirdDiagnostic[] = [];
|
|
109
|
-
|
|
110
|
-
for (const declaration of protocolDeclarations(parsed)) {
|
|
111
|
-
if (!isProtocolType(declaration, "bgp")) {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const localAsStatement = declaration.statements.find(
|
|
116
|
-
(statement) => statement.kind === "local-as",
|
|
117
|
-
);
|
|
118
|
-
if (!localAsStatement || localAsStatement.kind !== "local-as") {
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const localAs = numericValue(localAsStatement.asn);
|
|
123
|
-
if (localAs === null) {
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const hasInternalNeighbor = declaration.statements.some(
|
|
128
|
-
(statement) =>
|
|
129
|
-
statement.kind === "neighbor" && isInternalSession(statement.asn),
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
if (!hasInternalNeighbor) {
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
for (const statement of declaration.statements) {
|
|
137
|
-
if (statement.kind !== "neighbor" || !statement.asn) {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (
|
|
142
|
-
isInternalSession(statement.asn) ||
|
|
143
|
-
isExternalSession(statement.asn)
|
|
144
|
-
) {
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const remoteAs = numericValue(statement.asn);
|
|
149
|
-
if (remoteAs === null || remoteAs === localAs) {
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
diagnostics.push(
|
|
154
|
-
createRuleDiagnostic(
|
|
155
|
-
"bgp/as-mismatch",
|
|
156
|
-
`BGP protocol '${declaration.name}' internal session requires same ASN (local ${localAs}, remote ${remoteAs})`,
|
|
157
|
-
statement.asnRange ?? statement.addressRange,
|
|
158
|
-
),
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return diagnostics;
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
const bgpTimerInvalidRule: BirdRule = ({ parsed }) => {
|
|
167
|
-
const diagnostics: BirdDiagnostic[] = [];
|
|
168
|
-
|
|
169
|
-
for (const declaration of protocolDeclarations(parsed)) {
|
|
170
|
-
if (!isProtocolType(declaration, "bgp")) {
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
let hold: number | null = null;
|
|
175
|
-
let keepalive: number | null = null;
|
|
176
|
-
|
|
177
|
-
for (const statement of declaration.statements) {
|
|
178
|
-
if (statement.kind !== "other") {
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const text = statement.text.toLowerCase();
|
|
183
|
-
const holdValue = extractFirstNumberAfterKeyword(text, "hold");
|
|
184
|
-
const keepaliveValue = extractFirstNumberAfterKeyword(text, "keepalive");
|
|
185
|
-
|
|
186
|
-
if (holdValue !== null) {
|
|
187
|
-
hold = holdValue;
|
|
188
|
-
if (hold < 3 || hold > 65_535) {
|
|
189
|
-
diagnostics.push(
|
|
190
|
-
createRuleDiagnostic(
|
|
191
|
-
"bgp/timer-invalid",
|
|
192
|
-
`BGP protocol '${declaration.name}' hold time must be in range 3..65535`,
|
|
193
|
-
statement,
|
|
194
|
-
),
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (keepaliveValue !== null) {
|
|
200
|
-
keepalive = keepaliveValue;
|
|
201
|
-
if (keepalive < 1 || keepalive > 65_535) {
|
|
202
|
-
diagnostics.push(
|
|
203
|
-
createRuleDiagnostic(
|
|
204
|
-
"bgp/timer-invalid",
|
|
205
|
-
`BGP protocol '${declaration.name}' keepalive must be in range 1..65535`,
|
|
206
|
-
statement,
|
|
207
|
-
),
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (hold !== null && keepalive !== null && keepalive >= hold) {
|
|
214
|
-
diagnostics.push(
|
|
215
|
-
createProtocolDiagnostic(
|
|
216
|
-
"bgp/timer-invalid",
|
|
217
|
-
`BGP protocol '${declaration.name}' keepalive (${keepalive}) must be smaller than hold (${hold})`,
|
|
218
|
-
declaration,
|
|
219
|
-
),
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return diagnostics;
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
export const bgpRules: BirdRule[] = [
|
|
228
|
-
bgpMissingLocalAsRule,
|
|
229
|
-
bgpMissingNeighborRule,
|
|
230
|
-
bgpMissingRemoteAsRule,
|
|
231
|
-
bgpAsMismatchRule,
|
|
232
|
-
bgpTimerInvalidRule,
|
|
233
|
-
];
|
|
234
|
-
|
|
235
|
-
export const collectBgpRuleDiagnostics = (
|
|
236
|
-
context: Parameters<BirdRule>[0],
|
|
237
|
-
): BirdDiagnostic[] => {
|
|
238
|
-
return bgpRules.flatMap((rule) => rule(context));
|
|
239
|
-
};
|
package/src/rules/catalog.ts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import type { BirdDiagnosticSeverity } from "@birdcc/core";
|
|
2
|
-
|
|
3
|
-
export const RULE_CODES = {
|
|
4
|
-
sym: [
|
|
5
|
-
"sym/undefined",
|
|
6
|
-
"sym/duplicate",
|
|
7
|
-
"sym/proto-type-mismatch",
|
|
8
|
-
"sym/filter-required",
|
|
9
|
-
"sym/function-required",
|
|
10
|
-
"sym/table-required",
|
|
11
|
-
"sym/variable-scope",
|
|
12
|
-
],
|
|
13
|
-
cfg: [
|
|
14
|
-
"cfg/no-protocol",
|
|
15
|
-
"cfg/missing-router-id",
|
|
16
|
-
"cfg/syntax-error",
|
|
17
|
-
"cfg/value-out-of-range",
|
|
18
|
-
"cfg/switch-value-expected",
|
|
19
|
-
"cfg/number-expected",
|
|
20
|
-
"cfg/incompatible-type",
|
|
21
|
-
"cfg/ip-network-mismatch",
|
|
22
|
-
"cfg/circular-template",
|
|
23
|
-
],
|
|
24
|
-
net: [
|
|
25
|
-
"net/invalid-prefix-length",
|
|
26
|
-
"net/invalid-ipv4-prefix",
|
|
27
|
-
"net/invalid-ipv6-prefix",
|
|
28
|
-
"net/max-prefix-length",
|
|
29
|
-
],
|
|
30
|
-
type: ["type/mismatch", "type/not-iterable", "type/set-incompatible"],
|
|
31
|
-
bgp: [
|
|
32
|
-
"bgp/missing-local-as",
|
|
33
|
-
"bgp/missing-neighbor",
|
|
34
|
-
"bgp/missing-remote-as",
|
|
35
|
-
"bgp/as-mismatch",
|
|
36
|
-
"bgp/timer-invalid",
|
|
37
|
-
],
|
|
38
|
-
ospf: [
|
|
39
|
-
"ospf/missing-area",
|
|
40
|
-
"ospf/backbone-stub",
|
|
41
|
-
"ospf/vlink-in-backbone",
|
|
42
|
-
"ospf/asbr-stub-area",
|
|
43
|
-
],
|
|
44
|
-
} as const;
|
|
45
|
-
|
|
46
|
-
export type RuleCode =
|
|
47
|
-
| (typeof RULE_CODES.sym)[number]
|
|
48
|
-
| (typeof RULE_CODES.cfg)[number]
|
|
49
|
-
| (typeof RULE_CODES.net)[number]
|
|
50
|
-
| (typeof RULE_CODES.type)[number]
|
|
51
|
-
| (typeof RULE_CODES.bgp)[number]
|
|
52
|
-
| (typeof RULE_CODES.ospf)[number];
|
|
53
|
-
|
|
54
|
-
const ruleSeverityEntries: ReadonlyArray<
|
|
55
|
-
readonly [RuleCode, BirdDiagnosticSeverity]
|
|
56
|
-
> = [
|
|
57
|
-
...RULE_CODES.sym.map((code) => [code, "error"] as const),
|
|
58
|
-
...RULE_CODES.cfg.map((code) => [code, "error"] as const),
|
|
59
|
-
...RULE_CODES.net.map((code) => [code, "error"] as const),
|
|
60
|
-
...RULE_CODES.type.map((code) => [code, "error"] as const),
|
|
61
|
-
...RULE_CODES.bgp.map((code) => [code, "warning"] as const),
|
|
62
|
-
...RULE_CODES.ospf.map((code) => [code, "warning"] as const),
|
|
63
|
-
];
|
|
64
|
-
|
|
65
|
-
export const RULE_SEVERITY: Record<RuleCode, BirdDiagnosticSeverity> =
|
|
66
|
-
Object.fromEntries(ruleSeverityEntries) as Record<
|
|
67
|
-
RuleCode,
|
|
68
|
-
BirdDiagnosticSeverity
|
|
69
|
-
>;
|
|
70
|
-
|
|
71
|
-
export const LEGACY_CODE_PATTERNS = [
|
|
72
|
-
/^protocol\//,
|
|
73
|
-
/^security\//,
|
|
74
|
-
/^performance\//,
|
|
75
|
-
/^structure\//,
|
|
76
|
-
] as const;
|
|
77
|
-
|
|
78
|
-
export const isRuleCode = (code: string): code is RuleCode => {
|
|
79
|
-
return code in RULE_SEVERITY;
|
|
80
|
-
};
|