fix: 加固运行时证据凭据脱敏
This commit is contained in:
parent
e480c0edcb
commit
269289a859
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
**Goal:** Implement Phase 1 of the approved API runtime foundation: typed runtime config, classified runtime errors, structured runtime evidence, and a lightweight `GET /health/runtime` endpoint that can be used by Jenkins/K8s/ktWorkflow in the next plan.
|
**Goal:** Implement Phase 1 of the approved API runtime foundation: typed runtime config, classified runtime errors, structured runtime evidence, and a lightweight `GET /health/runtime` endpoint that can be used by Jenkins/K8s/ktWorkflow in the next plan.
|
||||||
|
|
||||||
|
**Post-review amendment:** The final public `GET /health/runtime` contract intentionally returns only `service`, `checkedAt`, `status`, and `checks`. Earlier code snippets in this plan that included `config` or described the endpoint as a safe config view were superseded during code review to avoid exposing runtime topology on an unauthenticated health endpoint. The internal `RuntimeConfigService` still owns the safe snapshot used to derive checks.
|
||||||
|
|
||||||
**Architecture:** Add a focused `src/runtime` Nest module. It depends on `ConfigModule` and `CommonModule`, exports typed runtime primitives, and keeps business modules unchanged during this phase. The endpoint returns plain machine-readable JSON, not a Vben response wrapper, so deployment tooling can consume it directly.
|
**Architecture:** Add a focused `src/runtime` Nest module. It depends on `ConfigModule` and `CommonModule`, exports typed runtime primitives, and keeps business modules unchanged during this phase. The endpoint returns plain machine-readable JSON, not a Vben response wrapper, so deployment tooling can consume it directly.
|
||||||
|
|
||||||
**Tech Stack:** NestJS 11, TypeScript 5.9, Jest 29 with `ts-jest`, existing `@nestjs/config`, existing `ToolsService`, existing Swagger/Knife4j setup.
|
**Tech Stack:** NestJS 11, TypeScript 5.9, Jest 29 with `ts-jest`, existing `@nestjs/config`, existing `ToolsService`, existing Swagger/Knife4j setup.
|
||||||
@ -1245,11 +1247,12 @@ git commit -m "feat: 添加API运行时健康检查"
|
|||||||
### Runtime health
|
### Runtime health
|
||||||
|
|
||||||
The API exposes `GET /health/runtime` for deployment and local smoke checks.
|
The API exposes `GET /health/runtime` for deployment and local smoke checks.
|
||||||
|
|
||||||
|
Post-review note: the public response no longer returns `config`; it exposes status and config check results only.
|
||||||
It returns plain JSON with:
|
It returns plain JSON with:
|
||||||
|
|
||||||
- `status`: `live`, `ready`, `degraded`, or `blocked`.
|
- `status`: `live`, `ready`, `degraded`, or `blocked`.
|
||||||
- `checks`: process and runtime config checks.
|
- `checks`: process and runtime config checks.
|
||||||
- `config`: a safe runtime config snapshot with secrets masked.
|
|
||||||
|
|
||||||
The endpoint is machine-readable and intentionally does not use the Vben
|
The endpoint is machine-readable and intentionally does not use the Vben
|
||||||
response wrapper.
|
response wrapper.
|
||||||
|
|||||||
@ -7,9 +7,9 @@ import {
|
|||||||
const REDACTED_VALUE = '<redacted>';
|
const REDACTED_VALUE = '<redacted>';
|
||||||
const REDACTED_BASE64_VALUE = '<redacted-base64>';
|
const REDACTED_BASE64_VALUE = '<redacted-base64>';
|
||||||
const SENSITIVE_KEY_PATTERN =
|
const SENSITIVE_KEY_PATTERN =
|
||||||
/password|secret|token|authorization|cookie|privatekey|sshkey|ticket|randstr|replytext|base64/i;
|
/password|secret|token|authorization|cookie|privatekey|sshkey|accesskey|apikey|ticket|randstr|replytext|base64/i;
|
||||||
const SENSITIVE_TEXT_KEY_PATTERN =
|
const SENSITIVE_TEXT_KEY_PATTERN =
|
||||||
'(?:[A-Za-z0-9_-]*(?:password|secret|token|authorization|cookie|private[_-]?key|ssh[_-]?key|ticket|randstr|replyText|base64)[A-Za-z0-9_-]*|sid)';
|
'(?:[A-Za-z0-9_-]*(?:password|secret|token|authorization|cookie|private[_-]?key|ssh[_-]?key|access[_-]?key|api[_-]?key|ticket|randstr|replyText|base64)[A-Za-z0-9_-]*|sid)';
|
||||||
const SENSITIVE_TEXT_REPLACEMENTS: Array<[RegExp, string]> = [
|
const SENSITIVE_TEXT_REPLACEMENTS: Array<[RegExp, string]> = [
|
||||||
[
|
[
|
||||||
/data:[a-z0-9.+-]+\/[a-z0-9.+-]+;base64,[a-z0-9+/=\r\n]+/gi,
|
/data:[a-z0-9.+-]+\/[a-z0-9.+-]+;base64,[a-z0-9+/=\r\n]+/gi,
|
||||||
@ -18,6 +18,21 @@ const SENSITIVE_TEXT_REPLACEMENTS: Array<[RegExp, string]> = [
|
|||||||
[/\b[A-Za-z0-9+/]{120,}={0,2}\b/g, REDACTED_BASE64_VALUE],
|
[/\b[A-Za-z0-9+/]{120,}={0,2}\b/g, REDACTED_BASE64_VALUE],
|
||||||
[/\b(Authorization)\s*[:=]\s*[^\r\n]+/gi, '$1=<redacted>'],
|
[/\b(Authorization)\s*[:=]\s*[^\r\n]+/gi, '$1=<redacted>'],
|
||||||
[/\b(Cookie)\s*[:=]\s*[^\r\n]+/gi, '$1=<redacted>'],
|
[/\b(Cookie)\s*[:=]\s*[^\r\n]+/gi, '$1=<redacted>'],
|
||||||
|
[
|
||||||
|
/\b((?:private|ssh)[_-]?key)(\s*[:=]\s*)-----BEGIN[\s\S]*?-----END [^-]+-----/gi,
|
||||||
|
'$1$2<redacted>',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
/\b((?:private|ssh)[_-]?key)(\s*[:=]\s*)-----BEGIN[\s\S]*?(?=\s+[A-Za-z0-9_-]+\s*[:=]|\s*$)/gi,
|
||||||
|
'$1$2<redacted>',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
new RegExp(
|
||||||
|
`\\b(${SENSITIVE_TEXT_KEY_PATTERN})(\\s*[:=]\\s*)Bearer\\s+[^\\s,;&]+`,
|
||||||
|
'gi',
|
||||||
|
),
|
||||||
|
'$1$2<redacted>',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
new RegExp(
|
new RegExp(
|
||||||
`(["'])(${SENSITIVE_TEXT_KEY_PATTERN})\\1\\s*:\\s*(?:(["'])[^"']*\\3|[-+]?\\d+(?:\\.\\d+)?|true|false|null)`,
|
`(["'])(${SENSITIVE_TEXT_KEY_PATTERN})\\1\\s*:\\s*(?:(["'])[^"']*\\3|[-+]?\\d+(?:\\.\\d+)?|true|false|null)`,
|
||||||
|
|||||||
@ -279,4 +279,36 @@ describe('RuntimeEvidenceService', () => {
|
|||||||
expect(serialized).not.toContain('67890');
|
expect(serialized).not.toContain('67890');
|
||||||
expect(serialized).toContain('KT_SCAN_SAFE');
|
expect(serialized).toContain('KT_SCAN_SAFE');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('redacts access key and api key text plus unquoted multi-word secret values', () => {
|
||||||
|
const service = new RuntimeEvidenceService();
|
||||||
|
|
||||||
|
const record = service.createRecord({
|
||||||
|
title: 'access key evidence',
|
||||||
|
taskType: 'backend',
|
||||||
|
project: 'kt-template-online-api',
|
||||||
|
environment: 'local',
|
||||||
|
operation: 'runtime-evidence',
|
||||||
|
status: 'failed',
|
||||||
|
details: {
|
||||||
|
text:
|
||||||
|
'accessKey=raw-access-key access_key=raw-snake-access-key apiKey=raw-api-key api_key=raw-snake-api-key private_key=-----BEGIN PRIVATE KEY----- raw unquoted pem token=Bearer raw-bearer-token safe=value',
|
||||||
|
jsonText:
|
||||||
|
'{"accessKey":"raw-json-access-key","api_key":"raw-json-api-key","safe":"kept"}',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const serialized = JSON.stringify(record);
|
||||||
|
|
||||||
|
expect(serialized).not.toContain('raw-access-key');
|
||||||
|
expect(serialized).not.toContain('raw-snake-access-key');
|
||||||
|
expect(serialized).not.toContain('raw-api-key');
|
||||||
|
expect(serialized).not.toContain('raw-snake-api-key');
|
||||||
|
expect(serialized).not.toContain('raw unquoted pem');
|
||||||
|
expect(serialized).not.toContain('raw-bearer-token');
|
||||||
|
expect(serialized).not.toContain('raw-json-access-key');
|
||||||
|
expect(serialized).not.toContain('raw-json-api-key');
|
||||||
|
expect(serialized).toContain('safe=value');
|
||||||
|
expect(serialized).toContain('kept');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user