@panguard-ai/security-hardening 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/dist/audit/audit-logger.d.ts +44 -0
- package/dist/audit/audit-logger.d.ts.map +1 -0
- package/dist/audit/audit-logger.js +94 -0
- package/dist/audit/audit-logger.js.map +1 -0
- package/dist/audit/index.d.ts +9 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +9 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/audit/syslog-adapter.d.ts +48 -0
- package/dist/audit/syslog-adapter.d.ts.map +1 -0
- package/dist/audit/syslog-adapter.js +97 -0
- package/dist/audit/syslog-adapter.js.map +1 -0
- package/dist/credentials/credential-store.d.ts +51 -0
- package/dist/credentials/credential-store.d.ts.map +1 -0
- package/dist/credentials/credential-store.js +183 -0
- package/dist/credentials/credential-store.js.map +1 -0
- package/dist/credentials/index.d.ts +9 -0
- package/dist/credentials/index.d.ts.map +1 -0
- package/dist/credentials/index.js +9 -0
- package/dist/credentials/index.js.map +1 -0
- package/dist/credentials/migration.d.ts +37 -0
- package/dist/credentials/migration.d.ts.map +1 -0
- package/dist/credentials/migration.js +122 -0
- package/dist/credentials/migration.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/permissions/index.d.ts +9 -0
- package/dist/permissions/index.d.ts.map +1 -0
- package/dist/permissions/index.js +8 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/permissions/security-policy.d.ts +74 -0
- package/dist/permissions/security-policy.d.ts.map +1 -0
- package/dist/permissions/security-policy.js +109 -0
- package/dist/permissions/security-policy.js.map +1 -0
- package/dist/sandbox/command-whitelist.d.ts +43 -0
- package/dist/sandbox/command-whitelist.d.ts.map +1 -0
- package/dist/sandbox/command-whitelist.js +84 -0
- package/dist/sandbox/command-whitelist.js.map +1 -0
- package/dist/sandbox/filesystem-guard.d.ts +30 -0
- package/dist/sandbox/filesystem-guard.d.ts.map +1 -0
- package/dist/sandbox/filesystem-guard.js +67 -0
- package/dist/sandbox/filesystem-guard.js.map +1 -0
- package/dist/sandbox/index.d.ts +9 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/dist/sandbox/index.js +9 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/scanner/index.d.ts +8 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +8 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/vulnerability-scanner.d.ts +22 -0
- package/dist/scanner/vulnerability-scanner.d.ts.map +1 -0
- package/dist/scanner/vulnerability-scanner.js +138 -0
- package/dist/scanner/vulnerability-scanner.js.map +1 -0
- package/dist/types.d.ts +143 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/websocket/connection-validator.d.ts +35 -0
- package/dist/websocket/connection-validator.d.ts.map +1 -0
- package/dist/websocket/connection-validator.js +93 -0
- package/dist/websocket/connection-validator.js.map +1 -0
- package/dist/websocket/csrf-token.d.ts +65 -0
- package/dist/websocket/csrf-token.d.ts.map +1 -0
- package/dist/websocket/csrf-token.js +123 -0
- package/dist/websocket/csrf-token.js.map +1 -0
- package/dist/websocket/index.d.ts +13 -0
- package/dist/websocket/index.d.ts.map +1 -0
- package/dist/websocket/index.js +13 -0
- package/dist/websocket/index.js.map +1 -0
- package/dist/websocket/origin-validator.d.ts +28 -0
- package/dist/websocket/origin-validator.d.ts.map +1 -0
- package/dist/websocket/origin-validator.js +60 -0
- package/dist/websocket/origin-validator.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem access control for Skills
|
|
3
|
+
* Skills 的檔案系統存取控制
|
|
4
|
+
*
|
|
5
|
+
* Restricts file access to whitelisted directories only.
|
|
6
|
+
* 限制檔案存取僅限白名單目錄。
|
|
7
|
+
*
|
|
8
|
+
* @module @panguard-ai/security-hardening/sandbox/filesystem-guard
|
|
9
|
+
*/
|
|
10
|
+
import { resolve, normalize } from 'path';
|
|
11
|
+
import { createLogger, validateFilePath } from '@panguard-ai/core';
|
|
12
|
+
const logger = createLogger('sandbox:filesystem-guard');
|
|
13
|
+
/**
|
|
14
|
+
* Check if a file path is within allowed directories
|
|
15
|
+
* 檢查檔案路徑是否在允許的目錄內
|
|
16
|
+
*
|
|
17
|
+
* @param filePath - File path to check / 要檢查的檔案路徑
|
|
18
|
+
* @param allowedDirs - Allowed directories / 允許的目錄
|
|
19
|
+
* @returns true if path is allowed / 路徑被允許則回傳 true
|
|
20
|
+
*/
|
|
21
|
+
export function isPathAllowed(filePath, allowedDirs) {
|
|
22
|
+
if (allowedDirs.length === 0) {
|
|
23
|
+
logger.warn('No allowed directories configured, blocking all access');
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
// Use core's validateFilePath to check for traversal attacks
|
|
28
|
+
const sanitized = validateFilePath(filePath);
|
|
29
|
+
const absolutePath = normalize(resolve(sanitized));
|
|
30
|
+
for (const dir of allowedDirs) {
|
|
31
|
+
const allowedAbsolute = normalize(resolve(dir));
|
|
32
|
+
if (absolutePath.startsWith(allowedAbsolute + '/') || absolutePath === allowedAbsolute) {
|
|
33
|
+
logger.info('Path access allowed', { filePath, allowedDir: dir });
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
logger.warn('Path access denied: not in allowed directories', { filePath });
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
// validateFilePath throws on traversal attempts
|
|
42
|
+
logger.error('Path validation failed (possible traversal attack)', {
|
|
43
|
+
filePath,
|
|
44
|
+
error: String(error),
|
|
45
|
+
});
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a filesystem guard function
|
|
51
|
+
* 建立檔案系統守衛函式
|
|
52
|
+
*
|
|
53
|
+
* Returns a function that throws if a path is not allowed.
|
|
54
|
+
* 回傳一個在路徑不被允許時拋出錯誤的函式。
|
|
55
|
+
*
|
|
56
|
+
* @param allowedDirs - Allowed directories / 允許的目錄
|
|
57
|
+
* @returns Guard function / 守衛函式
|
|
58
|
+
*/
|
|
59
|
+
export function createFilesystemGuard(allowedDirs) {
|
|
60
|
+
return (filePath) => {
|
|
61
|
+
if (!isPathAllowed(filePath, allowedDirs)) {
|
|
62
|
+
throw new Error(`Filesystem access denied: ${filePath} is not in allowed directories / ` +
|
|
63
|
+
`檔案系統存取拒絕:${filePath} 不在允許的目錄中`);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=filesystem-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem-guard.js","sourceRoot":"","sources":["../../src/sandbox/filesystem-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEnE,MAAM,MAAM,GAAG,YAAY,CAAC,0BAA0B,CAAC,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,WAAqB;IACnE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,wDAAwD,CAAC,CAAC;QACtE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC;QACH,6DAA6D;QAC7D,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,YAAY,GAAG,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;QAEnD,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;YAC9B,MAAM,eAAe,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;YAChD,IAAI,YAAY,CAAC,UAAU,CAAC,eAAe,GAAG,GAAG,CAAC,IAAI,YAAY,KAAK,eAAe,EAAE,CAAC;gBACvF,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;gBAClE,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,gDAAgD,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC5E,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,gDAAgD;QAChD,MAAM,CAAC,KAAK,CAAC,oDAAoD,EAAE;YACjE,QAAQ;YACR,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;SACrB,CAAC,CAAC;QACH,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,WAAqB;IACzD,OAAO,CAAC,QAAgB,EAAQ,EAAE;QAChC,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,CAAC;YAC1C,MAAM,IAAI,KAAK,CACb,6BAA6B,QAAQ,mCAAmC;gBACtE,YAAY,QAAQ,WAAW,CAClC,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill sandbox module
|
|
3
|
+
* Skill 沙盒模組
|
|
4
|
+
*
|
|
5
|
+
* @module @panguard-ai/security-hardening/sandbox
|
|
6
|
+
*/
|
|
7
|
+
export { isPathAllowed, createFilesystemGuard } from './filesystem-guard.js';
|
|
8
|
+
export { isCommandAllowed, createCommandValidator, extractBaseCommand, DEFAULT_ALLOWED_COMMANDS, } from './command-whitelist.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill sandbox module
|
|
3
|
+
* Skill 沙盒模組
|
|
4
|
+
*
|
|
5
|
+
* @module @panguard-ai/security-hardening/sandbox
|
|
6
|
+
*/
|
|
7
|
+
export { isPathAllowed, createFilesystemGuard } from './filesystem-guard.js';
|
|
8
|
+
export { isCommandAllowed, createCommandValidator, extractBaseCommand, DEFAULT_ALLOWED_COMMANDS, } from './command-whitelist.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sandbox/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scanner/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security vulnerability scanner
|
|
3
|
+
* 安全漏洞掃描器
|
|
4
|
+
*
|
|
5
|
+
* Detects known vulnerabilities and generates audit reports.
|
|
6
|
+
* 偵測已知漏洞並產生稽核報告。
|
|
7
|
+
*
|
|
8
|
+
* @module @panguard-ai/security-hardening/scanner/vulnerability-scanner
|
|
9
|
+
*/
|
|
10
|
+
import type { SecurityPolicy, SecurityAuditReport } from '../types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Run a complete security audit
|
|
13
|
+
* 執行完整安全稽核
|
|
14
|
+
*
|
|
15
|
+
* Scans for known vulnerabilities and generates a report with risk score.
|
|
16
|
+
* 掃描已知漏洞並產生含風險評分的報告。
|
|
17
|
+
*
|
|
18
|
+
* @param policy - Current security policy (defaults to DEFAULT_SECURITY_POLICY) / 目前的安全政策
|
|
19
|
+
* @returns Security audit report / 安全稽核報告
|
|
20
|
+
*/
|
|
21
|
+
export declare function runSecurityAudit(policy?: SecurityPolicy): SecurityAuditReport;
|
|
22
|
+
//# sourceMappingURL=vulnerability-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vulnerability-scanner.d.ts","sourceRoot":"","sources":["../../src/scanner/vulnerability-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAwB,mBAAmB,EAAE,MAAM,aAAa,CAAC;AA2G7F;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,GAAE,cAAwC,GAC/C,mBAAmB,CAmCrB"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security vulnerability scanner
|
|
3
|
+
* 安全漏洞掃描器
|
|
4
|
+
*
|
|
5
|
+
* Detects known vulnerabilities and generates audit reports.
|
|
6
|
+
* 偵測已知漏洞並產生稽核報告。
|
|
7
|
+
*
|
|
8
|
+
* @module @panguard-ai/security-hardening/scanner/vulnerability-scanner
|
|
9
|
+
*/
|
|
10
|
+
import { createLogger } from '@panguard-ai/core';
|
|
11
|
+
import { DEFAULT_SECURITY_POLICY } from '../permissions/security-policy.js';
|
|
12
|
+
import { scanPlaintextCredentials } from '../credentials/migration.js';
|
|
13
|
+
const logger = createLogger('scanner:vulnerability');
|
|
14
|
+
const HARDENING_VERSION = '0.1.0';
|
|
15
|
+
/** Severity weights for risk score calculation / 嚴重程度權重用於風險評分計算 */
|
|
16
|
+
const SEVERITY_WEIGHTS = {
|
|
17
|
+
info: 0,
|
|
18
|
+
low: 10,
|
|
19
|
+
medium: 25,
|
|
20
|
+
high: 50,
|
|
21
|
+
critical: 100,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Check CVE-2026-25253: WebSocket authentication bypass
|
|
25
|
+
* 檢查 CVE-2026-25253:WebSocket 認證繞過
|
|
26
|
+
*/
|
|
27
|
+
function checkWebSocketAuth(policy) {
|
|
28
|
+
const hasCsrf = policy.requireCsrfToken;
|
|
29
|
+
const hasOriginCheck = policy.allowedOrigins.length > 0;
|
|
30
|
+
const fixed = hasCsrf && hasOriginCheck;
|
|
31
|
+
return {
|
|
32
|
+
id: 'CVE-2026-25253',
|
|
33
|
+
severity: fixed ? 'info' : 'high',
|
|
34
|
+
title: 'WebSocket Cross-Site Authentication Hijacking / WebSocket 跨站認證劫持',
|
|
35
|
+
description: fixed
|
|
36
|
+
? 'CSRF token and Origin validation are enabled. / CSRF token 和 Origin 驗證已啟用。'
|
|
37
|
+
: 'WebSocket connections lack CSRF protection and/or Origin validation. / WebSocket 連線缺少 CSRF 保護和/或 Origin 驗證。',
|
|
38
|
+
component: 'WebSocket Gateway',
|
|
39
|
+
remediation: 'Enable requireCsrfToken and configure allowedOrigins in security policy. / 在安全政策中啟用 requireCsrfToken 並配置 allowedOrigins。',
|
|
40
|
+
fixed,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check for plaintext credential storage
|
|
45
|
+
* 檢查明文憑證儲存
|
|
46
|
+
*/
|
|
47
|
+
function checkCredentialStorage() {
|
|
48
|
+
const plaintext = scanPlaintextCredentials();
|
|
49
|
+
const fixed = plaintext.length === 0;
|
|
50
|
+
return {
|
|
51
|
+
id: 'OCL-SEC-001',
|
|
52
|
+
severity: fixed ? 'info' : 'high',
|
|
53
|
+
title: 'Plaintext Credential Storage / 明文憑證儲存',
|
|
54
|
+
description: fixed
|
|
55
|
+
? 'No plaintext credentials detected. / 未偵測到明文憑證。'
|
|
56
|
+
: `Found ${plaintext.length} plaintext credential file(s). / 發現 ${plaintext.length} 個明文憑證檔案。`,
|
|
57
|
+
component: 'Credential Storage',
|
|
58
|
+
remediation: 'Use EncryptedFileCredentialStore and run credential migration. / 使用 EncryptedFileCredentialStore 並執行憑證遷移。',
|
|
59
|
+
fixed,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check Skill sandbox configuration
|
|
64
|
+
* 檢查 Skill 沙盒配置
|
|
65
|
+
*/
|
|
66
|
+
function checkSkillSandbox(policy) {
|
|
67
|
+
const hasFilesystemRestriction = policy.allowedDirectories.length > 0;
|
|
68
|
+
const hasCommandRestriction = policy.allowedCommands.length > 0;
|
|
69
|
+
const noShell = !policy.allowShellAccess;
|
|
70
|
+
const fixed = noShell && (hasFilesystemRestriction || hasCommandRestriction);
|
|
71
|
+
return {
|
|
72
|
+
id: 'OCL-SEC-002',
|
|
73
|
+
severity: fixed ? 'info' : 'medium',
|
|
74
|
+
title: 'Unrestricted Skill Permissions / 無限制的 Skill 權限',
|
|
75
|
+
description: fixed
|
|
76
|
+
? 'Skill sandbox is properly configured. / Skill 沙盒已正確配置。'
|
|
77
|
+
: 'Skills may have unrestricted filesystem and/or command execution access. / Skills 可能有無限制的檔案系統和/或命令執行存取。',
|
|
78
|
+
component: 'Skill Sandbox',
|
|
79
|
+
remediation: 'Set allowShellAccess=false and configure allowedDirectories/allowedCommands. / 設定 allowShellAccess=false 並配置 allowedDirectories/allowedCommands。',
|
|
80
|
+
fixed,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check audit logging configuration
|
|
85
|
+
* 檢查稽核日誌配置
|
|
86
|
+
*/
|
|
87
|
+
function checkAuditLogging(policy) {
|
|
88
|
+
const fixed = policy.enableAuditLog;
|
|
89
|
+
return {
|
|
90
|
+
id: 'OCL-SEC-003',
|
|
91
|
+
severity: fixed ? 'info' : 'low',
|
|
92
|
+
title: 'Audit Logging Disabled / 稽核日誌停用',
|
|
93
|
+
description: fixed
|
|
94
|
+
? 'Audit logging is enabled. / 稽核日誌已啟用。'
|
|
95
|
+
: 'Security operations are not being audited. / 安全操作未被稽核。',
|
|
96
|
+
component: 'Audit System',
|
|
97
|
+
remediation: 'Set enableAuditLog=true in security policy. / 在安全政策中設定 enableAuditLog=true。',
|
|
98
|
+
fixed,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Run a complete security audit
|
|
103
|
+
* 執行完整安全稽核
|
|
104
|
+
*
|
|
105
|
+
* Scans for known vulnerabilities and generates a report with risk score.
|
|
106
|
+
* 掃描已知漏洞並產生含風險評分的報告。
|
|
107
|
+
*
|
|
108
|
+
* @param policy - Current security policy (defaults to DEFAULT_SECURITY_POLICY) / 目前的安全政策
|
|
109
|
+
* @returns Security audit report / 安全稽核報告
|
|
110
|
+
*/
|
|
111
|
+
export function runSecurityAudit(policy = DEFAULT_SECURITY_POLICY) {
|
|
112
|
+
logger.info('Starting security audit');
|
|
113
|
+
const findings = [
|
|
114
|
+
checkWebSocketAuth(policy),
|
|
115
|
+
checkCredentialStorage(),
|
|
116
|
+
checkSkillSandbox(policy),
|
|
117
|
+
checkAuditLogging(policy),
|
|
118
|
+
];
|
|
119
|
+
// Calculate risk score: sum of unfixed severity weights, capped at 100
|
|
120
|
+
const riskScore = Math.min(100, findings
|
|
121
|
+
.filter((f) => !f.fixed)
|
|
122
|
+
.reduce((sum, f) => sum + (SEVERITY_WEIGHTS[f.severity] ?? 0), 0));
|
|
123
|
+
const recommendations = findings.filter((f) => !f.fixed).map((f) => f.remediation);
|
|
124
|
+
const report = {
|
|
125
|
+
timestamp: new Date().toISOString(),
|
|
126
|
+
version: HARDENING_VERSION,
|
|
127
|
+
findings,
|
|
128
|
+
riskScore,
|
|
129
|
+
recommendations,
|
|
130
|
+
};
|
|
131
|
+
logger.info('Security audit complete', {
|
|
132
|
+
totalFindings: findings.length,
|
|
133
|
+
unfixed: findings.filter((f) => !f.fixed).length,
|
|
134
|
+
riskScore,
|
|
135
|
+
});
|
|
136
|
+
return report;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=vulnerability-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vulnerability-scanner.js","sourceRoot":"","sources":["../../src/scanner/vulnerability-scanner.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security hardening type definitions
|
|
3
|
+
* 安全強化類型定義
|
|
4
|
+
*
|
|
5
|
+
* @module @panguard-ai/security-hardening/types
|
|
6
|
+
*/
|
|
7
|
+
import type { Severity } from '@panguard-ai/core';
|
|
8
|
+
/**
|
|
9
|
+
* Security policy configuration
|
|
10
|
+
* 安全政策配置
|
|
11
|
+
*/
|
|
12
|
+
export interface SecurityPolicy {
|
|
13
|
+
/** Enable shell access / 啟用 shell 存取 */
|
|
14
|
+
allowShellAccess: boolean;
|
|
15
|
+
/** Allowed filesystem directories / 允許的檔案系統目錄 */
|
|
16
|
+
allowedDirectories: string[];
|
|
17
|
+
/** Allowed commands for sandbox / 沙盒允許的命令 */
|
|
18
|
+
allowedCommands: string[];
|
|
19
|
+
/** Require CSRF tokens for WebSocket / WebSocket 要求 CSRF token */
|
|
20
|
+
requireCsrfToken: boolean;
|
|
21
|
+
/** Allowed WebSocket origins / 允許的 WebSocket 來源 */
|
|
22
|
+
allowedOrigins: string[];
|
|
23
|
+
/** Enable audit logging / 啟用稽核日誌 */
|
|
24
|
+
enableAuditLog: boolean;
|
|
25
|
+
/** Syslog server address (optional) / Syslog 伺服器位址(可選) */
|
|
26
|
+
syslogServer?: string;
|
|
27
|
+
/** Syslog server port (optional) / Syslog 伺服器連接埠(可選) */
|
|
28
|
+
syslogPort?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* CSRF token data
|
|
32
|
+
* CSRF token 資料
|
|
33
|
+
*/
|
|
34
|
+
export interface CsrfToken {
|
|
35
|
+
/** Token value / Token 值 */
|
|
36
|
+
token: string;
|
|
37
|
+
/** Session ID / 會話 ID */
|
|
38
|
+
sessionId: string;
|
|
39
|
+
/** Expiration timestamp / 過期時間戳 */
|
|
40
|
+
expiresAt: Date;
|
|
41
|
+
/** Creation timestamp / 建立時間戳 */
|
|
42
|
+
createdAt: Date;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Origin validation configuration
|
|
46
|
+
* Origin 驗證配置
|
|
47
|
+
*/
|
|
48
|
+
export interface OriginConfig {
|
|
49
|
+
/** Explicitly allowed origins / 明確允許的 origins */
|
|
50
|
+
allowedOrigins: string[];
|
|
51
|
+
/** Allow localhost connections / 允許本機連線 */
|
|
52
|
+
allowLocalhost: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Audit event action types
|
|
56
|
+
* 稽核事件操作類型
|
|
57
|
+
*/
|
|
58
|
+
export type AuditAction = 'websocket_connect' | 'credential_access' | 'credential_migrate' | 'file_access' | 'command_execution' | 'policy_check' | 'security_scan';
|
|
59
|
+
/**
|
|
60
|
+
* Audit event for security operations
|
|
61
|
+
* 安全操作的稽核事件
|
|
62
|
+
*/
|
|
63
|
+
export interface AuditEvent {
|
|
64
|
+
/** ISO timestamp / ISO 時間戳 */
|
|
65
|
+
timestamp: string;
|
|
66
|
+
/** Log level / 日誌等級 */
|
|
67
|
+
level: 'info' | 'warn' | 'error';
|
|
68
|
+
/** Action type / 操作類型 */
|
|
69
|
+
action: AuditAction;
|
|
70
|
+
/** Target resource / 目標資源 */
|
|
71
|
+
target: string;
|
|
72
|
+
/** Operation result / 操作結果 */
|
|
73
|
+
result: 'success' | 'failure' | 'blocked';
|
|
74
|
+
/** Module name / 模組名稱 */
|
|
75
|
+
module: string;
|
|
76
|
+
/** Additional context / 額外上下文 */
|
|
77
|
+
context?: Record<string, unknown>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Credential store interface
|
|
81
|
+
* 憑證儲存介面
|
|
82
|
+
*/
|
|
83
|
+
export interface CredentialStore {
|
|
84
|
+
/** Get credential / 取得憑證 */
|
|
85
|
+
get(service: string, account: string): Promise<string | null>;
|
|
86
|
+
/** Set credential / 設定憑證 */
|
|
87
|
+
set(service: string, account: string, password: string): Promise<void>;
|
|
88
|
+
/** Delete credential / 刪除憑證 */
|
|
89
|
+
delete(service: string, account: string): Promise<boolean>;
|
|
90
|
+
/** List accounts for a service / 列出服務的帳號 */
|
|
91
|
+
list(service: string): Promise<string[]>;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Vulnerability finding from security audit
|
|
95
|
+
* 安全稽核的漏洞發現
|
|
96
|
+
*/
|
|
97
|
+
export interface VulnerabilityFinding {
|
|
98
|
+
/** CVE or internal identifier / CVE 或內部識別碼 */
|
|
99
|
+
id: string;
|
|
100
|
+
/** Severity level / 嚴重程度 */
|
|
101
|
+
severity: Severity;
|
|
102
|
+
/** Vulnerability title / 漏洞標題 */
|
|
103
|
+
title: string;
|
|
104
|
+
/** Description / 描述 */
|
|
105
|
+
description: string;
|
|
106
|
+
/** Affected component / 受影響的元件 */
|
|
107
|
+
component: string;
|
|
108
|
+
/** Remediation steps / 修復步驟 */
|
|
109
|
+
remediation: string;
|
|
110
|
+
/** Is fixed / 是否已修復 */
|
|
111
|
+
fixed: boolean;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Security audit report
|
|
115
|
+
* 安全稽核報告
|
|
116
|
+
*/
|
|
117
|
+
export interface SecurityAuditReport {
|
|
118
|
+
/** Scan timestamp / 掃描時間戳 */
|
|
119
|
+
timestamp: string;
|
|
120
|
+
/** Security hardening version / 安全強化版本 */
|
|
121
|
+
version: string;
|
|
122
|
+
/** Vulnerability findings / 漏洞發現 */
|
|
123
|
+
findings: VulnerabilityFinding[];
|
|
124
|
+
/** Overall risk score (0-100) / 整體風險評分(0-100) */
|
|
125
|
+
riskScore: number;
|
|
126
|
+
/** Recommendations / 建議 */
|
|
127
|
+
recommendations: string[];
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Migration report for credential migration
|
|
131
|
+
* 憑證遷移報告
|
|
132
|
+
*/
|
|
133
|
+
export interface MigrationReport {
|
|
134
|
+
/** Number of credentials scanned / 掃描的憑證數量 */
|
|
135
|
+
scanned: number;
|
|
136
|
+
/** Number of credentials migrated / 已遷移的憑證數量 */
|
|
137
|
+
migrated: number;
|
|
138
|
+
/** Number of failed migrations / 遷移失敗的數量 */
|
|
139
|
+
failed: number;
|
|
140
|
+
/** Error messages / 錯誤訊息 */
|
|
141
|
+
errors: string[];
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket connection validation
|
|
3
|
+
* WebSocket 連線驗證
|
|
4
|
+
*
|
|
5
|
+
* Prevents auto-connection to untrusted external URLs (CVE-2026-25253).
|
|
6
|
+
* 防止自動連線到不受信任的外部 URL(CVE-2026-25253)。
|
|
7
|
+
*
|
|
8
|
+
* @module @panguard-ai/security-hardening/websocket/connection-validator
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Validate a gatewayUrl query parameter
|
|
12
|
+
* 驗證 gatewayUrl 查詢參數
|
|
13
|
+
*
|
|
14
|
+
* Prevents automatic connection to external WebSocket URLs.
|
|
15
|
+
* Localhost URLs are always allowed; external URLs require explicit confirmation.
|
|
16
|
+
* 防止自動連線到外部 WebSocket URL。
|
|
17
|
+
* 本機 URL 始終允許;外部 URL 需要明確確認。
|
|
18
|
+
*
|
|
19
|
+
* @param gatewayUrl - Gateway URL from query parameter / 來自查詢參數的 Gateway URL
|
|
20
|
+
* @param confirmCallback - Optional callback to request user confirmation / 可選的使用者確認回呼
|
|
21
|
+
* @returns true if connection is allowed / 連線被允許則回傳 true
|
|
22
|
+
*/
|
|
23
|
+
export declare function validateGatewayUrl(gatewayUrl: string | undefined, confirmCallback?: (url: string) => Promise<boolean>): Promise<boolean>;
|
|
24
|
+
/**
|
|
25
|
+
* Sanitize a WebSocket URL
|
|
26
|
+
* 清理 WebSocket URL
|
|
27
|
+
*
|
|
28
|
+
* Only allows ws:// and wss:// protocols, strips credentials.
|
|
29
|
+
* 僅允許 ws:// 和 wss:// 協定,移除憑證。
|
|
30
|
+
*
|
|
31
|
+
* @param url - Raw URL / 原始 URL
|
|
32
|
+
* @returns Sanitized URL or null if invalid / 清理後的 URL,無效則為 null
|
|
33
|
+
*/
|
|
34
|
+
export declare function sanitizeWebSocketUrl(url: string): string | null;
|
|
35
|
+
//# sourceMappingURL=connection-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-validator.d.ts","sourceRoot":"","sources":["../../src/websocket/connection-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAiBH;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAClD,OAAO,CAAC,OAAO,CAAC,CAkClB;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiB/D"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket connection validation
|
|
3
|
+
* WebSocket 連線驗證
|
|
4
|
+
*
|
|
5
|
+
* Prevents auto-connection to untrusted external URLs (CVE-2026-25253).
|
|
6
|
+
* 防止自動連線到不受信任的外部 URL(CVE-2026-25253)。
|
|
7
|
+
*
|
|
8
|
+
* @module @panguard-ai/security-hardening/websocket/connection-validator
|
|
9
|
+
*/
|
|
10
|
+
import { createLogger } from '@panguard-ai/core';
|
|
11
|
+
const logger = createLogger('websocket:connection-validator');
|
|
12
|
+
/**
|
|
13
|
+
* Check if a URL is a localhost address
|
|
14
|
+
* 檢查 URL 是否為本機位址
|
|
15
|
+
*
|
|
16
|
+
* @param url - URL to check / 要檢查的 URL
|
|
17
|
+
* @returns true if localhost / 為本機則回傳 true
|
|
18
|
+
*/
|
|
19
|
+
function isLocalhostUrl(url) {
|
|
20
|
+
return url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '::1';
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Validate a gatewayUrl query parameter
|
|
24
|
+
* 驗證 gatewayUrl 查詢參數
|
|
25
|
+
*
|
|
26
|
+
* Prevents automatic connection to external WebSocket URLs.
|
|
27
|
+
* Localhost URLs are always allowed; external URLs require explicit confirmation.
|
|
28
|
+
* 防止自動連線到外部 WebSocket URL。
|
|
29
|
+
* 本機 URL 始終允許;外部 URL 需要明確確認。
|
|
30
|
+
*
|
|
31
|
+
* @param gatewayUrl - Gateway URL from query parameter / 來自查詢參數的 Gateway URL
|
|
32
|
+
* @param confirmCallback - Optional callback to request user confirmation / 可選的使用者確認回呼
|
|
33
|
+
* @returns true if connection is allowed / 連線被允許則回傳 true
|
|
34
|
+
*/
|
|
35
|
+
export async function validateGatewayUrl(gatewayUrl, confirmCallback) {
|
|
36
|
+
if (!gatewayUrl) {
|
|
37
|
+
return true; // No URL = use default, always OK
|
|
38
|
+
}
|
|
39
|
+
let parsed;
|
|
40
|
+
try {
|
|
41
|
+
parsed = new URL(gatewayUrl);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
logger.error('Invalid gateway URL format rejected', { gatewayUrl });
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (isLocalhostUrl(parsed)) {
|
|
48
|
+
logger.info('Localhost gateway URL allowed', { gatewayUrl });
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
// External URL detected
|
|
52
|
+
logger.warn('External gateway URL detected', { gatewayUrl });
|
|
53
|
+
if (confirmCallback) {
|
|
54
|
+
const confirmed = await confirmCallback(gatewayUrl);
|
|
55
|
+
if (confirmed) {
|
|
56
|
+
logger.info('User confirmed external gateway connection', { gatewayUrl });
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
logger.warn('User rejected external gateway connection', { gatewayUrl });
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
// No confirmation mechanism - block by default
|
|
63
|
+
logger.error('External gateway URL blocked (no confirmation mechanism)', { gatewayUrl });
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Sanitize a WebSocket URL
|
|
68
|
+
* 清理 WebSocket URL
|
|
69
|
+
*
|
|
70
|
+
* Only allows ws:// and wss:// protocols, strips credentials.
|
|
71
|
+
* 僅允許 ws:// 和 wss:// 協定,移除憑證。
|
|
72
|
+
*
|
|
73
|
+
* @param url - Raw URL / 原始 URL
|
|
74
|
+
* @returns Sanitized URL or null if invalid / 清理後的 URL,無效則為 null
|
|
75
|
+
*/
|
|
76
|
+
export function sanitizeWebSocketUrl(url) {
|
|
77
|
+
let parsed;
|
|
78
|
+
try {
|
|
79
|
+
parsed = new URL(url);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
logger.error('Failed to parse WebSocket URL', { url });
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
if (parsed.protocol !== 'ws:' && parsed.protocol !== 'wss:') {
|
|
86
|
+
logger.warn('Invalid WebSocket protocol rejected', { url, protocol: parsed.protocol });
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
// Reconstruct without credentials
|
|
90
|
+
const sanitized = `${parsed.protocol}//${parsed.host}${parsed.pathname}`;
|
|
91
|
+
return sanitized;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=connection-validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-validator.js","sourceRoot":"","sources":["../../src/websocket/connection-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,MAAM,GAAG,YAAY,CAAC,gCAAgC,CAAC,CAAC;AAE9D;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,GAAQ;IAC9B,OAAO,GAAG,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG,CAAC,QAAQ,KAAK,KAAK,CAAC;AAChG,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,UAA8B,EAC9B,eAAmD;IAEnD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC,CAAC,kCAAkC;IACjD,CAAC;IAED,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,CAAC,KAAK,CAAC,qCAAqC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACpE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,+BAA+B,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wBAAwB;IACxB,MAAM,CAAC,IAAI,CAAC,+BAA+B,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IAE7D,IAAI,eAAe,EAAE,CAAC;QACpB,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,4CAA4C,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;YAC1E,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,2CAA2C,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACzE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,+CAA+C;IAC/C,MAAM,CAAC,KAAK,CAAC,0DAA0D,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IACzF,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,KAAK,KAAK,IAAI,MAAM,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;QAC5D,MAAM,CAAC,IAAI,CAAC,qCAAqC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QACvF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kCAAkC;IAClC,MAAM,SAAS,GAAG,GAAG,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IACzE,OAAO,SAAS,CAAC;AACnB,CAAC"}
|