openclaw-grafana-lens
Version:
OpenClaw plugin that gives AI agents full Grafana access — 18 composable tools for PromQL/LogQL/TraceQL queries, dashboard creation, alerting, SRE investigation, security monitoring, data collection pipeline management via Grafana Alloy (29 recipes), and
98 lines (97 loc) • 3.87 kB
JavaScript
/**
* Sensitive data redaction — local implementation following openclaw's convention.
*
* `redactSensitiveText` is NOT in the published npm package (v2026.2.15) —
* only in openclaw source. When it becomes available via plugin-sdk, switch to it.
*
* Format: `${first6}…${last4}` for tokens >= 18 chars, `***` for shorter tokens.
* Patterns cover common API key formats, Bearer tokens, and PEM blocks.
*/
// ── Loki compatibility: flatten dotted OTel keys to underscores ──────
// Loki structured metadata uses underscore keys; OTel semantic conventions use dots.
// Shared by lifecycle-telemetry and metrics-collector log emission paths.
export function flattenLogKeys(attrs) {
const result = {};
for (const [key, value] of Object.entries(attrs)) {
result[key.replace(/\./g, "_")] = value;
}
return result;
}
// ══════════════════════════════════════════════════════════════════════
// Token patterns — ordered by specificity (longer prefixes first)
// ══════════════════════════════════════════════════════════════════════
const TOKEN_PATTERNS = [
// GitHub
/\bghp_[A-Za-z0-9]{36,}\b/g,
/\bgithub_pat_[A-Za-z0-9_]{22,}\b/g,
/\bgho_[A-Za-z0-9]{36,}\b/g,
/\bghs_[A-Za-z0-9]{36,}\b/g,
/\bghr_[A-Za-z0-9]{36,}\b/g,
// Slack
/\bxoxb-[A-Za-z0-9\-]{20,}\b/g,
/\bxoxp-[A-Za-z0-9\-]{20,}\b/g,
/\bxoxa-[A-Za-z0-9\-]{20,}\b/g,
/\bxoxr-[A-Za-z0-9\-]{20,}\b/g,
/\bxapp-[A-Za-z0-9\-]{20,}\b/g,
// Anthropic / OpenAI / Groq / Google / Perplexity
/\bsk-ant-[A-Za-z0-9\-_]{20,}\b/g,
/\bsk-[A-Za-z0-9\-_]{20,}\b/g,
/\bgsk_[A-Za-z0-9]{20,}\b/g,
/\bAIza[A-Za-z0-9\-_]{30,}\b/g,
/\bpplx-[A-Za-z0-9]{20,}\b/g,
// npm
/\bnpm_[A-Za-z0-9]{20,}\b/g,
// Grafana service account tokens
/\bglsa_[A-Za-z0-9]{20,}\b/g,
// Bearer tokens in headers
/\bBearer\s+[A-Za-z0-9\-_.~+/]{20,}=*\b/g,
// Telegram bot tokens (numeric:alphanumeric)
/\b\d{8,}:[A-Za-z0-9_-]{30,}\b/g,
];
// PEM blocks (multi-line)
const PEM_PATTERN = /-----BEGIN [A-Z ]+-----[\s\S]*?-----END [A-Z ]+-----/g;
/**
* Redact a single token string using the `first6…last4` format.
* Short tokens (< 18 chars) are fully replaced with `***`.
*/
function redactToken(token) {
if (token.length < 18)
return "***";
return `${token.slice(0, 6)}…${token.slice(-4)}`;
}
/**
* Redact sensitive tokens and credentials from a text string.
*
* Covers: API keys (sk-*, ghp_*, github_pat_*, xox*, gsk_*, AIza*, pplx-*,
* npm_*, glsa_*), Bearer tokens, PEM blocks, Telegram bot tokens.
*
* @param text - The input text to redact
* @returns Text with sensitive values replaced
*/
export function redactSecrets(text) {
if (!text)
return text;
// PEM blocks first (multi-line)
let result = text.replace(PEM_PATTERN, "[REDACTED PEM BLOCK]");
// Token patterns (single-line)
for (const pattern of TOKEN_PATTERNS) {
// Reset lastIndex for global regexes
pattern.lastIndex = 0;
result = result.replace(pattern, (match) => redactToken(match));
}
return result;
}
/**
* Apply `redactSecrets` to all string values in an attributes record.
* Non-string values are passed through unchanged.
*
* @param attrs - Record of attribute key-value pairs
* @returns New record with string values redacted
*/
export function redactAttributes(attrs) {
const result = {};
for (const [key, value] of Object.entries(attrs)) {
result[key] = typeof value === "string" ? redactSecrets(value) : value;
}
return result;
}