@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.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +197 -0
  3. package/dist/checks/auth-001-device-auth-disabled.d.ts +10 -0
  4. package/dist/checks/auth-001-device-auth-disabled.js +34 -0
  5. package/dist/checks/auth-002-insecure-fallback.d.ts +10 -0
  6. package/dist/checks/auth-002-insecure-fallback.js +34 -0
  7. package/dist/checks/auth-003-no-gateway-auth.d.ts +10 -0
  8. package/dist/checks/auth-003-no-gateway-auth.js +40 -0
  9. package/dist/checks/auth-004-public-trusted-proxies.d.ts +10 -0
  10. package/dist/checks/auth-004-public-trusted-proxies.js +37 -0
  11. package/dist/checks/auth-005-hooks-no-token.d.ts +10 -0
  12. package/dist/checks/auth-005-hooks-no-token.js +42 -0
  13. package/dist/checks/auth-006-pairing-exposed.d.ts +10 -0
  14. package/dist/checks/auth-006-pairing-exposed.js +46 -0
  15. package/dist/checks/auth-007-missing-trusted-proxies.d.ts +11 -0
  16. package/dist/checks/auth-007-missing-trusted-proxies.js +46 -0
  17. package/dist/checks/chan-001-open-dm.d.ts +10 -0
  18. package/dist/checks/chan-001-open-dm.js +48 -0
  19. package/dist/checks/chan-002-group-policy.d.ts +10 -0
  20. package/dist/checks/chan-002-group-policy.js +43 -0
  21. package/dist/checks/chan-003-no-mention.d.ts +10 -0
  22. package/dist/checks/chan-003-no-mention.js +45 -0
  23. package/dist/checks/chan-004-dm-isolation.d.ts +10 -0
  24. package/dist/checks/chan-004-dm-isolation.js +50 -0
  25. package/dist/checks/chan-005-verbose-groups.d.ts +10 -0
  26. package/dist/checks/chan-005-verbose-groups.js +53 -0
  27. package/dist/checks/disc-001-mdns-full.d.ts +10 -0
  28. package/dist/checks/disc-001-mdns-full.js +34 -0
  29. package/dist/checks/disc-002-mdns-enabled.d.ts +10 -0
  30. package/dist/checks/disc-002-mdns-enabled.js +35 -0
  31. package/dist/checks/exec-001-full-security.d.ts +10 -0
  32. package/dist/checks/exec-001-full-security.js +34 -0
  33. package/dist/checks/exec-002-sandbox-disabled.d.ts +10 -0
  34. package/dist/checks/exec-002-sandbox-disabled.js +34 -0
  35. package/dist/checks/exec-003-elevated-unrestricted.d.ts +10 -0
  36. package/dist/checks/exec-003-elevated-unrestricted.js +38 -0
  37. package/dist/checks/exec-004-approval-fallback.d.ts +10 -0
  38. package/dist/checks/exec-004-approval-fallback.js +50 -0
  39. package/dist/checks/exec-005-sandbox-non-main.d.ts +10 -0
  40. package/dist/checks/exec-005-sandbox-non-main.js +34 -0
  41. package/dist/checks/exec-006-cross-agent-sandbox.d.ts +10 -0
  42. package/dist/checks/exec-006-cross-agent-sandbox.js +34 -0
  43. package/dist/checks/exec-007-workspace-rw.d.ts +10 -0
  44. package/dist/checks/exec-007-workspace-rw.js +34 -0
  45. package/dist/checks/index.d.ts +16 -0
  46. package/dist/checks/index.js +94 -0
  47. package/dist/checks/loader.d.ts +38 -0
  48. package/dist/checks/loader.js +149 -0
  49. package/dist/checks/model-001-weak-model-tools.d.ts +10 -0
  50. package/dist/checks/model-001-weak-model-tools.js +68 -0
  51. package/dist/checks/net-001-gateway-binding.d.ts +10 -0
  52. package/dist/checks/net-001-gateway-binding.js +34 -0
  53. package/dist/checks/net-002-default-port.d.ts +10 -0
  54. package/dist/checks/net-002-default-port.js +35 -0
  55. package/dist/checks/net-003-tailnet-no-token.d.ts +10 -0
  56. package/dist/checks/net-003-tailnet-no-token.js +34 -0
  57. package/dist/checks/plug-001-no-allowlist.d.ts +10 -0
  58. package/dist/checks/plug-001-no-allowlist.js +52 -0
  59. package/dist/checks/plug-002-extensions-exposed.d.ts +10 -0
  60. package/dist/checks/plug-002-extensions-exposed.js +41 -0
  61. package/dist/checks/runner.d.ts +14 -0
  62. package/dist/checks/runner.js +72 -0
  63. package/dist/checks/schema.d.ts +54 -0
  64. package/dist/checks/schema.js +171 -0
  65. package/dist/checks/sec-001-hardcoded-keys.d.ts +10 -0
  66. package/dist/checks/sec-001-hardcoded-keys.js +34 -0
  67. package/dist/checks/sec-002-world-readable-config.d.ts +10 -0
  68. package/dist/checks/sec-002-world-readable-config.js +39 -0
  69. package/dist/checks/sec-003-credentials-exposed.d.ts +10 -0
  70. package/dist/checks/sec-003-credentials-exposed.js +41 -0
  71. package/dist/checks/sec-004-env-readable.d.ts +10 -0
  72. package/dist/checks/sec-004-env-readable.js +40 -0
  73. package/dist/checks/sec-005-transcripts-exposed.d.ts +11 -0
  74. package/dist/checks/sec-005-transcripts-exposed.js +62 -0
  75. package/dist/checks/sec-006-redaction-disabled.d.ts +10 -0
  76. package/dist/checks/sec-006-redaction-disabled.js +34 -0
  77. package/dist/checks/sec-007-no-redact-patterns.d.ts +10 -0
  78. package/dist/checks/sec-007-no-redact-patterns.js +39 -0
  79. package/dist/checks/sec-008-state-dir-permissions.d.ts +10 -0
  80. package/dist/checks/sec-008-state-dir-permissions.js +49 -0
  81. package/dist/checks/types.d.ts +45 -0
  82. package/dist/checks/types.js +2 -0
  83. package/dist/clawdit-output.schema.json +162 -0
  84. package/dist/cli.d.ts +23 -0
  85. package/dist/cli.js +132 -0
  86. package/dist/config.d.ts +22 -0
  87. package/dist/config.js +150 -0
  88. package/dist/formatter.d.ts +42 -0
  89. package/dist/formatter.js +233 -0
  90. package/dist/index.d.ts +3 -0
  91. package/dist/index.js +168 -0
  92. package/dist/utils.d.ts +46 -0
  93. package/dist/utils.js +146 -0
  94. package/package.json +48 -0
