@token-security/clawdit 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/LICENSE +21 -0
- package/README.md +197 -0
- package/dist/checks/auth-001-device-auth-disabled.d.ts +10 -0
- package/dist/checks/auth-001-device-auth-disabled.js +34 -0
- package/dist/checks/auth-002-insecure-fallback.d.ts +10 -0
- package/dist/checks/auth-002-insecure-fallback.js +34 -0
- package/dist/checks/auth-003-no-gateway-auth.d.ts +10 -0
- package/dist/checks/auth-003-no-gateway-auth.js +40 -0
- package/dist/checks/auth-004-public-trusted-proxies.d.ts +10 -0
- package/dist/checks/auth-004-public-trusted-proxies.js +37 -0
- package/dist/checks/auth-005-hooks-no-token.d.ts +10 -0
- package/dist/checks/auth-005-hooks-no-token.js +42 -0
- package/dist/checks/auth-006-pairing-exposed.d.ts +10 -0
- package/dist/checks/auth-006-pairing-exposed.js +46 -0
- package/dist/checks/auth-007-missing-trusted-proxies.d.ts +11 -0
- package/dist/checks/auth-007-missing-trusted-proxies.js +46 -0
- package/dist/checks/chan-001-open-dm.d.ts +10 -0
- package/dist/checks/chan-001-open-dm.js +48 -0
- package/dist/checks/chan-002-group-policy.d.ts +10 -0
- package/dist/checks/chan-002-group-policy.js +43 -0
- package/dist/checks/chan-003-no-mention.d.ts +10 -0
- package/dist/checks/chan-003-no-mention.js +45 -0
- package/dist/checks/chan-004-dm-isolation.d.ts +10 -0
- package/dist/checks/chan-004-dm-isolation.js +50 -0
- package/dist/checks/chan-005-verbose-groups.d.ts +10 -0
- package/dist/checks/chan-005-verbose-groups.js +53 -0
- package/dist/checks/disc-001-mdns-full.d.ts +10 -0
- package/dist/checks/disc-001-mdns-full.js +34 -0
- package/dist/checks/disc-002-mdns-enabled.d.ts +10 -0
- package/dist/checks/disc-002-mdns-enabled.js +35 -0
- package/dist/checks/exec-001-full-security.d.ts +10 -0
- package/dist/checks/exec-001-full-security.js +34 -0
- package/dist/checks/exec-002-sandbox-disabled.d.ts +10 -0
- package/dist/checks/exec-002-sandbox-disabled.js +34 -0
- package/dist/checks/exec-003-elevated-unrestricted.d.ts +10 -0
- package/dist/checks/exec-003-elevated-unrestricted.js +38 -0
- package/dist/checks/exec-004-approval-fallback.d.ts +10 -0
- package/dist/checks/exec-004-approval-fallback.js +50 -0
- package/dist/checks/exec-005-sandbox-non-main.d.ts +10 -0
- package/dist/checks/exec-005-sandbox-non-main.js +34 -0
- package/dist/checks/exec-006-cross-agent-sandbox.d.ts +10 -0
- package/dist/checks/exec-006-cross-agent-sandbox.js +34 -0
- package/dist/checks/exec-007-workspace-rw.d.ts +10 -0
- package/dist/checks/exec-007-workspace-rw.js +34 -0
- package/dist/checks/index.d.ts +16 -0
- package/dist/checks/index.js +94 -0
- package/dist/checks/loader.d.ts +38 -0
- package/dist/checks/loader.js +149 -0
- package/dist/checks/model-001-weak-model-tools.d.ts +10 -0
- package/dist/checks/model-001-weak-model-tools.js +68 -0
- package/dist/checks/net-001-gateway-binding.d.ts +10 -0
- package/dist/checks/net-001-gateway-binding.js +34 -0
- package/dist/checks/net-002-default-port.d.ts +10 -0
- package/dist/checks/net-002-default-port.js +35 -0
- package/dist/checks/net-003-tailnet-no-token.d.ts +10 -0
- package/dist/checks/net-003-tailnet-no-token.js +34 -0
- package/dist/checks/plug-001-no-allowlist.d.ts +10 -0
- package/dist/checks/plug-001-no-allowlist.js +52 -0
- package/dist/checks/plug-002-extensions-exposed.d.ts +10 -0
- package/dist/checks/plug-002-extensions-exposed.js +41 -0
- package/dist/checks/runner.d.ts +14 -0
- package/dist/checks/runner.js +72 -0
- package/dist/checks/schema.d.ts +54 -0
- package/dist/checks/schema.js +171 -0
- package/dist/checks/sec-001-hardcoded-keys.d.ts +10 -0
- package/dist/checks/sec-001-hardcoded-keys.js +34 -0
- package/dist/checks/sec-002-world-readable-config.d.ts +10 -0
- package/dist/checks/sec-002-world-readable-config.js +39 -0
- package/dist/checks/sec-003-credentials-exposed.d.ts +10 -0
- package/dist/checks/sec-003-credentials-exposed.js +41 -0
- package/dist/checks/sec-004-env-readable.d.ts +10 -0
- package/dist/checks/sec-004-env-readable.js +40 -0
- package/dist/checks/sec-005-transcripts-exposed.d.ts +11 -0
- package/dist/checks/sec-005-transcripts-exposed.js +62 -0
- package/dist/checks/sec-006-redaction-disabled.d.ts +10 -0
- package/dist/checks/sec-006-redaction-disabled.js +34 -0
- package/dist/checks/sec-007-no-redact-patterns.d.ts +10 -0
- package/dist/checks/sec-007-no-redact-patterns.js +39 -0
- package/dist/checks/sec-008-state-dir-permissions.d.ts +10 -0
- package/dist/checks/sec-008-state-dir-permissions.js +49 -0
- package/dist/checks/types.d.ts +45 -0
- package/dist/checks/types.js +2 -0
- package/dist/clawdit-output.schema.json +162 -0
- package/dist/cli.d.ts +23 -0
- package/dist/cli.js +132 -0
- package/dist/config.d.ts +22 -0
- package/dist/config.js +150 -0
- package/dist/formatter.d.ts +42 -0
- package/dist/formatter.js +233 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +168 -0
- package/dist/utils.d.ts +46 -0
- package/dist/utils.js +146 -0
- package/package.json +48 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EXEC-007: Workspace access too permissive
|
|
3
|
+
*
|
|
4
|
+
* Detects when workspaceAccess is set to 'rw' (read-write),
|
|
5
|
+
* giving agents full write access to the workspace.
|
|
6
|
+
*/
|
|
7
|
+
import { getValueAtPath } from '../utils.js';
|
|
8
|
+
const check = {
|
|
9
|
+
id: 'EXEC-007',
|
|
10
|
+
severity: 'MEDIUM',
|
|
11
|
+
name: 'Workspace access too permissive',
|
|
12
|
+
execute(ctx) {
|
|
13
|
+
const workspaceAccess = getValueAtPath(ctx.config, 'workspaceAccess');
|
|
14
|
+
if (workspaceAccess === 'rw') {
|
|
15
|
+
return [{
|
|
16
|
+
id: 'EXEC-007',
|
|
17
|
+
severity: 'MEDIUM',
|
|
18
|
+
name: 'Workspace access too permissive',
|
|
19
|
+
location: { file: ctx.configPath, path: 'workspaceAccess' },
|
|
20
|
+
currentValue: workspaceAccess,
|
|
21
|
+
expectedValue: 'ro (read-only) or not set',
|
|
22
|
+
risk: 'Agents have read-write access to the workspace. A compromised or misbehaving agent could modify or delete files in the workspace.',
|
|
23
|
+
fix: {
|
|
24
|
+
description: 'Set workspace access to read-only',
|
|
25
|
+
command: `jq '.workspaceAccess = "ro"' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
|
|
26
|
+
},
|
|
27
|
+
references: ['https://docs.openclaw.ai/workspace/security#access-modes'],
|
|
28
|
+
}];
|
|
29
|
+
}
|
|
30
|
+
return [];
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export default check;
|
|
34
|
+
//# sourceMappingURL=exec-007-workspace-rw.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security checks index
|
|
3
|
+
*
|
|
4
|
+
* Exports all security checks for use by the runner.
|
|
5
|
+
* Checks are imported from individual files for modularity.
|
|
6
|
+
*/
|
|
7
|
+
import type { Check } from './types.js';
|
|
8
|
+
export type { Check, Finding, CheckContext, Severity, Location, Fix, Summary, AuditResult } from './types.js';
|
|
9
|
+
export { runChecks, getExitCode, type RunChecksOptions } from './runner.js';
|
|
10
|
+
export { loadChecks, loadChecksWithErrors, discoverCheckFiles } from './loader.js';
|
|
11
|
+
export { validateCheck, validateCheckId, validateFilename, assertValidCheck, VALID_CATEGORIES, VALID_SEVERITIES, type ValidationError, type ValidationResult, } from './schema.js';
|
|
12
|
+
/**
|
|
13
|
+
* All security checks, sorted by ID
|
|
14
|
+
*/
|
|
15
|
+
export declare const checks: Check[];
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security checks index
|
|
3
|
+
*
|
|
4
|
+
* Exports all security checks for use by the runner.
|
|
5
|
+
* Checks are imported from individual files for modularity.
|
|
6
|
+
*/
|
|
7
|
+
export { runChecks, getExitCode } from './runner.js';
|
|
8
|
+
export { loadChecks, loadChecksWithErrors, discoverCheckFiles } from './loader.js';
|
|
9
|
+
export { validateCheck, validateCheckId, validateFilename, assertValidCheck, VALID_CATEGORIES, VALID_SEVERITIES, } from './schema.js';
|
|
10
|
+
// Import individual checks
|
|
11
|
+
import net001 from './net-001-gateway-binding.js';
|
|
12
|
+
import net002 from './net-002-default-port.js';
|
|
13
|
+
import net003 from './net-003-tailnet-no-token.js';
|
|
14
|
+
import auth001 from './auth-001-device-auth-disabled.js';
|
|
15
|
+
import auth002 from './auth-002-insecure-fallback.js';
|
|
16
|
+
import auth003 from './auth-003-no-gateway-auth.js';
|
|
17
|
+
import auth004 from './auth-004-public-trusted-proxies.js';
|
|
18
|
+
import auth005 from './auth-005-hooks-no-token.js';
|
|
19
|
+
import auth006 from './auth-006-pairing-exposed.js';
|
|
20
|
+
import auth007 from './auth-007-missing-trusted-proxies.js';
|
|
21
|
+
import exec001 from './exec-001-full-security.js';
|
|
22
|
+
import exec002 from './exec-002-sandbox-disabled.js';
|
|
23
|
+
import exec003 from './exec-003-elevated-unrestricted.js';
|
|
24
|
+
import exec004 from './exec-004-approval-fallback.js';
|
|
25
|
+
import exec005 from './exec-005-sandbox-non-main.js';
|
|
26
|
+
import exec006 from './exec-006-cross-agent-sandbox.js';
|
|
27
|
+
import exec007 from './exec-007-workspace-rw.js';
|
|
28
|
+
import sec001 from './sec-001-hardcoded-keys.js';
|
|
29
|
+
import sec002 from './sec-002-world-readable-config.js';
|
|
30
|
+
import sec003 from './sec-003-credentials-exposed.js';
|
|
31
|
+
import sec004 from './sec-004-env-readable.js';
|
|
32
|
+
import sec005 from './sec-005-transcripts-exposed.js';
|
|
33
|
+
import sec006 from './sec-006-redaction-disabled.js';
|
|
34
|
+
import sec007 from './sec-007-no-redact-patterns.js';
|
|
35
|
+
import sec008 from './sec-008-state-dir-permissions.js';
|
|
36
|
+
import disc001 from './disc-001-mdns-full.js';
|
|
37
|
+
import disc002 from './disc-002-mdns-enabled.js';
|
|
38
|
+
import chan001 from './chan-001-open-dm.js';
|
|
39
|
+
import chan002 from './chan-002-group-policy.js';
|
|
40
|
+
import chan003 from './chan-003-no-mention.js';
|
|
41
|
+
import chan004 from './chan-004-dm-isolation.js';
|
|
42
|
+
import chan005 from './chan-005-verbose-groups.js';
|
|
43
|
+
import model001 from './model-001-weak-model-tools.js';
|
|
44
|
+
import plug001 from './plug-001-no-allowlist.js';
|
|
45
|
+
import plug002 from './plug-002-extensions-exposed.js';
|
|
46
|
+
/**
|
|
47
|
+
* All security checks, sorted by ID
|
|
48
|
+
*/
|
|
49
|
+
export const checks = [
|
|
50
|
+
// Network checks
|
|
51
|
+
net001,
|
|
52
|
+
net002,
|
|
53
|
+
net003,
|
|
54
|
+
// Authentication checks
|
|
55
|
+
auth001,
|
|
56
|
+
auth002,
|
|
57
|
+
auth003,
|
|
58
|
+
auth004,
|
|
59
|
+
auth005,
|
|
60
|
+
auth006,
|
|
61
|
+
auth007,
|
|
62
|
+
// Execution security checks
|
|
63
|
+
exec001,
|
|
64
|
+
exec002,
|
|
65
|
+
exec003,
|
|
66
|
+
exec004,
|
|
67
|
+
exec005,
|
|
68
|
+
exec006,
|
|
69
|
+
exec007,
|
|
70
|
+
// Secrets and credentials checks
|
|
71
|
+
sec001,
|
|
72
|
+
sec002,
|
|
73
|
+
sec003,
|
|
74
|
+
sec004,
|
|
75
|
+
sec005,
|
|
76
|
+
sec006,
|
|
77
|
+
sec007,
|
|
78
|
+
sec008,
|
|
79
|
+
// Discovery checks
|
|
80
|
+
disc001,
|
|
81
|
+
disc002,
|
|
82
|
+
// Channel checks
|
|
83
|
+
chan001,
|
|
84
|
+
chan002,
|
|
85
|
+
chan003,
|
|
86
|
+
chan004,
|
|
87
|
+
chan005,
|
|
88
|
+
// Model checks
|
|
89
|
+
model001,
|
|
90
|
+
// Plugin checks
|
|
91
|
+
plug001,
|
|
92
|
+
plug002,
|
|
93
|
+
];
|
|
94
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check auto-discovery and loading
|
|
3
|
+
*
|
|
4
|
+
* Discovers all check files matching the pattern {category}-{nnn}-{desc}.ts
|
|
5
|
+
* and loads them as Check objects. Validates each check using the schema.
|
|
6
|
+
*/
|
|
7
|
+
import type { Check } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Discover all check files in the checks directory
|
|
10
|
+
* Returns filenames sorted alphabetically
|
|
11
|
+
*/
|
|
12
|
+
export declare function discoverCheckFiles(): string[];
|
|
13
|
+
/**
|
|
14
|
+
* Sort checks by category and number
|
|
15
|
+
*/
|
|
16
|
+
export declare function sortChecks(checks: Check[]): Check[];
|
|
17
|
+
/**
|
|
18
|
+
* Load all checks from the checks directory
|
|
19
|
+
*
|
|
20
|
+
* This is the async version that dynamically imports check files.
|
|
21
|
+
* For sync usage, import checks directly from index.ts.
|
|
22
|
+
*
|
|
23
|
+
* @returns Array of validated Check objects, sorted by ID
|
|
24
|
+
*/
|
|
25
|
+
export declare function loadChecks(): Promise<Check[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Load checks with detailed error reporting
|
|
28
|
+
*
|
|
29
|
+
* @returns Object with loaded checks and any errors encountered
|
|
30
|
+
*/
|
|
31
|
+
export declare function loadChecksWithErrors(): Promise<{
|
|
32
|
+
checks: Check[];
|
|
33
|
+
errors: Array<{
|
|
34
|
+
file: string;
|
|
35
|
+
error: string;
|
|
36
|
+
}>;
|
|
37
|
+
}>;
|
|
38
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check auto-discovery and loading
|
|
3
|
+
*
|
|
4
|
+
* Discovers all check files matching the pattern {category}-{nnn}-{desc}.ts
|
|
5
|
+
* and loads them as Check objects. Validates each check using the schema.
|
|
6
|
+
*/
|
|
7
|
+
import { readdirSync } from 'node:fs';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { assertValidCheck, getCategoryFromId, getNumberFromId } from './schema.js';
|
|
11
|
+
/**
|
|
12
|
+
* Pattern for check files: category-number-description.ts
|
|
13
|
+
* Examples: net-001-gateway-binding.ts, auth-002-insecure-fallback.ts
|
|
14
|
+
*/
|
|
15
|
+
const CHECK_FILE_PATTERN = /^([a-z]+)-(\d{3})-[a-z0-9-]+\.ts$/;
|
|
16
|
+
/**
|
|
17
|
+
* Files to exclude from check discovery
|
|
18
|
+
*/
|
|
19
|
+
const EXCLUDE_FILES = [
|
|
20
|
+
'index.ts',
|
|
21
|
+
'types.ts',
|
|
22
|
+
'runner.ts',
|
|
23
|
+
'loader.ts',
|
|
24
|
+
'schema.ts',
|
|
25
|
+
'_template.ts',
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* File patterns to exclude (tests, templates)
|
|
29
|
+
*/
|
|
30
|
+
const EXCLUDE_PATTERNS = [
|
|
31
|
+
/\.test\.ts$/, // Test files
|
|
32
|
+
/^_/, // Files starting with underscore
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* Check if a filename should be excluded from loading
|
|
36
|
+
*/
|
|
37
|
+
function shouldExclude(filename) {
|
|
38
|
+
if (EXCLUDE_FILES.includes(filename))
|
|
39
|
+
return true;
|
|
40
|
+
for (const pattern of EXCLUDE_PATTERNS) {
|
|
41
|
+
if (pattern.test(filename))
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if a filename matches the check file pattern
|
|
48
|
+
*/
|
|
49
|
+
function isCheckFile(filename) {
|
|
50
|
+
if (shouldExclude(filename))
|
|
51
|
+
return false;
|
|
52
|
+
return CHECK_FILE_PATTERN.test(filename);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get the directory where checks are located
|
|
56
|
+
*/
|
|
57
|
+
function getChecksDir() {
|
|
58
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
59
|
+
return dirname(__filename);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Discover all check files in the checks directory
|
|
63
|
+
* Returns filenames sorted alphabetically
|
|
64
|
+
*/
|
|
65
|
+
export function discoverCheckFiles() {
|
|
66
|
+
const checksDir = getChecksDir();
|
|
67
|
+
try {
|
|
68
|
+
const files = readdirSync(checksDir);
|
|
69
|
+
return files
|
|
70
|
+
.filter(isCheckFile)
|
|
71
|
+
.sort((a, b) => a.localeCompare(b));
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Sort checks by category and number
|
|
79
|
+
*/
|
|
80
|
+
export function sortChecks(checks) {
|
|
81
|
+
return [...checks].sort((a, b) => {
|
|
82
|
+
const catA = getCategoryFromId(a.id) ?? '';
|
|
83
|
+
const catB = getCategoryFromId(b.id) ?? '';
|
|
84
|
+
if (catA !== catB)
|
|
85
|
+
return catA.localeCompare(catB);
|
|
86
|
+
const numA = getNumberFromId(a.id) ?? '';
|
|
87
|
+
const numB = getNumberFromId(b.id) ?? '';
|
|
88
|
+
return numA.localeCompare(numB);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Load a single check from a file
|
|
93
|
+
*/
|
|
94
|
+
async function loadCheckFile(filename) {
|
|
95
|
+
const checksDir = getChecksDir();
|
|
96
|
+
const filePath = join(checksDir, filename);
|
|
97
|
+
// Import expects .js extension for compiled files
|
|
98
|
+
const jsPath = filePath.replace(/\.ts$/, '.js');
|
|
99
|
+
try {
|
|
100
|
+
const module = await import(jsPath);
|
|
101
|
+
const check = module.default;
|
|
102
|
+
// Validate the loaded check
|
|
103
|
+
assertValidCheck(check, filename);
|
|
104
|
+
return check;
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
108
|
+
throw new Error(`Failed to load check from ${filename}: ${message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Load all checks from the checks directory
|
|
113
|
+
*
|
|
114
|
+
* This is the async version that dynamically imports check files.
|
|
115
|
+
* For sync usage, import checks directly from index.ts.
|
|
116
|
+
*
|
|
117
|
+
* @returns Array of validated Check objects, sorted by ID
|
|
118
|
+
*/
|
|
119
|
+
export async function loadChecks() {
|
|
120
|
+
const files = discoverCheckFiles();
|
|
121
|
+
const checks = [];
|
|
122
|
+
for (const file of files) {
|
|
123
|
+
const check = await loadCheckFile(file);
|
|
124
|
+
checks.push(check);
|
|
125
|
+
}
|
|
126
|
+
return sortChecks(checks);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Load checks with detailed error reporting
|
|
130
|
+
*
|
|
131
|
+
* @returns Object with loaded checks and any errors encountered
|
|
132
|
+
*/
|
|
133
|
+
export async function loadChecksWithErrors() {
|
|
134
|
+
const files = discoverCheckFiles();
|
|
135
|
+
const checks = [];
|
|
136
|
+
const errors = [];
|
|
137
|
+
for (const file of files) {
|
|
138
|
+
try {
|
|
139
|
+
const check = await loadCheckFile(file);
|
|
140
|
+
checks.push(check);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
144
|
+
errors.push({ file, error: message });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return { checks: sortChecks(checks), errors };
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MODEL-001: Weak model with tools enabled
|
|
3
|
+
*
|
|
4
|
+
* Detects when a weak-tier model is configured with tool execution enabled,
|
|
5
|
+
* which may not have adequate safety guardrails for tool use.
|
|
6
|
+
*/
|
|
7
|
+
import type { Check } from './types.js';
|
|
8
|
+
declare const check: Check;
|
|
9
|
+
export default check;
|
|
10
|
+
//# sourceMappingURL=model-001-weak-model-tools.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MODEL-001: Weak model with tools enabled
|
|
3
|
+
*
|
|
4
|
+
* Detects when a weak-tier model is configured with tool execution enabled,
|
|
5
|
+
* which may not have adequate safety guardrails for tool use.
|
|
6
|
+
*/
|
|
7
|
+
import { getValueAtPath } from '../utils.js';
|
|
8
|
+
/**
|
|
9
|
+
* Models considered "weak tier" - smaller models with potentially
|
|
10
|
+
* less robust safety guardrails for tool use.
|
|
11
|
+
*/
|
|
12
|
+
const WEAK_TIER_MODELS = [
|
|
13
|
+
'claude-instant',
|
|
14
|
+
'claude-instant-1',
|
|
15
|
+
'claude-instant-1.2',
|
|
16
|
+
'gpt-3.5-turbo',
|
|
17
|
+
'gpt-3.5-turbo-16k',
|
|
18
|
+
'gpt-3.5-turbo-instruct',
|
|
19
|
+
'text-davinci-003',
|
|
20
|
+
'text-davinci-002',
|
|
21
|
+
'mistral-tiny',
|
|
22
|
+
'mistral-small',
|
|
23
|
+
'open-mistral-7b',
|
|
24
|
+
'open-mixtral-8x7b',
|
|
25
|
+
'llama-2-7b',
|
|
26
|
+
'llama-2-13b',
|
|
27
|
+
'codellama-7b',
|
|
28
|
+
'codellama-13b',
|
|
29
|
+
];
|
|
30
|
+
const check = {
|
|
31
|
+
id: 'MODEL-001',
|
|
32
|
+
severity: 'LOW',
|
|
33
|
+
name: 'Weak model with tools enabled',
|
|
34
|
+
execute(ctx) {
|
|
35
|
+
const model = getValueAtPath(ctx.config, 'model');
|
|
36
|
+
const toolsExec = getValueAtPath(ctx.config, 'tools.exec');
|
|
37
|
+
if (!model)
|
|
38
|
+
return [];
|
|
39
|
+
// Check if model is weak tier (case-insensitive, supports prefixes)
|
|
40
|
+
const modelLower = model.toLowerCase();
|
|
41
|
+
const isWeakModel = WEAK_TIER_MODELS.some(weak => modelLower === weak.toLowerCase() ||
|
|
42
|
+
modelLower.startsWith(weak.toLowerCase() + '-'));
|
|
43
|
+
// Check if tools.exec is enabled
|
|
44
|
+
const toolsEnabled = toolsExec !== undefined &&
|
|
45
|
+
toolsExec !== false &&
|
|
46
|
+
toolsExec !== 'off' &&
|
|
47
|
+
toolsExec !== 'disabled';
|
|
48
|
+
if (isWeakModel && toolsEnabled) {
|
|
49
|
+
return [{
|
|
50
|
+
id: 'MODEL-001',
|
|
51
|
+
severity: 'LOW',
|
|
52
|
+
name: 'Weak model with tools enabled',
|
|
53
|
+
location: { file: ctx.configPath, path: 'model' },
|
|
54
|
+
currentValue: `model: ${model}, tools.exec: ${toolsExec}`,
|
|
55
|
+
expectedValue: 'A stronger model (claude-3-*, gpt-4-*) when tools are enabled',
|
|
56
|
+
risk: `Smaller models like "${model}" may have weaker safety guardrails for tool execution. This increases the risk of unintended or harmful tool invocations.`,
|
|
57
|
+
fix: {
|
|
58
|
+
description: 'Use a stronger model when tool execution is enabled',
|
|
59
|
+
command: `jq '.model = "claude-3-sonnet-20240229"' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
|
|
60
|
+
},
|
|
61
|
+
references: ['https://docs.openclaw.ai/models/security#tool-safety'],
|
|
62
|
+
}];
|
|
63
|
+
}
|
|
64
|
+
return [];
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
export default check;
|
|
68
|
+
//# sourceMappingURL=model-001-weak-model-tools.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NET-001: Gateway bound to all interfaces
|
|
3
|
+
*
|
|
4
|
+
* Detects when the gateway is bound to 0.0.0.0 or lan without authentication,
|
|
5
|
+
* exposing it to any network interface.
|
|
6
|
+
*/
|
|
7
|
+
import type { Check } from './types.js';
|
|
8
|
+
declare const check: Check;
|
|
9
|
+
export default check;
|
|
10
|
+
//# sourceMappingURL=net-001-gateway-binding.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NET-001: Gateway bound to all interfaces
|
|
3
|
+
*
|
|
4
|
+
* Detects when the gateway is bound to 0.0.0.0 or lan without authentication,
|
|
5
|
+
* exposing it to any network interface.
|
|
6
|
+
*/
|
|
7
|
+
import { getValueAtPath, hasGatewayAuth } from '../utils.js';
|
|
8
|
+
const check = {
|
|
9
|
+
id: 'NET-001',
|
|
10
|
+
severity: 'HIGH',
|
|
11
|
+
name: 'Gateway bound to all interfaces',
|
|
12
|
+
execute(ctx) {
|
|
13
|
+
const bind = getValueAtPath(ctx.config, 'gateway.bind');
|
|
14
|
+
if ((bind === '0.0.0.0' || bind === 'lan') && !hasGatewayAuth(ctx.config)) {
|
|
15
|
+
return [{
|
|
16
|
+
id: 'NET-001',
|
|
17
|
+
severity: 'HIGH',
|
|
18
|
+
name: 'Gateway bound to all interfaces',
|
|
19
|
+
location: { file: ctx.configPath, path: 'gateway.bind' },
|
|
20
|
+
currentValue: bind,
|
|
21
|
+
expectedValue: 'loopback (or configure gateway.auth.token)',
|
|
22
|
+
risk: 'The gateway is accessible from any network interface without authentication. Attackers on the same network can connect and control OpenClaw.',
|
|
23
|
+
fix: {
|
|
24
|
+
description: 'Bind to loopback only or configure authentication',
|
|
25
|
+
command: `jq '.gateway.bind = "loopback"' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
|
|
26
|
+
},
|
|
27
|
+
references: ['https://docs.openclaw.ai/gateway/security#network-binding'],
|
|
28
|
+
}];
|
|
29
|
+
}
|
|
30
|
+
return [];
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export default check;
|
|
34
|
+
//# sourceMappingURL=net-001-gateway-binding.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NET-002: Non-default gateway port
|
|
3
|
+
*
|
|
4
|
+
* Detects when the gateway is using the default port 18789,
|
|
5
|
+
* which is well-known and makes the service easier to discover.
|
|
6
|
+
*/
|
|
7
|
+
import type { Check } from './types.js';
|
|
8
|
+
declare const check: Check;
|
|
9
|
+
export default check;
|
|
10
|
+
//# sourceMappingURL=net-002-default-port.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NET-002: Non-default gateway port
|
|
3
|
+
*
|
|
4
|
+
* Detects when the gateway is using the default port 18789,
|
|
5
|
+
* which is well-known and makes the service easier to discover.
|
|
6
|
+
*/
|
|
7
|
+
import { getValueAtPath } from '../utils.js';
|
|
8
|
+
const check = {
|
|
9
|
+
id: 'NET-002',
|
|
10
|
+
severity: 'MEDIUM',
|
|
11
|
+
name: 'Non-default gateway port',
|
|
12
|
+
execute(ctx) {
|
|
13
|
+
const port = getValueAtPath(ctx.config, 'gateway.port');
|
|
14
|
+
// Default port is 18789 - flag if using default
|
|
15
|
+
if (port === 18789 || port === undefined) {
|
|
16
|
+
return [{
|
|
17
|
+
id: 'NET-002',
|
|
18
|
+
severity: 'MEDIUM',
|
|
19
|
+
name: 'Non-default gateway port',
|
|
20
|
+
location: { file: ctx.configPath, path: 'gateway.port' },
|
|
21
|
+
currentValue: port ?? 18789,
|
|
22
|
+
expectedValue: 'Non-default port (not 18789)',
|
|
23
|
+
risk: 'Using the default port 18789 makes the gateway easier to discover through port scanning. Attackers commonly scan for well-known service ports.',
|
|
24
|
+
fix: {
|
|
25
|
+
description: 'Configure a non-default port',
|
|
26
|
+
command: `jq '.gateway.port = 28789' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
|
|
27
|
+
},
|
|
28
|
+
references: ['https://docs.openclaw.ai/gateway/security#port-configuration'],
|
|
29
|
+
}];
|
|
30
|
+
}
|
|
31
|
+
return [];
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
export default check;
|
|
35
|
+
//# sourceMappingURL=net-002-default-port.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NET-003: Gateway bound to tailnet without token auth
|
|
3
|
+
*
|
|
4
|
+
* Detects when the gateway is bound to a Tailscale network (tailnet)
|
|
5
|
+
* without token authentication, relying solely on Tailscale's network security.
|
|
6
|
+
*/
|
|
7
|
+
import type { Check } from './types.js';
|
|
8
|
+
declare const check: Check;
|
|
9
|
+
export default check;
|
|
10
|
+
//# sourceMappingURL=net-003-tailnet-no-token.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NET-003: Gateway bound to tailnet without token auth
|
|
3
|
+
*
|
|
4
|
+
* Detects when the gateway is bound to a Tailscale network (tailnet)
|
|
5
|
+
* without token authentication, relying solely on Tailscale's network security.
|
|
6
|
+
*/
|
|
7
|
+
import { getValueAtPath, hasGatewayAuth } from '../utils.js';
|
|
8
|
+
const check = {
|
|
9
|
+
id: 'NET-003',
|
|
10
|
+
severity: 'LOW',
|
|
11
|
+
name: 'Gateway bound to tailnet without token auth',
|
|
12
|
+
execute(ctx) {
|
|
13
|
+
const bind = getValueAtPath(ctx.config, 'gateway.bind');
|
|
14
|
+
if (bind === 'tailnet' && !hasGatewayAuth(ctx.config)) {
|
|
15
|
+
return [{
|
|
16
|
+
id: 'NET-003',
|
|
17
|
+
severity: 'LOW',
|
|
18
|
+
name: 'Gateway bound to tailnet without token auth',
|
|
19
|
+
location: { file: ctx.configPath, path: 'gateway.bind' },
|
|
20
|
+
currentValue: bind,
|
|
21
|
+
expectedValue: 'tailnet with gateway.auth.token configured',
|
|
22
|
+
risk: 'While Tailscale provides network-level security, adding token auth provides defense-in-depth. Compromised tailnet devices could access the gateway.',
|
|
23
|
+
fix: {
|
|
24
|
+
description: 'Configure token authentication for additional security',
|
|
25
|
+
command: `jq '.gateway.auth.token = "your-secure-token"' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
|
|
26
|
+
},
|
|
27
|
+
references: ['https://docs.openclaw.ai/gateway/security#tailnet'],
|
|
28
|
+
}];
|
|
29
|
+
}
|
|
30
|
+
return [];
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export default check;
|
|
34
|
+
//# sourceMappingURL=net-003-tailnet-no-token.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLUG-001: Plugins without explicit allowlist
|
|
3
|
+
*
|
|
4
|
+
* Detects when extensions are installed but no plugin allowlist is configured,
|
|
5
|
+
* meaning any extension in the directory will be loaded without explicit approval.
|
|
6
|
+
*/
|
|
7
|
+
import type { Check } from './types.js';
|
|
8
|
+
declare const check: Check;
|
|
9
|
+
export default check;
|
|
10
|
+
//# sourceMappingURL=plug-001-no-allowlist.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLUG-001: Plugins without explicit allowlist
|
|
3
|
+
*
|
|
4
|
+
* Detects when extensions are installed but no plugin allowlist is configured,
|
|
5
|
+
* meaning any extension in the directory will be loaded without explicit approval.
|
|
6
|
+
*/
|
|
7
|
+
import { getValueAtPath } from '../utils.js';
|
|
8
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
const check = {
|
|
11
|
+
id: 'PLUG-001',
|
|
12
|
+
severity: 'MEDIUM',
|
|
13
|
+
name: 'Plugins without explicit allowlist',
|
|
14
|
+
execute(ctx) {
|
|
15
|
+
const extensionsDir = join(ctx.configDir, 'extensions');
|
|
16
|
+
// Skip if extensions directory doesn't exist
|
|
17
|
+
if (!existsSync(extensionsDir))
|
|
18
|
+
return [];
|
|
19
|
+
// Check if directory has any contents
|
|
20
|
+
let hasExtensions = false;
|
|
21
|
+
try {
|
|
22
|
+
const entries = readdirSync(extensionsDir);
|
|
23
|
+
hasExtensions = entries.length > 0;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return []; // Can't read dir, skip
|
|
27
|
+
}
|
|
28
|
+
if (!hasExtensions)
|
|
29
|
+
return [];
|
|
30
|
+
// Extensions exist - check if plugins.allow is configured
|
|
31
|
+
const pluginsAllow = getValueAtPath(ctx.config, 'plugins.allow');
|
|
32
|
+
if (pluginsAllow === undefined) {
|
|
33
|
+
return [{
|
|
34
|
+
id: 'PLUG-001',
|
|
35
|
+
severity: 'MEDIUM',
|
|
36
|
+
name: 'Plugins without explicit allowlist',
|
|
37
|
+
location: { file: ctx.configPath, path: 'plugins.allow' },
|
|
38
|
+
currentValue: 'not set',
|
|
39
|
+
expectedValue: 'Array of allowed plugin names',
|
|
40
|
+
risk: 'Extensions are installed but no allowlist is configured. Any extension in the directory will be loaded without explicit approval.',
|
|
41
|
+
fix: {
|
|
42
|
+
description: 'Configure an explicit plugin allowlist',
|
|
43
|
+
command: `jq '.plugins.allow = ["plugin-name"]' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
|
|
44
|
+
},
|
|
45
|
+
references: ['https://docs.openclaw.ai/plugins/security'],
|
|
46
|
+
}];
|
|
47
|
+
}
|
|
48
|
+
return [];
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
export default check;
|
|
52
|
+
//# sourceMappingURL=plug-001-no-allowlist.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLUG-002: Plugin directory permissions exposed
|
|
3
|
+
*
|
|
4
|
+
* Detects when the extensions directory has permissions that allow other users
|
|
5
|
+
* on the system to access it, potentially installing malicious plugins.
|
|
6
|
+
*/
|
|
7
|
+
import type { Check } from './types.js';
|
|
8
|
+
declare const check: Check;
|
|
9
|
+
export default check;
|
|
10
|
+
//# sourceMappingURL=plug-002-extensions-exposed.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLUG-002: Plugin directory permissions exposed
|
|
3
|
+
*
|
|
4
|
+
* Detects when the extensions directory has permissions that allow other users
|
|
5
|
+
* on the system to access it, potentially installing malicious plugins.
|
|
6
|
+
*/
|
|
7
|
+
import { getFileMode, formatMode } from '../utils.js';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
const check = {
|
|
10
|
+
id: 'PLUG-002',
|
|
11
|
+
severity: 'MEDIUM',
|
|
12
|
+
name: 'Plugin directory permissions exposed',
|
|
13
|
+
execute(ctx) {
|
|
14
|
+
const extensionsDir = join(ctx.configDir, 'extensions');
|
|
15
|
+
const mode = getFileMode(extensionsDir);
|
|
16
|
+
// Skip on Windows or if dir doesn't exist
|
|
17
|
+
if (mode === null)
|
|
18
|
+
return [];
|
|
19
|
+
// Check if group or other has any permissions
|
|
20
|
+
const groupOther = mode & 0o077;
|
|
21
|
+
if (groupOther > 0) {
|
|
22
|
+
return [{
|
|
23
|
+
id: 'PLUG-002',
|
|
24
|
+
severity: 'MEDIUM',
|
|
25
|
+
name: 'Plugin directory permissions exposed',
|
|
26
|
+
location: { file: extensionsDir, path: null },
|
|
27
|
+
currentValue: formatMode(mode),
|
|
28
|
+
expectedValue: '700',
|
|
29
|
+
risk: 'Other users on the system can access the extensions directory, potentially installing malicious plugins or modifying existing ones.',
|
|
30
|
+
fix: {
|
|
31
|
+
description: 'Restrict directory permissions',
|
|
32
|
+
command: `chmod 700 ${extensionsDir}`,
|
|
33
|
+
},
|
|
34
|
+
references: ['https://docs.openclaw.ai/plugins/security'],
|
|
35
|
+
}];
|
|
36
|
+
}
|
|
37
|
+
return [];
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
export default check;
|
|
41
|
+
//# sourceMappingURL=plug-002-extensions-exposed.js.map
|