@moonquake2004/dsh-security 0.1.0
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/README.md +102 -0
- package/package.json +25 -0
- package/src/checks/index.mjs +24 -0
- package/src/checks/sl1-supply-chain.mjs +157 -0
- package/src/checks/sl2-update-integrity.mjs +59 -0
- package/src/checks/sl3-reputation-score.mjs +73 -0
- package/src/checks/sl4-release-compat.mjs +159 -0
- package/src/checks/sp1-dependency-audit.mjs +157 -0
- package/src/checks/sp2-secret-scan.mjs +131 -0
- package/src/checks/sp3-sandbox-consistency.mjs +220 -0
- package/src/checks/sp4-entry-poison.mjs +74 -0
- package/src/checks/sp5-permission-model.mjs +58 -0
- package/src/checks/sp6-vuln-match.mjs +67 -0
- package/src/checks/sr1-sandbox-violation.mjs +177 -0
- package/src/checks/sr2-privilege-escalation.mjs +67 -0
- package/src/checks/sr3-data-exfiltration.mjs +163 -0
- package/src/checks/sr4-isolation-verify.mjs +64 -0
- package/src/checks/ss1-credential-leak.mjs +158 -0
- package/src/checks/ss2-pii-exposure.mjs +70 -0
- package/src/checks/ss3-sensitive-output.mjs +63 -0
- package/src/config.mjs +43 -0
- package/src/index.mjs +49 -0
- package/src/integrations/ecosystem.mjs +95 -0
- package/src/integrations/index.mjs +37 -0
- package/src/integrations/plugin-reducer.mjs +66 -0
- package/src/integrations/poison-guard.mjs +83 -0
- package/src/integrations/sandbox-audit.mjs +68 -0
- package/src/protocol/check.mjs +49 -0
- package/src/protocol/index.mjs +3 -0
- package/src/protocol/phase.mjs +16 -0
- package/src/protocol/severity.mjs +40 -0
- package/src/registry.mjs +122 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-sandbox-audit 集成
|
|
3
|
+
*
|
|
4
|
+
* 通过 Plugin Interface 注册沙箱策略审计检查。
|
|
5
|
+
* 如果 dsh-sandbox-audit 已安装,自动集成到安全检查流程。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
import { Severity } from '../protocol/severity.mjs';
|
|
10
|
+
import { CheckPhase } from '../protocol/phase.mjs';
|
|
11
|
+
|
|
12
|
+
export function isAvailable() {
|
|
13
|
+
try {
|
|
14
|
+
execSync('which dsh-sandbox-audit', { encoding: 'utf8', stdio: 'pipe' });
|
|
15
|
+
return true;
|
|
16
|
+
} catch {
|
|
17
|
+
try {
|
|
18
|
+
execSync('npx dsh-sandbox-audit --version', { encoding: 'utf8', stdio: 'pipe', timeout: 10000 });
|
|
19
|
+
return true;
|
|
20
|
+
} catch { return false; }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function runAudit(profileDir) {
|
|
25
|
+
const id = 'EXT-SA-1';
|
|
26
|
+
|
|
27
|
+
if (!isAvailable()) {
|
|
28
|
+
return { id, ok: true, severity: Severity.MEDIUM, detail: 'dsh-sandbox-audit 未安装,跳过沙箱策略审计' };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const output = execSync(`npx dsh-sandbox-audit "${profileDir}" --json`, {
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
timeout: 60000,
|
|
35
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const result = JSON.parse(output);
|
|
39
|
+
|
|
40
|
+
if (result.clean || result.findings?.length === 0) {
|
|
41
|
+
return { id, ok: true, severity: Severity.MEDIUM, detail: 'dsh-sandbox-audit 审计通过:沙箱策略配置一致' };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const findings = result.findings || [];
|
|
45
|
+
const details = findings.slice(0, 10).map(f => `[${f.severity}] ${f.tool}: ${f.finding}`).join('\n');
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
id,
|
|
49
|
+
ok: false,
|
|
50
|
+
severity: Severity.MEDIUM,
|
|
51
|
+
detail: `dsh-sandbox-audit 检测到 ${findings.length} 个沙箱策略不一致:\n${details}`,
|
|
52
|
+
fix: '参考 dsh-sandbox-audit 文档修复沙箱配置',
|
|
53
|
+
};
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return { id, ok: true, severity: Severity.MEDIUM, detail: `dsh-sandbox-audit 执行失败:${e.message.slice(0, 80)}` };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const sandboxAuditCheck = {
|
|
60
|
+
id: 'EXT-SA-1',
|
|
61
|
+
name: 'sandbox-audit',
|
|
62
|
+
severity: Severity.MEDIUM,
|
|
63
|
+
phase: CheckPhase.POST_INSTALL,
|
|
64
|
+
description: 'dsh-sandbox-audit 沙箱策略审计',
|
|
65
|
+
src: 'external',
|
|
66
|
+
source: 'dsh-sandbox-audit',
|
|
67
|
+
runner: (profileDir) => runAudit(profileDir),
|
|
68
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH Security Framework — Check Protocol 核心类型
|
|
3
|
+
*
|
|
4
|
+
* 安全检查的统一接口,任何工具都可以通过此接口注册检查。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {Object} SecurityCheck
|
|
9
|
+
* @property {string} id - 检查 ID(SP1, SR2, EXT-PG-1 等)
|
|
10
|
+
* @property {string} name - 人类可读名称
|
|
11
|
+
* @property {string} severity - 严重度(critical/high/medium/low/info)
|
|
12
|
+
* @property {string} phase - 检查阶段(pre-install/post-install/runtime/lifecycle)
|
|
13
|
+
* @property {string} description - 描述
|
|
14
|
+
* @property {'builtin'|'external'} src - 内置 or 外部
|
|
15
|
+
* @property {string} [source] - 外部工具名
|
|
16
|
+
* @property {function(): Promise<SecurityCheckResult>} runner - 执行函数
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {Object} SecurityCheckResult
|
|
21
|
+
* @property {string} id - 检查 ID
|
|
22
|
+
* @property {boolean} ok - 是否通过
|
|
23
|
+
* @property {string} severity - 严重度
|
|
24
|
+
* @property {string} detail - 详情
|
|
25
|
+
* @property {string} [fix] - 修复建议
|
|
26
|
+
* @property {string[]} [references] - 相关讨论/漏洞编号
|
|
27
|
+
* @property {any} [evidence] - 原始证据(可选)
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 创建安全检查结果
|
|
32
|
+
*/
|
|
33
|
+
export function createResult(id, ok, severity, detail, fix = undefined, references = [], evidence = undefined) {
|
|
34
|
+
return { id, ok, severity, detail, fix, references, evidence };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 通过结果
|
|
39
|
+
*/
|
|
40
|
+
export function pass(id, severity, detail) {
|
|
41
|
+
return createResult(id, true, severity, detail);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 失败结果
|
|
46
|
+
*/
|
|
47
|
+
export function fail(id, severity, detail, fix, references = []) {
|
|
48
|
+
return createResult(id, false, severity, detail, fix, references);
|
|
49
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH Security Framework — Check Phase 枚举
|
|
3
|
+
*
|
|
4
|
+
* 每个安全检查在插件生命周期的特定阶段运行:
|
|
5
|
+
* PRE_INSTALL — 安装前(静态分析,如投毒扫描)
|
|
6
|
+
* POST_INSTALL — 安装后(配置审计,如密钥扫描)
|
|
7
|
+
* RUNTIME — 运行时(监控,如沙箱逃逸检测)
|
|
8
|
+
* LIFECYCLE — 生命周期(更新/退役,如版本篡改检测)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const CheckPhase = Object.freeze({
|
|
12
|
+
PRE_INSTALL: 'pre-install',
|
|
13
|
+
POST_INSTALL: 'post-install',
|
|
14
|
+
RUNTIME: 'runtime',
|
|
15
|
+
LIFECYCLE: 'lifecycle',
|
|
16
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH Security Framework — Severity 枚举
|
|
3
|
+
*
|
|
4
|
+
* severity 驱动退出码和用户通知:
|
|
5
|
+
* CRITICAL → exit 2(阻断)
|
|
6
|
+
* HIGH → exit 1(警告)
|
|
7
|
+
* MEDIUM/LOW/INFO → exit 0(信息)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const Severity = Object.freeze({
|
|
11
|
+
CRITICAL: 'critical',
|
|
12
|
+
HIGH: 'high',
|
|
13
|
+
MEDIUM: 'medium',
|
|
14
|
+
LOW: 'low',
|
|
15
|
+
INFO: 'info',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/** severity → 退出码映射 */
|
|
19
|
+
export const severityToExitCode = Object.freeze({
|
|
20
|
+
[Severity.CRITICAL]: 2,
|
|
21
|
+
[Severity.HIGH]: 1,
|
|
22
|
+
[Severity.MEDIUM]: 0,
|
|
23
|
+
[Severity.LOW]: 0,
|
|
24
|
+
[Severity.INFO]: 0,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/** severity 排序(高→低) */
|
|
28
|
+
const SEVERITY_ORDER = [Severity.CRITICAL, Severity.HIGH, Severity.MEDIUM, Severity.LOW, Severity.INFO];
|
|
29
|
+
|
|
30
|
+
export function severityGte(a, b) {
|
|
31
|
+
return SEVERITY_ORDER.indexOf(a) <= SEVERITY_ORDER.indexOf(b);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 从一组 severity 中取最高 */
|
|
35
|
+
export function maxSeverity(severities) {
|
|
36
|
+
for (const s of SEVERITY_ORDER) {
|
|
37
|
+
if (severities.includes(s)) return s;
|
|
38
|
+
}
|
|
39
|
+
return Severity.INFO;
|
|
40
|
+
}
|
package/src/registry.mjs
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH Security Framework — Check Registry
|
|
3
|
+
*
|
|
4
|
+
* 管理安全检查的注册、发现和执行。
|
|
5
|
+
* 支持内置检查和外部工具注册的检查。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readdirSync, readFileSync, existsSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { maxSeverity, severityToExitCode } from './protocol/severity.mjs';
|
|
11
|
+
|
|
12
|
+
export class SecurityCheckRegistry {
|
|
13
|
+
constructor() {
|
|
14
|
+
/** @type {Map<string, import('./protocol/check.mjs').SecurityCheck>} */
|
|
15
|
+
this.checks = new Map();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
register(check) { this.checks.set(check.id, check); }
|
|
19
|
+
registerAll(checks) { for (const check of checks) this.register(check); }
|
|
20
|
+
|
|
21
|
+
loadExternalChecks(securityDir) {
|
|
22
|
+
const checksDir = join(securityDir, 'checks.d');
|
|
23
|
+
if (!existsSync(checksDir)) return;
|
|
24
|
+
const files = readdirSync(checksDir).filter(f => f.endsWith('.json'));
|
|
25
|
+
for (const file of files) {
|
|
26
|
+
try {
|
|
27
|
+
const reg = JSON.parse(readFileSync(join(checksDir, file), 'utf8'));
|
|
28
|
+
if (reg.checks && Array.isArray(reg.checks)) {
|
|
29
|
+
for (const ext of reg.checks) {
|
|
30
|
+
this.register({
|
|
31
|
+
id: ext.id, name: ext.name, severity: ext.severity, phase: ext.phase,
|
|
32
|
+
description: ext.description, src: 'external', source: reg.source,
|
|
33
|
+
runner: async () => {
|
|
34
|
+
const { execSync } = await import('node:child_process');
|
|
35
|
+
try {
|
|
36
|
+
const output = execSync(ext.command, { encoding: 'utf8', timeout: 30000 });
|
|
37
|
+
const result = JSON.parse(output);
|
|
38
|
+
return { id: ext.id, ok: result.ok ?? true, severity: ext.severity, detail: result.detail || 'External check completed', fix: result.fix, references: result.references };
|
|
39
|
+
} catch (e) {
|
|
40
|
+
return { id: ext.id, ok: false, severity: ext.severity, detail: `External check failed: ${e.message}` };
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
} catch { /* skip invalid JSON */ }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
getByPhase(phase) { return [...this.checks.values()].filter(c => c.phase === phase); }
|
|
51
|
+
|
|
52
|
+
getBySeverity(minSeverity) {
|
|
53
|
+
const order = ['critical', 'high', 'medium', 'low', 'info'];
|
|
54
|
+
const minIdx = order.indexOf(minSeverity);
|
|
55
|
+
return [...this.checks.values()].filter(c => order.indexOf(c.severity) <= minIdx);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async runAll(contextFn, phase = null) {
|
|
59
|
+
const checks = phase ? this.getByPhase(phase) : [...this.checks.values()];
|
|
60
|
+
const results = [];
|
|
61
|
+
for (const check of checks) {
|
|
62
|
+
try {
|
|
63
|
+
const context = contextFn(check);
|
|
64
|
+
const result = await check.runner(context);
|
|
65
|
+
results.push(result);
|
|
66
|
+
} catch (e) {
|
|
67
|
+
results.push({ id: check.id, ok: false, severity: check.severity, detail: `Check execution failed: ${e.message}` });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const summary = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
|
|
71
|
+
const failedSeverities = [];
|
|
72
|
+
for (const r of results) {
|
|
73
|
+
if (!r.ok && summary[r.severity] !== undefined) {
|
|
74
|
+
summary[r.severity]++;
|
|
75
|
+
failedSeverities.push(r.severity);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const exitCode = failedSeverities.length > 0 ? severityToExitCode[maxSeverity(failedSeverities)] : 0;
|
|
79
|
+
return { results, exitCode, summary };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function createDefaultRegistry(loadExternal = true) {
|
|
84
|
+
const registry = new SecurityCheckRegistry();
|
|
85
|
+
const modules = await Promise.all([
|
|
86
|
+
import('./checks/sp1-dependency-audit.mjs'),
|
|
87
|
+
import('./checks/sp2-secret-scan.mjs'),
|
|
88
|
+
import('./checks/sp3-sandbox-consistency.mjs'),
|
|
89
|
+
import('./checks/sp4-entry-poison.mjs'),
|
|
90
|
+
import('./checks/sp5-permission-model.mjs'),
|
|
91
|
+
import('./checks/sp6-vuln-match.mjs'),
|
|
92
|
+
import('./checks/sr1-sandbox-violation.mjs'),
|
|
93
|
+
import('./checks/sr2-privilege-escalation.mjs'),
|
|
94
|
+
import('./checks/sr3-data-exfiltration.mjs'),
|
|
95
|
+
import('./checks/sr4-isolation-verify.mjs'),
|
|
96
|
+
import('./checks/sl1-supply-chain.mjs'),
|
|
97
|
+
import('./checks/sl2-update-integrity.mjs'),
|
|
98
|
+
import('./checks/sl3-reputation-score.mjs'),
|
|
99
|
+
import('./checks/sl4-release-compat.mjs'),
|
|
100
|
+
import('./checks/ss1-credential-leak.mjs'),
|
|
101
|
+
import('./checks/ss2-pii-exposure.mjs'),
|
|
102
|
+
import('./checks/ss3-sensitive-output.mjs'),
|
|
103
|
+
]);
|
|
104
|
+
for (const mod of modules) {
|
|
105
|
+
for (const val of Object.values(mod)) {
|
|
106
|
+
if (val && typeof val === 'object' && val.id && val.runner) {
|
|
107
|
+
registry.register(val);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 加载外部集成
|
|
113
|
+
if (loadExternal) {
|
|
114
|
+
try {
|
|
115
|
+
const { getAvailableIntegrations } = await import('./integrations/index.mjs');
|
|
116
|
+
const integrations = await getAvailableIntegrations();
|
|
117
|
+
registry.registerAll(integrations);
|
|
118
|
+
} catch { /* 外部集成不可用时静默跳过 */ }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return registry;
|
|
122
|
+
}
|