@@ -0,0 +1,39 @@
1
+ /**
2
+ * SEC-007: No custom redact patterns
3
+ *
4
+ * Detects when logging.redactPatterns is empty or not configured,
5
+ * relying only on built-in redaction patterns.
6
+ */
7
+ import { getValueAtPath } from '../utils.js';
8
+ const check = {
9
+ id: 'SEC-007',
10
+ severity: 'LOW',
11
+ name: 'No custom redact patterns',
12
+ execute(ctx) {
13
+ const redactPatterns = getValueAtPath(ctx.config, 'logging.redactPatterns');
14
+ const redactSensitive = getValueAtPath(ctx.config, 'logging.redactSensitive');
15
+ // Only flag if redaction is enabled but no custom patterns
16
+ if (redactSensitive === 'off' || redactSensitive === false)
17
+ return [];
18
+ const hasPatterns = Array.isArray(redactPatterns) && redactPatterns.length > 0;
19
+ if (!hasPatterns) {
20
+ return [{
21
+ id: 'SEC-007',
22
+ severity: 'LOW',
23
+ name: 'No custom redact patterns',
24
+ location: { file: ctx.configPath, path: 'logging.redactPatterns' },
25
+ currentValue: redactPatterns ?? 'not set',
26
+ expectedValue: 'Array of regex patterns for sensitive data',
27
+ risk: 'Only built-in redaction patterns are active. Organization-specific sensitive data patterns (internal project names, custom tokens) may not be redacted.',
28
+ fix: {
29
+ description: 'Add custom redaction patterns for organization-specific sensitive data',
30
+ command: `jq '.logging.redactPatterns = ["(?i)internal-project-.*", "(?i)secret-.*"]' ${ctx.configPath} > tmp.json && mv tmp.json ${ctx.configPath}`,
31
+ },
32
+ references: ['https://docs.openclaw.ai/logging/security#custom-redaction'],
33
+ }];
34
+ }
35
+ return [];
36
+ },
37
+ };
38
+ export default check;
39
+ //# sourceMappingURL=sec-007-no-redact-patterns.js.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * SEC-008: State directory has insecure permissions
3
+ *
4
+ * Detects when the OpenClaw state directory (~/.openclaw) exists with
5
+ * permissions that allow other users to read it.
6
+ */
7
+ import type { Check } from './types.js';
8
+ declare const check: Check;
9
+ export default check;
10
+ //# sourceMappingURL=sec-008-state-dir-permissions.d.ts.map
@@ -0,0 +1,49 @@
1
+ /**
2
+ * SEC-008: State directory has insecure permissions
3
+ *
4
+ * Detects when the OpenClaw state directory (~/.openclaw) exists with
5
+ * permissions that allow other users to read it.
6
+ */
7
+ import { existsSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { join } from 'node:path';
10
+ import { getFileMode, formatMode } from '../utils.js';
11
+ const check = {
12
+ id: 'SEC-008',
13
+ severity: 'MEDIUM',
14
+ name: 'State directory has insecure permissions',
15
+ execute(ctx) {
16
+ const stateDir = join(homedir(), '.openclaw');
17
+ // State directory doesn't exist yet - nothing to check
18
+ if (!existsSync(stateDir))
19
+ return [];
20
+ const mode = getFileMode(stateDir);
21
+ // Skip on Windows or if we can't read permissions
22
+ if (mode === null)
23
+ return [];
24
+ // Check if group or other has any permissions
25
+ const groupOther = mode & 0o077;
26
+ if (groupOther !== 0) {
27
+ return [{
28
+ id: 'SEC-008',
29
+ severity: 'MEDIUM',
30
+ name: 'State directory has insecure permissions',
31
+ location: {
32
+ file: stateDir,
33
+ path: null,
34
+ },
35
+ currentValue: formatMode(mode),
36
+ expectedValue: '700',
37
+ risk: 'The state directory may contain sensitive session data, tokens, or transcripts. Group/world-readable permissions expose this data to other users on the system.',
38
+ fix: {
39
+ description: 'Restrict directory permissions',
40
+ command: `chmod 700 ${stateDir}`,
41
+ },
42
+ references: [],
43
+ }];
44
+ }
45
+ return [];
46
+ },
47
+ };
48
+ export default check;
49
+ //# sourceMappingURL=sec-008-state-dir-permissions.js.map
@@ -0,0 +1,45 @@
1
+ export type Severity = 'HIGH' | 'MEDIUM' | 'LOW';
2
+ export interface Location {
3
+ file: string;
4
+ path: string | null;
5
+ }
6
+ export interface Fix {
7
+ description: string;
8
+ command: string;
9
+ }
10
+ export interface Finding {
11
+ id: string;
12
+ severity: Severity;
13
+ name: string;
14
+ location: Location;
15
+ currentValue: unknown;
16
+ expectedValue: unknown;
17
+ risk: string;
18
+ fix: Fix;
19
+ references: string[];
20
+ }
21
+ export interface CheckContext {
22
+ config: Record<string, unknown>;
23
+ configPath: string;
24
+ configDir: string;
25
+ }
26
+ export interface Check {
27
+ id: string;
28
+ severity: Severity;
29
+ name: string;
30
+ execute(ctx: CheckContext): Finding[];
31
+ }
32
+ export interface Summary {
33
+ total: number;
34
+ high: number;
35
+ medium: number;
36
+ low: number;
37
+ passed: number;
38
+ result: 'PASS' | 'FAIL';
39
+ }
40
+ export interface AuditResult {
41
+ findings: Finding[];
42
+ passed: string[];
43
+ summary: Summary;
44
+ }
45
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,162 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://clawdit.dev/schemas/output.schema.json",
4
+ "title": "clawdit Output",
5
+ "description": "JSON output format for clawdit security audit tool",
6
+ "type": "object",
7
+ "required": ["version", "timestamp", "config_path", "summary", "findings", "checks_passed"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "version": {
11
+ "type": "string",
12
+ "pattern": "^\\d+\\.\\d+\\.\\d+$",
13
+ "description": "clawdit version that produced this output"
14
+ },
15
+ "timestamp": {
16
+ "type": "string",
17
+ "format": "date-time",
18
+ "description": "ISO 8601 timestamp when the audit was run"
19
+ },
20
+ "config_path": {
21
+ "type": "string",
22
+ "description": "Path to the configuration file that was audited"
23
+ },
24
+ "summary": {
25
+ "$ref": "#/definitions/Summary"
26
+ },
27
+ "findings": {
28
+ "type": "array",
29
+ "items": {
30
+ "$ref": "#/definitions/Finding"
31
+ },
32
+ "description": "List of security findings (empty if all checks passed)"
33
+ },
34
+ "checks_passed": {
35
+ "type": "array",
36
+ "items": {
37
+ "$ref": "#/definitions/CheckId"
38
+ },
39
+ "description": "List of check IDs that passed"
40
+ }
41
+ },
42
+ "definitions": {
43
+ "Severity": {
44
+ "type": "string",
45
+ "enum": ["HIGH", "MEDIUM", "LOW"],
46
+ "description": "Severity level of a finding"
47
+ },
48
+ "CheckId": {
49
+ "type": "string",
50
+ "pattern": "^[A-Z]{2,5}-\\d{3}$",
51
+ "description": "Check identifier in format CAT-NNN (e.g., NET-001, AUTH-002)"
52
+ },
53
+ "Summary": {
54
+ "type": "object",
55
+ "required": ["total", "high", "medium", "low", "passed", "result"],
56
+ "additionalProperties": false,
57
+ "properties": {
58
+ "total": {
59
+ "type": "integer",
60
+ "minimum": 0,
61
+ "description": "Total number of findings"
62
+ },
63
+ "high": {
64
+ "type": "integer",
65
+ "minimum": 0,
66
+ "description": "Number of HIGH severity findings"
67
+ },
68
+ "medium": {
69
+ "type": "integer",
70
+ "minimum": 0,
71
+ "description": "Number of MEDIUM severity findings"
72
+ },
73
+ "low": {
74
+ "type": "integer",
75
+ "minimum": 0,
76
+ "description": "Number of LOW severity findings"
77
+ },
78
+ "passed": {
79
+ "type": "integer",
80
+ "minimum": 0,
81
+ "description": "Number of checks that passed"
82
+ },
83
+ "result": {
84
+ "type": "string",
85
+ "enum": ["PASS", "FAIL"],
86
+ "description": "Overall audit result"
87
+ }
88
+ }
89
+ },
90
+ "Location": {
91
+ "type": "object",
92
+ "required": ["file", "path"],
93
+ "additionalProperties": false,
94
+ "properties": {
95
+ "file": {
96
+ "type": "string",
97
+ "description": "Path to the configuration file"
98
+ },
99
+ "path": {
100
+ "type": ["string", "null"],
101
+ "description": "JSON path to the problematic value (null if file-level issue)"
102
+ }
103
+ }
104
+ },
105
+ "Fix": {
106
+ "type": "object",
107
+ "required": ["description", "command"],
108
+ "additionalProperties": false,
109
+ "properties": {
110
+ "description": {
111
+ "type": "string",
112
+ "description": "Human-readable description of the fix"
113
+ },
114
+ "command": {
115
+ "type": "string",
116
+ "description": "Command or code snippet to apply the fix"
117
+ }
118
+ }
119
+ },
120
+ "Finding": {
121
+ "type": "object",
122
+ "required": ["id", "severity", "name", "location", "current_value", "expected_value", "risk", "fix", "references"],
123
+ "additionalProperties": false,
124
+ "properties": {
125
+ "id": {
126
+ "$ref": "#/definitions/CheckId"
127
+ },
128
+ "severity": {
129
+ "$ref": "#/definitions/Severity"
130
+ },
131
+ "name": {
132
+ "type": "string",
133
+ "description": "Short description of the security issue"
134
+ },
135
+ "location": {
136
+ "$ref": "#/definitions/Location"
137
+ },
138
+ "current_value": {
139
+ "description": "The current (problematic) value found in the config"
140
+ },
141
+ "expected_value": {
142
+ "description": "The expected (secure) value"
143
+ },
144
+ "risk": {
145
+ "type": "string",
146
+ "description": "Explanation of the security risk"
147
+ },
148
+ "fix": {
149
+ "$ref": "#/definitions/Fix"
150
+ },
151
+ "references": {
152
+ "type": "array",
153
+ "items": {
154
+ "type": "string",
155
+ "format": "uri"
156
+ },
157
+ "description": "URLs to relevant documentation or security resources"
158
+ }
159
+ }
160
+ }
161
+ }
162
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import type { Check, Severity } from './checks/types.js';
2
+ export type OutputFormat = 'text' | 'json' | 'sarif';
3
+ export interface CliOptions {
4
+ configPath?: string;
5
+ outputFile?: string;
6
+ severity: Severity;
7
+ format: OutputFormat;
8
+ noColor: boolean;
9
+ quiet: boolean;
10
+ verbose: boolean;
11
+ version: boolean;
12
+ help: boolean;
13
+ listChecks: boolean;
14
+ checks?: string[];
15
+ skipChecks?: string[];
16
+ }
17
+ export declare function parseCliArgs(args: string[]): CliOptions;
18
+ export declare function validateCheckIds(ids: string[], validIds: string[]): string[];
19
+ export declare function printHelp(): void;
20
+ export declare function printVersion(): void;
21
+ export declare function printListChecksText(checks: Check[], noColor: boolean): void;
22
+ export declare function printListChecksJson(checks: Check[]): void;
23
+ //# sourceMappingURL=cli.d.ts.map
package/dist/cli.js ADDED
@@ -0,0 +1,132 @@
1
+ import { parseArgs } from 'node:util';
2
+ const HELP_TEXT = `
3
+ clawdit - OpenClaw Security Audit Tool
4
+
5
+ Usage: clawdit [OPTIONS] [CONFIG_PATH]
6
+
7
+ ARGUMENTS:
8
+ CONFIG_PATH Path to moltbot.json, or - to read from stdin (default: auto-discovered)
9
+
10
+ OPTIONS:
11
+ -o, --output <FILE> Write output to file instead of stdout
12
+ -s, --severity <LEVEL> Minimum severity to report: low, medium, high (default: low)
13
+ -f, --format <FORMAT> Output format: text, json, sarif (default: text if TTY, json otherwise)
14
+ --checks <IDS> Run only specified checks (comma-separated)
15
+ --skip-checks <IDS> Skip specified checks (comma-separated)
16
+ --no-color Disable colored output
17
+ -q, --quiet Suppress banner and summary; output findings only
18
+ -v, --verbose Show check execution progress
19
+ --list-checks List all available security checks and exit
20
+ --version Print version and exit
21
+ --help Show this help message
22
+
23
+ EXAMPLES:
24
+ clawdit # Auto-discover config, audit with text output
25
+ clawdit ~/.clawdbot/moltbot.json # Audit specific config
26
+ cat config.json | clawdit - # Read config from stdin
27
+ clawdit --severity high # Only report HIGH severity findings
28
+ clawdit -o audit.txt # Write output to file
29
+
30
+ EXIT CODES:
31
+ 0 All checks passed
32
+ 1 HIGH severity findings
33
+ 2 MEDIUM severity findings (no HIGH)
34
+ 3 LOW severity findings only
35
+ 10 Configuration file not found
36
+ 11 Configuration parse error
37
+ 12 Invalid CLI arguments
38
+ 13 Permission denied
39
+ 14 $include directive failed
40
+ 20 Internal error
41
+ `;
42
+ export function parseCliArgs(args) {
43
+ try {
44
+ const { values, positionals } = parseArgs({
45
+ args,
46
+ options: {
47
+ output: { type: 'string', short: 'o' },
48
+ severity: { type: 'string', short: 's', default: 'low' },
49
+ format: { type: 'string', short: 'f' },
50
+ checks: { type: 'string' },
51
+ 'skip-checks': { type: 'string' },
52
+ 'no-color': { type: 'boolean', default: false },
53
+ quiet: { type: 'boolean', short: 'q', default: false },
54
+ verbose: { type: 'boolean', short: 'v', default: false },
55
+ 'list-checks': { type: 'boolean', default: false },
56
+ version: { type: 'boolean', default: false },
57
+ help: { type: 'boolean', default: false },
58
+ },
59
+ allowPositionals: true,
60
+ });
61
+ // Validate severity
62
+ const severityMap = {
63
+ low: 'LOW',
64
+ medium: 'MEDIUM',
65
+ high: 'HIGH',
66
+ };
67
+ const severityInput = values.severity.toLowerCase();
68
+ if (!severityMap[severityInput]) {
69
+ throw new Error(`Invalid severity level: ${values.severity}. Must be: low, medium, high`);
70
+ }
71
+ // Validate format (default to text for TTY, json otherwise)
72
+ const formatInput = values.format
73
+ ? values.format.toLowerCase()
74
+ : (process.stdout.isTTY ? 'text' : 'json');
75
+ if (formatInput !== 'text' && formatInput !== 'json' && formatInput !== 'sarif') {
76
+ throw new Error(`Invalid format: ${values.format}. Must be: text, json, sarif`);
77
+ }
78
+ return {
79
+ configPath: positionals[0],
80
+ outputFile: values.output,
81
+ severity: severityMap[severityInput],
82
+ format: formatInput,
83
+ noColor: values['no-color'] || !process.stdout.isTTY,
84
+ quiet: values.quiet,
85
+ verbose: values.verbose,
86
+ version: values.version,
87
+ help: values.help,
88
+ listChecks: values['list-checks'],
89
+ checks: values.checks
90
+ ? values.checks.split(',').map(s => s.trim().toUpperCase())
91
+ : undefined,
92
+ skipChecks: values['skip-checks']
93
+ ? values['skip-checks'].split(',').map(s => s.trim().toUpperCase())
94
+ : undefined,
95
+ };
96
+ }
97
+ catch (error) {
98
+ const err = error;
99
+ console.error(`Error: ${err.message}`);
100
+ console.error('Use --help for usage information');
101
+ process.exit(12);
102
+ }
103
+ }
104
+ export function validateCheckIds(ids, validIds) {
105
+ const validSet = new Set(validIds);
106
+ return ids.filter(id => !validSet.has(id));
107
+ }
108
+ export function printHelp() {
109
+ console.log(HELP_TEXT.trim());
110
+ }
111
+ export function printVersion() {
112
+ console.log('clawdit v0.1.0');
113
+ }
114
+ export function printListChecksText(checks, noColor) {
115
+ const c = noColor
116
+ ? { reset: '', bold: '', dim: '', red: '', yellow: '', cyan: '' }
117
+ : { reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m', red: '\x1b[31m', yellow: '\x1b[33m', cyan: '\x1b[36m' };
118
+ const severityColor = (s) => s === 'HIGH' ? c.red : s === 'MEDIUM' ? c.yellow : c.cyan;
119
+ console.log(`${c.bold}ID SEVERITY NAME${c.reset}`);
120
+ for (const check of checks) {
121
+ const sev = severityColor(check.severity) + check.severity.padEnd(8) + c.reset;
122
+ console.log(`${check.id.padEnd(12)}${sev} ${check.name}`);
123
+ }
124
+ console.log(`${c.dim}${checks.length} checks available${c.reset}`);
125
+ }
126
+ export function printListChecksJson(checks) {
127
+ const output = {
128
+ checks: checks.map(c => ({ id: c.id, severity: c.severity, name: c.name }))
129
+ };
130
+ console.log(JSON.stringify(output, null, 2));
131
+ }
132
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Read content from stdin synchronously
3
+ */
4
+ export declare function readStdinSync(): string;
5
+ export declare class ConfigError extends Error {
6
+ code: number;
7
+ details?: string | undefined;
8
+ constructor(message: string, code: number, details?: string | undefined);
9
+ }
10
+ /**
11
+ * Discover config file path in priority order
12
+ */
13
+ export declare function discoverConfig(cliPath?: string): string;
14
+ /**
15
+ * Parse config from string content with $include resolution
16
+ */
17
+ export declare function parseConfigFromString(content: string, baseDir: string, sourceName?: string): Record<string, unknown>;
18
+ /**
19
+ * Parse config file with $include resolution
20
+ */
21
+ export declare function parseConfig(configPath: string): Record<string, unknown>;
22
+ //# sourceMappingURL=config.d.ts.map
package/dist/config.js ADDED
@@ -0,0 +1,150 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import { resolve, dirname, join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import JSON5 from 'json5';
5
+ /**
6
+ * Read content from stdin synchronously
7
+ */
8
+ export function readStdinSync() {
9
+ try {
10
+ return readFileSync(0, 'utf-8');
11
+ }
12
+ catch (error) {
13
+ throw new ConfigError('Failed to read from stdin', 20, error.message);
14
+ }
15
+ }
16
+ export class ConfigError extends Error {
17
+ code;
18
+ details;
19
+ constructor(message, code, details) {
20
+ super(message);
21
+ this.code = code;
22
+ this.details = details;
23
+ this.name = 'ConfigError';
24
+ }
25
+ }
26
+ /**
27
+ * Discover config file path in priority order
28
+ */
29
+ export function discoverConfig(cliPath) {
30
+ // 1. CLI argument (highest priority)
31
+ if (cliPath) {
32
+ const resolved = resolve(cliPath);
33
+ if (!existsSync(resolved)) {
34
+ throw new ConfigError('Configuration file not found', 10, `Path: ${resolved}\n\nPossible causes:\n - OpenClaw is not installed\n - Config is in a non-standard location\n - Running as wrong user`);
35
+ }
36
+ return resolved;
37
+ }
38
+ // 2. CLAWDBOT_CONFIG environment variable
39
+ const envPath = process.env.CLAWDBOT_CONFIG;
40
+ if (envPath) {
41
+ const resolved = resolve(envPath);
42
+ if (existsSync(resolved))
43
+ return resolved;
44
+ }
45
+ // 3. Search paths in priority order
46
+ const home = homedir();
47
+ const CONFIG_FILENAMES = ['openclaw.json', 'moltbot.json', 'clawdbot.json'];
48
+ const CONFIG_DIRS = [
49
+ '.',
50
+ join(home, '.openclaw'),
51
+ join(home, '.clawdbot'),
52
+ join(home, '.moltbot'),
53
+ ];
54
+ // Generate all combinations, prioritizing openclaw.json in each directory
55
+ const searchPaths = CONFIG_DIRS.flatMap(dir => CONFIG_FILENAMES.map(file => join(dir, file)));
56
+ for (const searchPath of searchPaths) {
57
+ const resolved = resolve(searchPath);
58
+ if (existsSync(resolved))
59
+ return resolved;
60
+ }
61
+ throw new ConfigError('Configuration file not found', 10, `Searched paths:\n${searchPaths.map(p => ` - ${p}`).join('\n')}\n\nActions:\n 1. Install OpenClaw: curl -fsSL https://get.openclaw.ai | sh\n 2. Or specify path: clawdit /path/to/openclaw.json`);
62
+ }
63
+ /**
64
+ * Parse config from string content with $include resolution
65
+ */
66
+ export function parseConfigFromString(content, baseDir, sourceName = '<stdin>') {
67
+ let config;
68
+ try {
69
+ config = JSON5.parse(content);
70
+ }
71
+ catch (error) {
72
+ throw new ConfigError(`JSON5 parse error`, 11, `Source: ${sourceName}\nError: ${error.message}`);
73
+ }
74
+ // Process $include directives with empty inclusion chain
75
+ return processIncludes(config, baseDir, new Set());
76
+ }
77
+ /**
78
+ * Parse config file with $include resolution
79
+ */
80
+ export function parseConfig(configPath) {
81
+ const inclusionChain = new Set();
82
+ return parseConfigRecursive(configPath, inclusionChain);
83
+ }
84
+ function parseConfigRecursive(configPath, inclusionChain) {
85
+ const resolvedPath = resolve(configPath);
86
+ // Check for circular inclusion
87
+ if (inclusionChain.has(resolvedPath)) {
88
+ throw new ConfigError('$include directive failed: circular reference detected', 14, `Circular inclusion chain:\n${[...inclusionChain, resolvedPath].map(p => ` -> ${p}`).join('\n')}`);
89
+ }
90
+ inclusionChain.add(resolvedPath);
91
+ // Read and parse file
92
+ let content;
93
+ try {
94
+ content = readFileSync(resolvedPath, 'utf-8');
95
+ }
96
+ catch (err) {
97
+ const error = err;
98
+ if (error.code === 'ENOENT') {
99
+ throw new ConfigError('Configuration file not found', 10, `Path: ${resolvedPath}`);
100
+ }
101
+ if (error.code === 'EACCES') {
102
+ throw new ConfigError('Permission denied reading configuration', 13, `Path: ${resolvedPath}\n\nCheck file permissions: ls -la ${resolvedPath}`);
103
+ }
104
+ throw new ConfigError('Failed to read configuration file', 20, `Path: ${resolvedPath}\nError: ${error.message}`);
105
+ }
106
+ let config;
107
+ try {
108
+ config = JSON5.parse(content);
109
+ }
110
+ catch (err) {
111
+ const error = err;
112
+ throw new ConfigError('JSON5 parse error', 11, `Path: ${resolvedPath}\nError: ${error.message}`);
113
+ }
114
+ // Process $include directives
115
+ const configDir = dirname(resolvedPath);
116
+ config = processIncludes(config, configDir, inclusionChain);
117
+ inclusionChain.delete(resolvedPath);
118
+ return config;
119
+ }
120
+ function processIncludes(config, baseDir, inclusionChain) {
121
+ const result = {};
122
+ for (const [key, value] of Object.entries(config)) {
123
+ if (key === '$include') {
124
+ // Handle $include directive
125
+ if (typeof value === 'string') {
126
+ const includePath = resolve(baseDir, value);
127
+ const included = parseConfigRecursive(includePath, new Set(inclusionChain));
128
+ Object.assign(result, included);
129
+ }
130
+ else if (Array.isArray(value)) {
131
+ for (const item of value) {
132
+ if (typeof item === 'string') {
133
+ const includePath = resolve(baseDir, item);
134
+ const included = parseConfigRecursive(includePath, new Set(inclusionChain));
135
+ Object.assign(result, included);
136
+ }
137
+ }
138
+ }
139
+ }
140
+ else if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
141
+ // Recursively process nested objects
142
+ result[key] = processIncludes(value, baseDir, inclusionChain);
143
+ }
144
+ else {
145
+ result[key] = value;
146
+ }
147
+ }
148
+ return result;
149
+ }
150
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,42 @@
1
+ import type { AuditResult } from './checks/types.js';
2
+ interface FormatOptions {
3
+ noColor: boolean;
4
+ quiet: boolean;
5
+ configPath: string;
6
+ }
7
+ /**
8
+ * Result of formatText split into parts for proper stream separation
9
+ * - banner: Goes to stderr (chrome/metadata)
10
+ * - findings: Goes to stdout (data)
11
+ * - summary: Goes to stderr (chrome/metadata)
12
+ */
13
+ export interface TextFormatResult {
14
+ banner: string;
15
+ findings: string;
16
+ summary: string;
17
+ }
18
+ /**
19
+ * Format audit result as text, split into parts for proper stream separation
20
+ */
21
+ export declare function formatText(result: AuditResult, options: FormatOptions): TextFormatResult;
22
+ /**
23
+ * Format audit result as text, joined as a single string.
24
+ * Used for file output where stream separation is not needed.
25
+ */
26
+ export declare function formatTextFull(result: AuditResult, options: FormatOptions): string;
27
+ interface JsonFormatOptions {
28
+ configPath: string;
29
+ }
30
+ interface SarifFormatOptions {
31
+ configPath: string;
32
+ }
33
+ /**
34
+ * Format audit result as SARIF 2.1.0
35
+ */
36
+ export declare function formatSarif(result: AuditResult, options: SarifFormatOptions): string;
37
+ /**
38
+ * Format audit result as JSON
39
+ */
40
+ export declare function formatJson(result: AuditResult, options: JsonFormatOptions): string;
41
+ export {};
42
+ //# sourceMappingURL=formatter.d.ts.map