mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
fix(security): redact standalone hlk_ / hlr_ native API tokens in logs
The Bearer rule already covers `Authorization: Bearer hlk_…` headers, but standalone tokens in error bodies, JSON dumps, or raw debug strings were untouched. The idempotency replay-cache rejects bodies containing `hlk_` / `hlr_` (CLAUDE.md headless-client-API patterns) exactly so cached responses can never echo a token back; this commit adds the matching egress guard for log/error surfaces. Boundary-aware shape mirrors the v1.5 phase-2 sk- regex tightening so benign identifiers like `html_render` or short `hlk_short` strings are not over-matched. Closes H4 from .planning/phase-6-code-review-findings.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
8e17f227e4
commit
f0cfd26d96
2 files changed
+32
No files matched your search
@@ -85,6 +85,32 @@ describe("redactSecrets", () => {
|
||||
expect(redactSecrets(",sk-1234567890abcdef,")).toBe(",[REDACTED],");
|
||||
});
|
||||
|
||||
it("redacts standalone HealthLog native API tokens (hlk_ / hlr_)", () => {
|
||||
// Start of string.
|
||||
expect(
|
||||
redactSecrets("hlk_abc123def4567890abcdef1234567890ABCDEF1234567890abcd"),
|
||||
).toBe("[REDACTED]");
|
||||
expect(
|
||||
redactSecrets("hlr_abc123def4567890abcdef1234567890ABCDEF1234567890abcd"),
|
||||
).toBe("[REDACTED]");
|
||||
// Preceded by separator, leading separator preserved.
|
||||
expect(
|
||||
redactSecrets(
|
||||
"refresh: hlr_abc123def4567890abcdef1234567890ABCDEF1234567890abcd end",
|
||||
),
|
||||
).toBe("refresh: [REDACTED] end");
|
||||
expect(
|
||||
redactSecrets('{"token": "hlk_0123456789abcdef0123456789abcdef0123"}'),
|
||||
).toBe('{"token": "[REDACTED]"}');
|
||||
});
|
||||
|
||||
it("does NOT redact non-token strings starting with `hl`", () => {
|
||||
expect(redactSecrets("html_render finished")).toBe("html_render finished");
|
||||
expect(redactSecrets("healthcheck ok")).toBe("healthcheck ok");
|
||||
// Too-short trailing hex — keep as-is.
|
||||
expect(redactSecrets("hlk_short")).toBe("hlk_short");
|
||||
});
|
||||
|
||||
it("handles multiple secrets in one string", () => {
|
||||
// Bearer matches `\S+` so it greedily consumes `hlk_x;` — that's
|
||||
// safe (over-redaction is fine when the alternative is leaking).
|
||||
|
||||
@@ -36,6 +36,12 @@ export function redactSecrets(input: string): string {
|
||||
// tiny safety margin against `sk-1`-style false-positives in code
|
||||
// identifiers. Capture group 1 = the leading separator (preserved).
|
||||
.replace(/(^|[^A-Za-z0-9])sk-(?:ant-)?[A-Za-z0-9_-]{8,}/g, "$1[REDACTED]")
|
||||
// HealthLog native API tokens (`hlk_<64hex>` access, `hlr_<64hex>`
|
||||
// refresh). Same boundary-aware shape as the `sk-` rule. The
|
||||
// idempotency replay-cache already rejects bodies containing
|
||||
// these prefixes (CLAUDE.md headless-client-API patterns); this
|
||||
// is the matching egress guard for log/error surfaces.
|
||||
.replace(/(^|[^A-Za-z0-9])hl[kr]_[A-Fa-f0-9]{32,}/g, "$1[REDACTED]")
|
||||
.replace(
|
||||
/([?&])(secret|code|token|api[_-]?key)=[^&\s]+/gi,
|
||||
"$1$2=[REDACTED]",
|
||||
|
||||
Reference in new issue
Block a user