@intentius/chant-lexicon-prometheus 0.124.30 → 0.124.32
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/dist/alertmanager.js +61 -0
- package/dist/api/alertmanager.js +98 -0
- package/dist/api/apply.js +306 -0
- package/dist/api/bind.js +66 -0
- package/dist/api/client.js +110 -0
- package/dist/api/evaluated.js +86 -0
- package/dist/api/fake-servers.js +165 -0
- package/dist/api/index.js +17 -0
- package/dist/api/ruler.js +122 -0
- package/dist/build.js +240 -0
- package/dist/catalog.js +66 -0
- package/dist/codegen/docs-cli.js +3 -0
- package/dist/codegen/docs.js +105 -0
- package/dist/codegen/generate-cli.js +7 -0
- package/dist/codegen/generate.js +42 -0
- package/dist/codegen/package.js +28 -0
- package/dist/collector-metrics.js +225 -0
- package/dist/composites/alert-routing.js +210 -0
- package/dist/composites/catalog.js +344 -0
- package/dist/composites/genai.js +454 -0
- package/dist/composites/index.js +8 -0
- package/dist/composites/red-alerts.js +135 -0
- package/dist/composites/slo.js +353 -0
- package/dist/composites/watchdog.js +110 -0
- package/dist/config-model.js +52 -0
- package/dist/config.js +225 -0
- package/dist/describe-resources.js +299 -0
- package/dist/detect.js +12 -0
- package/dist/duration.js +54 -0
- package/dist/export-resources.js +163 -0
- package/dist/import/alertmanager-live.js +309 -0
- package/dist/import/embedded.js +100 -0
- package/dist/import/generator.js +683 -0
- package/dist/import/parser.js +428 -0
- package/dist/import/slo.js +207 -0
- package/dist/index.js +33 -0
- package/dist/init-templates.js +119 -0
- package/dist/integrations.js +33 -0
- package/dist/integrity.json +2 -2
- package/dist/lint/audit-catalog.js +60 -0
- package/dist/lint/audit-lineage.js +42 -0
- package/dist/lint/post-synth/index.js +68 -0
- package/dist/lint/post-synth/prom-helpers.js +157 -0
- package/dist/lint/post-synth/prom101.js +13 -0
- package/dist/lint/post-synth/prom102.js +13 -0
- package/dist/lint/post-synth/prom103.js +13 -0
- package/dist/lint/post-synth/prom104.js +13 -0
- package/dist/lint/post-synth/prom105.js +13 -0
- package/dist/lint/post-synth/prom106.js +13 -0
- package/dist/lint/post-synth/prom107.js +13 -0
- package/dist/lint/post-synth/prom201.js +13 -0
- package/dist/lint/post-synth/prom202.js +13 -0
- package/dist/lint/post-synth/prom203.js +13 -0
- package/dist/lint/post-synth/prom204.js +13 -0
- package/dist/lint/post-synth/prom205.js +13 -0
- package/dist/lint/post-synth/prom206.js +13 -0
- package/dist/lint/post-synth/prom207.js +13 -0
- package/dist/lint/post-synth/prom208.js +13 -0
- package/dist/lint/post-synth/prom209.js +13 -0
- package/dist/lint/post-synth/prom210.js +13 -0
- package/dist/lint/post-synth/prom211.js +17 -0
- package/dist/lint/post-synth/prom212.js +14 -0
- package/dist/lint/post-synth/prom213.js +16 -0
- package/dist/lint/post-synth/prom214.js +14 -0
- package/dist/lint/post-synth/prom215.js +14 -0
- package/dist/lint/post-synth/prom216.js +15 -0
- package/dist/lint/post-synth/prom217.js +14 -0
- package/dist/lint/post-synth/prom218.js +15 -0
- package/dist/lint/post-synth/prom219.js +14 -0
- package/dist/lint/post-synth/prom220.js +14 -0
- package/dist/lint/post-synth/prom221.js +15 -0
- package/dist/lint/post-synth/prom222.js +14 -0
- package/dist/lint/post-synth/prom223.js +15 -0
- package/dist/lint/post-synth/prom224.js +14 -0
- package/dist/lint/post-synth/prom225.js +15 -0
- package/dist/lint/post-synth/prom301.js +19 -0
- package/dist/lint/rules/index.js +8 -0
- package/dist/lint/rules/literal-credential.js +127 -0
- package/dist/lint/rules/prom-ast.js +65 -0
- package/dist/lint/rules/promql-literal.js +65 -0
- package/dist/lint/rules/slo-literal.js +79 -0
- package/dist/lsp/completions.js +11 -0
- package/dist/lsp/hover.js +21 -0
- package/dist/manifest.json +1 -1
- package/dist/matchers.js +131 -0
- package/dist/model.js +108 -0
- package/dist/op/activities/index.js +20 -0
- package/dist/op/activities/promtool.js +87 -0
- package/dist/op/activities/rule-audit.js +128 -0
- package/dist/op/activities/rules-loaded.js +73 -0
- package/dist/op/activities/silence.js +123 -0
- package/dist/op/activity-contracts.js +51 -0
- package/dist/op/audit-op.js +28 -0
- package/dist/op/builders.js +57 -0
- package/dist/op/slo-rule-tests.js +148 -0
- package/dist/package-cli.js +14 -0
- package/dist/pin.js +13 -0
- package/dist/plugin.js +143 -0
- package/dist/prometheus-config.js +38 -0
- package/dist/promql-analysis.js +343 -0
- package/dist/promql.js +44 -0
- package/dist/rule-eval.js +466 -0
- package/dist/rules.js +97 -0
- package/dist/serializer.js +59 -0
- package/dist/skill-defs.js +41 -0
- package/dist/tools.js +85 -0
- package/dist/validate-cli.js +7 -0
- package/dist/validate-config.js +556 -0
- package/dist/validate-integrations.js +388 -0
- package/dist/validate-relabel.js +52 -0
- package/dist/validate-security.js +135 -0
- package/dist/validate.js +69 -0
- package/package.json +10 -10
- package/src/codegen/package.ts +2 -2
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM204: A route names a time interval that is not declared
|
|
3
|
+
*
|
|
4
|
+
* mute_time_intervals and active_time_intervals must name entries under time_intervals.
|
|
5
|
+
*/
|
|
6
|
+
import { alertmanagerDiagnostics } from "./prom-helpers.js";
|
|
7
|
+
export const prom204 = {
|
|
8
|
+
id: "PROM204",
|
|
9
|
+
description: "A route names a time interval that is not declared",
|
|
10
|
+
check(ctx) {
|
|
11
|
+
return alertmanagerDiagnostics(ctx, "PROM204");
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM205: The root route is missing, has no receiver, or has matchers
|
|
3
|
+
*
|
|
4
|
+
* The root route is the default for every alert: it must exist, name a receiver and match everything.
|
|
5
|
+
*/
|
|
6
|
+
import { alertmanagerDiagnostics } from "./prom-helpers.js";
|
|
7
|
+
export const prom205 = {
|
|
8
|
+
id: "PROM205",
|
|
9
|
+
description: "The root route is missing, has no receiver, or has matchers",
|
|
10
|
+
check(ctx) {
|
|
11
|
+
return alertmanagerDiagnostics(ctx, "PROM205");
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM206: A route or inhibit rule matcher does not parse
|
|
3
|
+
*
|
|
4
|
+
* Matchers are label, operator (=, !=, =~, !~) and value, e.g. severity="page".
|
|
5
|
+
*/
|
|
6
|
+
import { alertmanagerDiagnostics } from "./prom-helpers.js";
|
|
7
|
+
export const prom206 = {
|
|
8
|
+
id: "PROM206",
|
|
9
|
+
description: "A route or inhibit rule matcher does not parse",
|
|
10
|
+
check(ctx) {
|
|
11
|
+
return alertmanagerDiagnostics(ctx, "PROM206");
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM207: A receiver is declared but no route sends to it
|
|
3
|
+
*
|
|
4
|
+
* An unused receiver usually means a route names the wrong one.
|
|
5
|
+
*/
|
|
6
|
+
import { alertmanagerDiagnostics } from "./prom-helpers.js";
|
|
7
|
+
export const prom207 = {
|
|
8
|
+
id: "PROM207",
|
|
9
|
+
description: "A receiver is declared but no route sends to it",
|
|
10
|
+
check(ctx) {
|
|
11
|
+
return alertmanagerDiagnostics(ctx, "PROM207");
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM208: An Alertmanager duration is not a duration
|
|
3
|
+
*
|
|
4
|
+
* group_wait, group_interval, repeat_interval, resolve_timeout and Jira reopen_duration take Prometheus durations (30s, 4h, 1d); the timeout of webhook, Slack, PagerDuty and incident.io, and Pushover retry, expire and ttl take Go durations (10s, 1m30s, 500ms).
|
|
5
|
+
*/
|
|
6
|
+
import { alertmanagerDiagnostics } from "./prom-helpers.js";
|
|
7
|
+
export const prom208 = {
|
|
8
|
+
id: "PROM208",
|
|
9
|
+
description: "An Alertmanager duration is not a duration",
|
|
10
|
+
check(ctx) {
|
|
11
|
+
return alertmanagerDiagnostics(ctx, "PROM208");
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM209: A receiver integration is missing its destination or credential
|
|
3
|
+
*
|
|
4
|
+
* Every one of Alertmanager's 18 integrations is checked for the destination, credential and required fields its config validation asks for, with the global defaults it falls back to: e.g. a webhook url, an Opsgenie api_key (or global.opsgenie_api_key), a Telegram chat_id and bot token, a Webex room_id and authorization, an SNS target, a Jira project and issue_type.
|
|
5
|
+
*/
|
|
6
|
+
import { alertmanagerDiagnostics } from "./prom-helpers.js";
|
|
7
|
+
export const prom209 = {
|
|
8
|
+
id: "PROM209",
|
|
9
|
+
description: "A receiver integration is missing its destination or credential",
|
|
10
|
+
check(ctx) {
|
|
11
|
+
return alertmanagerDiagnostics(ctx, "PROM209");
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM210: A receiver integration or global setting is one Alertmanager rejects
|
|
3
|
+
*
|
|
4
|
+
* Two settings that exclude each other (a value and its *_file, a Slack api_url and app_token, Pushover html and monospace, two SNS targets), or a value outside the allowed set (WeChat message_type, Telegram parse_mode, Jira api_type, an Opsgenie responder type, a VictorOps reserved custom field, a smarthost that is not host:port, a Slack or Mattermost field without title and value).
|
|
5
|
+
*/
|
|
6
|
+
import { alertmanagerDiagnostics } from "./prom-helpers.js";
|
|
7
|
+
export const prom210 = {
|
|
8
|
+
id: "PROM210",
|
|
9
|
+
description: "A receiver integration or global setting is one Alertmanager rejects",
|
|
10
|
+
check(ctx) {
|
|
11
|
+
return alertmanagerDiagnostics(ctx, "PROM210");
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM211: An alerting rule has no for, or for: 0s
|
|
3
|
+
*
|
|
4
|
+
* The alert fires on the first evaluation that matches, so one bad scrape
|
|
5
|
+
* pages. Left alone when the expression reads no series (vector(1), a dead
|
|
6
|
+
* man's switch) or already spans a window: every selector read through a
|
|
7
|
+
* *_over_time function, or two conditions joined by and (the multi-window
|
|
8
|
+
* burn-rate form, where the short window does what for would).
|
|
9
|
+
*/
|
|
10
|
+
import { ruleFileDiagnostics } from "./prom-helpers.js";
|
|
11
|
+
export const prom211 = {
|
|
12
|
+
id: "PROM211",
|
|
13
|
+
description: "An alerting rule has no for, or for: 0s",
|
|
14
|
+
check(ctx) {
|
|
15
|
+
return ruleFileDiagnostics(ctx, "PROM211");
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM212: An alerting rule has no runbook_url annotation (opt-in)
|
|
3
|
+
*
|
|
4
|
+
* Off unless enabled: the lint preset all, or a lint.rules entry for PROM212,
|
|
5
|
+
* turns it on. See validateRunbookUrls.
|
|
6
|
+
*/
|
|
7
|
+
import { runbookDiagnostics } from "./prom-helpers.js";
|
|
8
|
+
export const prom212 = {
|
|
9
|
+
id: "PROM212",
|
|
10
|
+
description: "An alerting rule has no runbook_url annotation (opt-in)",
|
|
11
|
+
check(ctx) {
|
|
12
|
+
return runbookDiagnostics(ctx);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM213: An alert expression has no comparison
|
|
3
|
+
*
|
|
4
|
+
* Every series the expression returns is an alert, so an expression with no
|
|
5
|
+
* comparison fires for all of them. absent(), absent_over_time() and unless
|
|
6
|
+
* count as a condition; a comparison with bool does not. An expression that
|
|
7
|
+
* reads no series (vector(1), a dead man's switch) is left alone.
|
|
8
|
+
*/
|
|
9
|
+
import { ruleFileDiagnostics } from "./prom-helpers.js";
|
|
10
|
+
export const prom213 = {
|
|
11
|
+
id: "PROM213",
|
|
12
|
+
description: "An alert expression has no comparison",
|
|
13
|
+
check(ctx) {
|
|
14
|
+
return ruleFileDiagnostics(ctx, "PROM213");
|
|
15
|
+
},
|
|
16
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM214: An alert template reads a label the expression aggregates away
|
|
3
|
+
*
|
|
4
|
+
* A $labels.x whose x the expression's by (...) leaves out, or its without
|
|
5
|
+
* (...) names, renders empty. Labels the rule or its group set are left alone.
|
|
6
|
+
*/
|
|
7
|
+
import { ruleFileDiagnostics } from "./prom-helpers.js";
|
|
8
|
+
export const prom214 = {
|
|
9
|
+
id: "PROM214",
|
|
10
|
+
description: "An alert template reads a label the expression aggregates away",
|
|
11
|
+
check(ctx) {
|
|
12
|
+
return ruleFileDiagnostics(ctx, "PROM214");
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM215: rate, irate or increase over a name that is not a counter's
|
|
3
|
+
*
|
|
4
|
+
* These functions treat every drop as a counter reset. A name not ending in
|
|
5
|
+
* _total, _count, _sum or _bucket is read as a gauge.
|
|
6
|
+
*/
|
|
7
|
+
import { ruleFileDiagnostics } from "./prom-helpers.js";
|
|
8
|
+
export const prom215 = {
|
|
9
|
+
id: "PROM215",
|
|
10
|
+
description: "rate, irate or increase over a name that is not a counter's",
|
|
11
|
+
check(ctx) {
|
|
12
|
+
return ruleFileDiagnostics(ctx, "PROM215");
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM216: histogram_quantile over a series without _bucket, or without le
|
|
3
|
+
*
|
|
4
|
+
* A classic histogram's quantile needs its _bucket series with the le label
|
|
5
|
+
* kept. A native histogram has no _bucket series, so this check does not apply
|
|
6
|
+
* to one.
|
|
7
|
+
*/
|
|
8
|
+
import { ruleFileDiagnostics } from "./prom-helpers.js";
|
|
9
|
+
export const prom216 = {
|
|
10
|
+
id: "PROM216",
|
|
11
|
+
description: "histogram_quantile over a series without _bucket, or without le",
|
|
12
|
+
check(ctx) {
|
|
13
|
+
return ruleFileDiagnostics(ctx, "PROM216");
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM217: A recording rule name is not in level:metric:operations form
|
|
3
|
+
*
|
|
4
|
+
* Prometheus's naming convention for recorded series, e.g.
|
|
5
|
+
* job:http_requests:rate5m.
|
|
6
|
+
*/
|
|
7
|
+
import { ruleFileDiagnostics } from "./prom-helpers.js";
|
|
8
|
+
export const prom217 = {
|
|
9
|
+
id: "PROM217",
|
|
10
|
+
description: "A recording rule name is not in level:metric:operations form",
|
|
11
|
+
check(ctx) {
|
|
12
|
+
return ruleFileDiagnostics(ctx, "PROM217");
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM218: A regex matcher needs no regex, or is anchored
|
|
3
|
+
*
|
|
4
|
+
* A =~ or !~ with no regex metacharacters is an equality matcher written the
|
|
5
|
+
* slow way. Prometheus anchors every regex, so a leading ^ or trailing $ does
|
|
6
|
+
* nothing.
|
|
7
|
+
*/
|
|
8
|
+
import { ruleFileDiagnostics } from "./prom-helpers.js";
|
|
9
|
+
export const prom218 = {
|
|
10
|
+
id: "PROM218",
|
|
11
|
+
description: "A regex matcher needs no regex, or is anchored",
|
|
12
|
+
check(ctx) {
|
|
13
|
+
return ruleFileDiagnostics(ctx, "PROM218");
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM219: An alerting rule sets the alertname label
|
|
3
|
+
*
|
|
4
|
+
* Prometheus sets alertname to the rule's name after applying the rule's
|
|
5
|
+
* labels, so the value is overwritten.
|
|
6
|
+
*/
|
|
7
|
+
import { ruleFileDiagnostics } from "./prom-helpers.js";
|
|
8
|
+
export const prom219 = {
|
|
9
|
+
id: "PROM219",
|
|
10
|
+
description: "An alerting rule sets the alertname label",
|
|
11
|
+
check(ctx) {
|
|
12
|
+
return ruleFileDiagnostics(ctx, "PROM219");
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM220: A receiver turns off TLS certificate verification
|
|
3
|
+
*
|
|
4
|
+
* tls_config.insecure_skip_verify: true on a receiver, or in global, accepts
|
|
5
|
+
* any certificate the endpoint presents.
|
|
6
|
+
*/
|
|
7
|
+
import { alertmanagerDiagnostics } from "./prom-helpers.js";
|
|
8
|
+
export const prom220 = {
|
|
9
|
+
id: "PROM220",
|
|
10
|
+
description: "A receiver turns off TLS certificate verification",
|
|
11
|
+
check(ctx) {
|
|
12
|
+
return alertmanagerDiagnostics(ctx, "PROM220");
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM221: SMTP credentials sent with require_tls false
|
|
3
|
+
*
|
|
4
|
+
* An email receiver with SMTP auth and require_tls false (its own, or
|
|
5
|
+
* global.smtp_require_tls) can send the credentials in clear text. Implicit
|
|
6
|
+
* TLS is left alone.
|
|
7
|
+
*/
|
|
8
|
+
import { alertmanagerDiagnostics } from "./prom-helpers.js";
|
|
9
|
+
export const prom221 = {
|
|
10
|
+
id: "PROM221",
|
|
11
|
+
description: "SMTP credentials sent with require_tls false",
|
|
12
|
+
check(ctx) {
|
|
13
|
+
return alertmanagerDiagnostics(ctx, "PROM221");
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM222: Credentials sent to an http:// receiver URL
|
|
3
|
+
*
|
|
4
|
+
* An integration whose URL is http:// and whose request carries a credential:
|
|
5
|
+
* user info in the URL, http_config auth, or a key or token field.
|
|
6
|
+
*/
|
|
7
|
+
import { alertmanagerDiagnostics } from "./prom-helpers.js";
|
|
8
|
+
export const prom222 = {
|
|
9
|
+
id: "PROM222",
|
|
10
|
+
description: "Credentials sent to an http:// receiver URL",
|
|
11
|
+
check(ctx) {
|
|
12
|
+
return alertmanagerDiagnostics(ctx, "PROM222");
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM223: repeat_interval is shorter than group_interval
|
|
3
|
+
*
|
|
4
|
+
* Alertmanager resends only when it flushes a group, every group_interval, so
|
|
5
|
+
* a shorter repeat_interval never takes effect. Intervals are inherited down
|
|
6
|
+
* the routing tree from Alertmanager's defaults.
|
|
7
|
+
*/
|
|
8
|
+
import { alertmanagerDiagnostics } from "./prom-helpers.js";
|
|
9
|
+
export const prom223 = {
|
|
10
|
+
id: "PROM223",
|
|
11
|
+
description: "repeat_interval is shorter than group_interval",
|
|
12
|
+
check(ctx) {
|
|
13
|
+
return alertmanagerDiagnostics(ctx, "PROM223");
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM224: An inhibit rule matches one alert as source and target, with no equal
|
|
3
|
+
*
|
|
4
|
+
* With no equal, any alert matching the source mutes every alert matching the
|
|
5
|
+
* target. When one alert can match both, alerts of that kind mute each other.
|
|
6
|
+
*/
|
|
7
|
+
import { alertmanagerDiagnostics } from "./prom-helpers.js";
|
|
8
|
+
export const prom224 = {
|
|
9
|
+
id: "PROM224",
|
|
10
|
+
description: "An inhibit rule matches one alert as source and target, with no equal",
|
|
11
|
+
check(ctx) {
|
|
12
|
+
return alertmanagerDiagnostics(ctx, "PROM224");
|
|
13
|
+
},
|
|
14
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM225: A labeldrop or labelkeep relabel step carries a field it does not take
|
|
3
|
+
*
|
|
4
|
+
* Both actions match regex against label names, so source_labels, separator,
|
|
5
|
+
* target_label, modulus and replacement do nothing, and Prometheus rejects the
|
|
6
|
+
* file at load. Reads every prometheus.yml in the output.
|
|
7
|
+
*/
|
|
8
|
+
import { relabelDiagnostics } from "./prom-helpers.js";
|
|
9
|
+
export const prom225 = {
|
|
10
|
+
id: "PROM225",
|
|
11
|
+
description: "A labeldrop or labelkeep relabel step carries a field it does not take",
|
|
12
|
+
check(ctx) {
|
|
13
|
+
return relabelDiagnostics(ctx);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROM301: A rule reads a connector metric no collector in the build emits
|
|
3
|
+
*
|
|
4
|
+
* For each selector in a rule whose name falls under a namespace the build's
|
|
5
|
+
* spanmetrics, servicegraph or GenAI connectors own (spanmetrics' and
|
|
6
|
+
* servicegraph's defaults included), reports a name no collector config in
|
|
7
|
+
* the build emits, and a by (...) label that is not a dimension the config
|
|
8
|
+
* declares for the metrics aggregated. Silent for names outside those
|
|
9
|
+
* namespaces, and when the build has no collector config. See
|
|
10
|
+
* ../../collector-metrics.ts.
|
|
11
|
+
*/
|
|
12
|
+
import { collectorMetricDiagnostics } from "./prom-helpers.js";
|
|
13
|
+
export const prom301 = {
|
|
14
|
+
id: "PROM301",
|
|
15
|
+
description: "A rule reads a connector metric no collector in the build emits",
|
|
16
|
+
check(ctx) {
|
|
17
|
+
return collectorMetricDiagnostics(ctx);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { literalCredentialRule } from "./literal-credential.js";
|
|
2
|
+
import { promqlLiteralRule } from "./promql-literal.js";
|
|
3
|
+
import { sloLiteralRule } from "./slo-literal.js";
|
|
4
|
+
export { literalCredentialRule, SECRET_FIELDS } from "./literal-credential.js";
|
|
5
|
+
export { promqlLiteralRule } from "./promql-literal.js";
|
|
6
|
+
export { sloLiteralRule } from "./slo-literal.js";
|
|
7
|
+
/** All lint rules provided by this lexicon (imported by plugin.ts's lintRules()). */
|
|
8
|
+
export const rules = [literalCredentialRule, promqlLiteralRule, sloLiteralRule];
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { ts } from "@intentius/chant/lint/typescript";
|
|
2
|
+
import { SLACK_APP_URL } from "../../validate-integrations.js";
|
|
3
|
+
import { calleeName, constInitializers, CREDENTIAL_CLASS, literalText, position, propertyName, resolveConst } from "./prom-ast.js";
|
|
4
|
+
/**
|
|
5
|
+
* Alertmanager fields that hold a secret, each with a `*_file` sibling that
|
|
6
|
+
* reads it from a mounted file instead.
|
|
7
|
+
*/
|
|
8
|
+
export const SECRET_FIELDS = new Set([
|
|
9
|
+
"api_url", // slack_configs only: the incoming-webhook URL is the credential (see SECRET_ONLY_IN)
|
|
10
|
+
"webhook_url", // discord, msteams, msteamsv2 and mattermost
|
|
11
|
+
"app_token",
|
|
12
|
+
"routing_key", // pagerduty (victorops' routing_key names a route, see SECRET_ONLY_IN)
|
|
13
|
+
"service_key",
|
|
14
|
+
"api_key",
|
|
15
|
+
"api_secret",
|
|
16
|
+
"bot_token",
|
|
17
|
+
"token",
|
|
18
|
+
"token_id",
|
|
19
|
+
"user_key",
|
|
20
|
+
"alert_source_token",
|
|
21
|
+
"auth_password",
|
|
22
|
+
"auth_secret",
|
|
23
|
+
"smtp_auth_password",
|
|
24
|
+
"smtp_auth_secret",
|
|
25
|
+
"slack_api_url",
|
|
26
|
+
"slack_app_token",
|
|
27
|
+
"opsgenie_api_key",
|
|
28
|
+
"wechat_api_secret",
|
|
29
|
+
"victorops_api_key",
|
|
30
|
+
"telegram_bot_token",
|
|
31
|
+
"rocketchat_token",
|
|
32
|
+
"rocketchat_token_id",
|
|
33
|
+
"mattermost_webhook_url",
|
|
34
|
+
"password",
|
|
35
|
+
"credentials",
|
|
36
|
+
"bearer_token",
|
|
37
|
+
"client_secret",
|
|
38
|
+
]);
|
|
39
|
+
/**
|
|
40
|
+
* Fields that are a secret in one integration and plain elsewhere: `api_url`
|
|
41
|
+
* is Slack's webhook credential but only an endpoint for the others, and a
|
|
42
|
+
* VictorOps `routing_key` names a route.
|
|
43
|
+
*/
|
|
44
|
+
const SECRET_ONLY_IN = {
|
|
45
|
+
api_url: "slack_configs",
|
|
46
|
+
routing_key: "pagerduty_configs",
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* PROM001: a credential written as a literal in a `Receiver` or
|
|
50
|
+
* `AlertmanagerSettings`.
|
|
51
|
+
*
|
|
52
|
+
* A literal there is a secret in the repository and in the emitted
|
|
53
|
+
* `alertmanager.yml`. Alertmanager does not expand environment variables in
|
|
54
|
+
* its config, so the fix is the field's `*_file` sibling (`api_url_file`,
|
|
55
|
+
* `routing_key_file`, `auth_password_file`, `credentials_file`), pointing at
|
|
56
|
+
* a file mounted from a secret store.
|
|
57
|
+
*/
|
|
58
|
+
export const literalCredentialRule = {
|
|
59
|
+
id: "PROM001",
|
|
60
|
+
severity: "error",
|
|
61
|
+
category: "security",
|
|
62
|
+
description: "Alertmanager credential declared as a literal instead of read from a *_file",
|
|
63
|
+
check(context) {
|
|
64
|
+
const diagnostics = [];
|
|
65
|
+
const source = context.sourceFile;
|
|
66
|
+
const consts = constInitializers(source);
|
|
67
|
+
const scanned = new Set();
|
|
68
|
+
// A value is followed to the const it names, so a list lifted out of the
|
|
69
|
+
// constructor (`email_configs: heartbeatEmail`) is scanned where it is used.
|
|
70
|
+
// `integration` is the `*_configs` list a value sits in, when that is known.
|
|
71
|
+
const scanValue = (key, value, integration) => {
|
|
72
|
+
const init = resolveConst(value, consts);
|
|
73
|
+
const within = key?.endsWith("_configs") ? key : integration;
|
|
74
|
+
if (ts.isObjectLiteralExpression(init) || ts.isArrayLiteralExpression(init)) {
|
|
75
|
+
if (scanned.has(init))
|
|
76
|
+
return;
|
|
77
|
+
scanned.add(init);
|
|
78
|
+
if (ts.isObjectLiteralExpression(init))
|
|
79
|
+
scan(init, within);
|
|
80
|
+
else
|
|
81
|
+
for (const el of init.elements)
|
|
82
|
+
scanValue(undefined, el, within);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (!key || !SECRET_FIELDS.has(key))
|
|
86
|
+
return;
|
|
87
|
+
const only = SECRET_ONLY_IN[key];
|
|
88
|
+
if (only && integration !== undefined && integration !== only)
|
|
89
|
+
return;
|
|
90
|
+
const text = literalText(init);
|
|
91
|
+
if (text === undefined || text === "")
|
|
92
|
+
return;
|
|
93
|
+
// A Slack bot posts to the public chat.postMessage endpoint with its token
|
|
94
|
+
// in http_config, and update_message needs exactly that api_url (PROM210).
|
|
95
|
+
if (key === "api_url" && text === SLACK_APP_URL)
|
|
96
|
+
return;
|
|
97
|
+
diagnostics.push({
|
|
98
|
+
ruleId: "PROM001",
|
|
99
|
+
severity: "error",
|
|
100
|
+
message: `\`${key}\` is a literal credential; Alertmanager doesn't expand environment variables, so mount the secret and set \`${key}_file\` instead`,
|
|
101
|
+
file: context.filePath,
|
|
102
|
+
...position(source, init),
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
const scan = (obj, integration) => {
|
|
106
|
+
for (const prop of obj.properties) {
|
|
107
|
+
if (ts.isPropertyAssignment(prop))
|
|
108
|
+
scanValue(propertyName(prop), prop.initializer, integration);
|
|
109
|
+
else if (ts.isShorthandPropertyAssignment(prop))
|
|
110
|
+
scanValue(prop.name.text, prop.name, integration);
|
|
111
|
+
else if (ts.isSpreadAssignment(prop))
|
|
112
|
+
scanValue(undefined, prop.expression, integration);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const visit = (node) => {
|
|
116
|
+
if (ts.isNewExpression(node) || ts.isCallExpression(node)) {
|
|
117
|
+
const name = calleeName(node);
|
|
118
|
+
const arg = node.arguments?.[0];
|
|
119
|
+
if (name && CREDENTIAL_CLASS.test(name) && arg)
|
|
120
|
+
scanValue(undefined, arg);
|
|
121
|
+
}
|
|
122
|
+
ts.forEachChild(node, visit);
|
|
123
|
+
};
|
|
124
|
+
visit(source);
|
|
125
|
+
return diagnostics;
|
|
126
|
+
},
|
|
127
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ts } from "@intentius/chant/lint/typescript";
|
|
2
|
+
/** The constructor name of a `new X(...)` or `X(...)` expression, if it is a plain or dotted name. */
|
|
3
|
+
export function calleeName(node) {
|
|
4
|
+
const callee = node.expression;
|
|
5
|
+
if (ts.isIdentifier(callee))
|
|
6
|
+
return callee.text;
|
|
7
|
+
if (ts.isPropertyAccessExpression(callee))
|
|
8
|
+
return callee.name.text;
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
/** The classes whose props hold Alertmanager credentials. */
|
|
12
|
+
export const CREDENTIAL_CLASS = /^(Receiver|AlertmanagerSettings)$/;
|
|
13
|
+
/** A property's key as text, for identifier and string-literal keys. */
|
|
14
|
+
export function propertyName(prop) {
|
|
15
|
+
if (!ts.isPropertyAssignment(prop))
|
|
16
|
+
return undefined;
|
|
17
|
+
if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name))
|
|
18
|
+
return prop.name.text;
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
/** The text of a string literal or substitution-free template, else undefined. */
|
|
22
|
+
export function literalText(node) {
|
|
23
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
|
|
24
|
+
return node.text;
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
export function position(source, node) {
|
|
28
|
+
const { line, character } = source.getLineAndCharacterOfPosition(node.getStart(source));
|
|
29
|
+
return { line: line + 1, column: character + 1 };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Every `const name = <initializer>` in the file, so a rule can follow a
|
|
33
|
+
* value lifted out of a constructor into a named const (the way COR001 asks
|
|
34
|
+
* for it, and the way `chant import` writes it) back to its literal.
|
|
35
|
+
*/
|
|
36
|
+
export function constInitializers(source) {
|
|
37
|
+
const consts = new Map();
|
|
38
|
+
const collect = (node) => {
|
|
39
|
+
if (ts.isVariableDeclarationList(node) && node.flags & ts.NodeFlags.Const) {
|
|
40
|
+
for (const d of node.declarations) {
|
|
41
|
+
if (ts.isIdentifier(d.name) && d.initializer)
|
|
42
|
+
consts.set(d.name.text, d.initializer);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
ts.forEachChild(node, collect);
|
|
46
|
+
};
|
|
47
|
+
collect(source);
|
|
48
|
+
return consts;
|
|
49
|
+
}
|
|
50
|
+
/** `expr` itself, or the initializer of the const it names (followed through `as` and `satisfies`). */
|
|
51
|
+
export function resolveConst(expr, consts) {
|
|
52
|
+
let cur = expr;
|
|
53
|
+
for (let hops = 0; hops < 8; hops++) {
|
|
54
|
+
if (ts.isAsExpression(cur) || ts.isSatisfiesExpression(cur) || ts.isParenthesizedExpression(cur)) {
|
|
55
|
+
cur = cur.expression;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (ts.isIdentifier(cur) && consts.has(cur.text)) {
|
|
59
|
+
cur = consts.get(cur.text);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
return cur;
|
|
65
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ts } from "@intentius/chant/lint/typescript";
|
|
2
|
+
import { calleeName, constInitializers, literalText, position, propertyName, resolveConst } from "./prom-ast.js";
|
|
3
|
+
import { checkPromql } from "../../promql.js";
|
|
4
|
+
/**
|
|
5
|
+
* PROM002: a rule's `expr`, written as a string literal inside a
|
|
6
|
+
* `RuleGroup`, is not valid PromQL.
|
|
7
|
+
*
|
|
8
|
+
* The same parse runs after the build as PROM104, over every rule in the
|
|
9
|
+
* emitted rule file. This rule reports it in the editor, at the expression,
|
|
10
|
+
* for the literals it can see; an `expr` built at runtime is left to PROM104.
|
|
11
|
+
*/
|
|
12
|
+
export const promqlLiteralRule = {
|
|
13
|
+
id: "PROM002",
|
|
14
|
+
severity: "error",
|
|
15
|
+
category: "correctness",
|
|
16
|
+
description: "A literal PromQL expression in a RuleGroup does not parse",
|
|
17
|
+
check(context) {
|
|
18
|
+
const diagnostics = [];
|
|
19
|
+
const source = context.sourceFile;
|
|
20
|
+
const consts = constInitializers(source);
|
|
21
|
+
const scanned = new Set();
|
|
22
|
+
// Rules lifted into a const (`rules: apiRules`) are followed to it and
|
|
23
|
+
// checked where the group uses them.
|
|
24
|
+
const follow = (value) => {
|
|
25
|
+
const init = resolveConst(value, consts);
|
|
26
|
+
if (init !== value && !scanned.has(init)) {
|
|
27
|
+
scanned.add(init);
|
|
28
|
+
scan(init);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const scan = (node) => {
|
|
32
|
+
if (ts.isShorthandPropertyAssignment(node))
|
|
33
|
+
follow(node.name);
|
|
34
|
+
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.initializer))
|
|
35
|
+
follow(node.initializer);
|
|
36
|
+
if (ts.isPropertyAssignment(node) && propertyName(node) === "expr") {
|
|
37
|
+
const text = literalText(node.initializer);
|
|
38
|
+
if (text !== undefined) {
|
|
39
|
+
const checked = checkPromql(text);
|
|
40
|
+
if (!checked.ok) {
|
|
41
|
+
diagnostics.push({
|
|
42
|
+
ruleId: "PROM002",
|
|
43
|
+
severity: "error",
|
|
44
|
+
message: `expr is not valid PromQL: ${checked.message}`,
|
|
45
|
+
file: context.filePath,
|
|
46
|
+
...position(source, node.initializer),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
ts.forEachChild(node, scan);
|
|
52
|
+
};
|
|
53
|
+
const visit = (node) => {
|
|
54
|
+
if (ts.isNewExpression(node) && calleeName(node) === "RuleGroup") {
|
|
55
|
+
const arg = node.arguments?.[0];
|
|
56
|
+
if (arg)
|
|
57
|
+
scan(arg);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
ts.forEachChild(node, visit);
|
|
61
|
+
};
|
|
62
|
+
visit(source);
|
|
63
|
+
return diagnostics;
|
|
64
|
+
},
|
|
65
|
+
};
|