codeharness 0.19.5 → 0.21.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/index.js +3149 -2656
- package/dist/modules/observability/index.d.ts +295 -0
- package/dist/modules/observability/index.js +366 -0
- package/package.json +6 -1
- package/patches/AGENTS.md +19 -1
- package/patches/observability/AGENTS.md +27 -0
- package/patches/observability/__tests__/catch-without-logging.ts +36 -0
- package/patches/observability/__tests__/error-path-no-log.ts +47 -0
- package/patches/observability/__tests__/function-no-debug-log.ts +54 -0
- package/patches/observability/catch-without-logging.ts +36 -0
- package/patches/observability/catch-without-logging.yaml +35 -0
- package/patches/observability/error-path-no-log.ts +47 -0
- package/patches/observability/error-path-no-log.yaml +68 -0
- package/patches/observability/function-no-debug-log.ts +54 -0
- package/patches/observability/function-no-debug-log.yaml +114 -0
- package/ralph/drivers/claude-code.sh +2 -28
- package/ralph/ralph.sh +153 -9
- package/templates/Dockerfile.verify +8 -1
package/patches/AGENTS.md
CHANGED
|
@@ -17,7 +17,25 @@ patches/
|
|
|
17
17
|
retro/enforcement.md — Retrospective quality metrics
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Subdirectories map to BMAD workflow roles.
|
|
20
|
+
Subdirectories map to BMAD workflow roles (or analysis categories like `observability/`).
|
|
21
|
+
|
|
22
|
+
## Observability Module (`observability/`)
|
|
23
|
+
|
|
24
|
+
Semgrep YAML rules for static analysis of observability gaps. Each `.yaml` file is a standalone Semgrep config — no build step required. Deleting a rule file removes that check.
|
|
25
|
+
|
|
26
|
+
**Rules:**
|
|
27
|
+
- `catch-without-logging.yaml` — Detects catch blocks without error/warn logging (WARNING)
|
|
28
|
+
- `function-no-debug-log.yaml` — Detects functions without debug/info logging (INFO)
|
|
29
|
+
- `error-path-no-log.yaml` — Detects error paths (throw/return err) without preceding log (WARNING)
|
|
30
|
+
|
|
31
|
+
**Test fixtures** (`.ts` files alongside rules, annotated with `// ruleid:` / `// ok:` comments):
|
|
32
|
+
- `catch-without-logging.ts` — Test cases for catch-without-logging rule
|
|
33
|
+
- `function-no-debug-log.ts` — Test cases for function-no-debug-log rule
|
|
34
|
+
- `error-path-no-log.ts` — Test cases for error-path-no-log rule
|
|
35
|
+
|
|
36
|
+
**Testing:** `semgrep --test patches/observability/` runs annotated test fixtures.
|
|
37
|
+
|
|
38
|
+
**Customization:** Edit YAML rules to add custom logger patterns (e.g., `logger.error(...)` for winston). Rules use `pattern-not` / `pattern-not-inside` to detect absence of logging.
|
|
21
39
|
|
|
22
40
|
## How Patches Work
|
|
23
41
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# patches/observability/ — Semgrep Rules for Observability Gap Detection
|
|
2
|
+
|
|
3
|
+
Standalone Semgrep YAML rules for static analysis of observability gaps. Each `.yaml` file is a complete Semgrep config — no build step, no TypeScript. Deleting a rule file removes that check.
|
|
4
|
+
|
|
5
|
+
## Rules
|
|
6
|
+
|
|
7
|
+
| File | Purpose | Severity |
|
|
8
|
+
|------|---------|----------|
|
|
9
|
+
| catch-without-logging.yaml | Detects catch blocks without error/warn logging | WARNING |
|
|
10
|
+
| function-no-debug-log.yaml | Detects functions without debug/info logging | INFO |
|
|
11
|
+
| error-path-no-log.yaml | Detects error paths (throw/return err) without preceding log | WARNING |
|
|
12
|
+
|
|
13
|
+
## Test Fixtures
|
|
14
|
+
|
|
15
|
+
| File | Purpose |
|
|
16
|
+
|------|---------|
|
|
17
|
+
| catch-without-logging.ts | Test cases for catch-without-logging rule (annotated with `// ruleid:` / `// ok:`) |
|
|
18
|
+
| function-no-debug-log.ts | Test cases for function-no-debug-log rule |
|
|
19
|
+
| error-path-no-log.ts | Test cases for error-path-no-log rule |
|
|
20
|
+
|
|
21
|
+
## Testing
|
|
22
|
+
|
|
23
|
+
Run `semgrep --test patches/observability/` to execute all test fixtures against their rules.
|
|
24
|
+
|
|
25
|
+
## Customization
|
|
26
|
+
|
|
27
|
+
Edit YAML rules to add custom logger patterns (e.g., `logger.error(...)` for winston). Rules use `pattern-not` / `pattern-not-inside` to detect absence of logging.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Test cases for catch-without-logging Semgrep rule
|
|
2
|
+
|
|
3
|
+
// ruleid: catch-without-logging
|
|
4
|
+
try { doSomething(); } catch (e) { /* no logging at all */ }
|
|
5
|
+
|
|
6
|
+
// ruleid: catch-without-logging
|
|
7
|
+
try {
|
|
8
|
+
riskyOperation();
|
|
9
|
+
} catch (err) {
|
|
10
|
+
cleanup();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// ok: catch-without-logging
|
|
14
|
+
try { doSomething(); } catch (e) { console.error('failed', e); }
|
|
15
|
+
|
|
16
|
+
// ok: catch-without-logging
|
|
17
|
+
try {
|
|
18
|
+
riskyOperation();
|
|
19
|
+
} catch (err) {
|
|
20
|
+
console.warn('operation failed', err);
|
|
21
|
+
cleanup();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ok: catch-without-logging
|
|
25
|
+
try {
|
|
26
|
+
riskyOperation();
|
|
27
|
+
} catch (err) {
|
|
28
|
+
logger.error('operation failed', err);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ok: catch-without-logging
|
|
32
|
+
try {
|
|
33
|
+
riskyOperation();
|
|
34
|
+
} catch (err) {
|
|
35
|
+
logger.warn('operation failed', err);
|
|
36
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Test cases for error-path-no-log Semgrep rule
|
|
2
|
+
|
|
3
|
+
function badThrow() {
|
|
4
|
+
// ruleid: error-path-no-log
|
|
5
|
+
throw new Error('something went wrong');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function badReturn() {
|
|
9
|
+
// ruleid: error-path-no-log
|
|
10
|
+
return err('something went wrong');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function goodThrow() {
|
|
14
|
+
// ok: error-path-no-log
|
|
15
|
+
console.error('about to throw');
|
|
16
|
+
throw new Error('something went wrong');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function goodReturn() {
|
|
20
|
+
// ok: error-path-no-log
|
|
21
|
+
console.error('returning error');
|
|
22
|
+
return err('something went wrong');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function goodThrowWithLogger() {
|
|
26
|
+
// ok: error-path-no-log
|
|
27
|
+
logger.error('about to throw');
|
|
28
|
+
throw new Error('something went wrong');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function goodReturnWithLogger() {
|
|
32
|
+
// ok: error-path-no-log
|
|
33
|
+
logger.error('returning error');
|
|
34
|
+
return err('something went wrong');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function goodThrowWithWarn() {
|
|
38
|
+
// ok: error-path-no-log
|
|
39
|
+
console.warn('about to throw');
|
|
40
|
+
throw new Error('something went wrong');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function goodReturnWithLoggerWarn() {
|
|
44
|
+
// ok: error-path-no-log
|
|
45
|
+
logger.warn('returning error');
|
|
46
|
+
return err('something went wrong');
|
|
47
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Test cases for function-no-debug-log Semgrep rule
|
|
2
|
+
|
|
3
|
+
// ruleid: function-no-debug-log
|
|
4
|
+
function processData(input: string) {
|
|
5
|
+
return input.trim();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// ruleid: function-no-debug-log
|
|
9
|
+
function handleRequest(req: any) {
|
|
10
|
+
const result = compute(req);
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// ruleid: function-no-debug-log
|
|
15
|
+
const transformData = (input: string) => {
|
|
16
|
+
return input.toUpperCase();
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// ok: function-no-debug-log
|
|
20
|
+
function processDataWithLog(input: string) {
|
|
21
|
+
console.log('processing data', input);
|
|
22
|
+
return input.trim();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ok: function-no-debug-log
|
|
26
|
+
function handleRequestWithDebug(req: any) {
|
|
27
|
+
console.debug('handling request', req);
|
|
28
|
+
const result = compute(req);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ok: function-no-debug-log
|
|
33
|
+
function serviceCall(params: any) {
|
|
34
|
+
logger.debug('service call', params);
|
|
35
|
+
return fetch(params.url);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ok: function-no-debug-log
|
|
39
|
+
function anotherService(params: any) {
|
|
40
|
+
logger.info('another service call', params);
|
|
41
|
+
return fetch(params.url);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ok: function-no-debug-log
|
|
45
|
+
const transformWithLog = (input: string) => {
|
|
46
|
+
console.log('transforming', input);
|
|
47
|
+
return input.toUpperCase();
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// ok: function-no-debug-log
|
|
51
|
+
const arrowWithDebug = (input: string) => {
|
|
52
|
+
logger.debug('arrow function', input);
|
|
53
|
+
return input.toLowerCase();
|
|
54
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Test cases for catch-without-logging Semgrep rule
|
|
2
|
+
|
|
3
|
+
// ruleid: catch-without-logging
|
|
4
|
+
try { doSomething(); } catch (e) { /* no logging at all */ }
|
|
5
|
+
|
|
6
|
+
// ruleid: catch-without-logging
|
|
7
|
+
try {
|
|
8
|
+
riskyOperation();
|
|
9
|
+
} catch (err) {
|
|
10
|
+
cleanup();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// ok: catch-without-logging
|
|
14
|
+
try { doSomething(); } catch (e) { console.error('failed', e); }
|
|
15
|
+
|
|
16
|
+
// ok: catch-without-logging
|
|
17
|
+
try {
|
|
18
|
+
riskyOperation();
|
|
19
|
+
} catch (err) {
|
|
20
|
+
console.warn('operation failed', err);
|
|
21
|
+
cleanup();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ok: catch-without-logging
|
|
25
|
+
try {
|
|
26
|
+
riskyOperation();
|
|
27
|
+
} catch (err) {
|
|
28
|
+
logger.error('operation failed', err);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ok: catch-without-logging
|
|
32
|
+
try {
|
|
33
|
+
riskyOperation();
|
|
34
|
+
} catch (err) {
|
|
35
|
+
logger.warn('operation failed', err);
|
|
36
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: catch-without-logging
|
|
3
|
+
patterns:
|
|
4
|
+
- pattern: |
|
|
5
|
+
try { ... } catch ($ERR) { ... }
|
|
6
|
+
- pattern-not: |
|
|
7
|
+
try { ... } catch ($ERR) {
|
|
8
|
+
...
|
|
9
|
+
console.error(...)
|
|
10
|
+
...
|
|
11
|
+
}
|
|
12
|
+
- pattern-not: |
|
|
13
|
+
try { ... } catch ($ERR) {
|
|
14
|
+
...
|
|
15
|
+
console.warn(...)
|
|
16
|
+
...
|
|
17
|
+
}
|
|
18
|
+
- pattern-not: |
|
|
19
|
+
try { ... } catch ($ERR) {
|
|
20
|
+
...
|
|
21
|
+
logger.error(...)
|
|
22
|
+
...
|
|
23
|
+
}
|
|
24
|
+
- pattern-not: |
|
|
25
|
+
try { ... } catch ($ERR) {
|
|
26
|
+
...
|
|
27
|
+
logger.warn(...)
|
|
28
|
+
...
|
|
29
|
+
}
|
|
30
|
+
message: "Catch block without error logging — observability gap"
|
|
31
|
+
languages: [typescript, javascript]
|
|
32
|
+
severity: WARNING
|
|
33
|
+
metadata:
|
|
34
|
+
category: observability
|
|
35
|
+
cwe: "CWE-778: Insufficient Logging"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Test cases for error-path-no-log Semgrep rule
|
|
2
|
+
|
|
3
|
+
function badThrow() {
|
|
4
|
+
// ruleid: error-path-no-log
|
|
5
|
+
throw new Error('something went wrong');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function badReturn() {
|
|
9
|
+
// ruleid: error-path-no-log
|
|
10
|
+
return err('something went wrong');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function goodThrow() {
|
|
14
|
+
// ok: error-path-no-log
|
|
15
|
+
console.error('about to throw');
|
|
16
|
+
throw new Error('something went wrong');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function goodReturn() {
|
|
20
|
+
// ok: error-path-no-log
|
|
21
|
+
console.error('returning error');
|
|
22
|
+
return err('something went wrong');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function goodThrowWithLogger() {
|
|
26
|
+
// ok: error-path-no-log
|
|
27
|
+
logger.error('about to throw');
|
|
28
|
+
throw new Error('something went wrong');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function goodReturnWithLogger() {
|
|
32
|
+
// ok: error-path-no-log
|
|
33
|
+
logger.error('returning error');
|
|
34
|
+
return err('something went wrong');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function goodThrowWithWarn() {
|
|
38
|
+
// ok: error-path-no-log
|
|
39
|
+
console.warn('about to throw');
|
|
40
|
+
throw new Error('something went wrong');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function goodReturnWithLoggerWarn() {
|
|
44
|
+
// ok: error-path-no-log
|
|
45
|
+
logger.warn('returning error');
|
|
46
|
+
return err('something went wrong');
|
|
47
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: error-path-no-log
|
|
3
|
+
patterns:
|
|
4
|
+
- pattern-either:
|
|
5
|
+
- pattern: throw $ERR;
|
|
6
|
+
- pattern: return err(...);
|
|
7
|
+
- pattern-not-inside: |
|
|
8
|
+
{
|
|
9
|
+
...
|
|
10
|
+
console.error(...)
|
|
11
|
+
...
|
|
12
|
+
throw $ERR;
|
|
13
|
+
}
|
|
14
|
+
- pattern-not-inside: |
|
|
15
|
+
{
|
|
16
|
+
...
|
|
17
|
+
console.warn(...)
|
|
18
|
+
...
|
|
19
|
+
throw $ERR;
|
|
20
|
+
}
|
|
21
|
+
- pattern-not-inside: |
|
|
22
|
+
{
|
|
23
|
+
...
|
|
24
|
+
logger.error(...)
|
|
25
|
+
...
|
|
26
|
+
throw $ERR;
|
|
27
|
+
}
|
|
28
|
+
- pattern-not-inside: |
|
|
29
|
+
{
|
|
30
|
+
...
|
|
31
|
+
logger.warn(...)
|
|
32
|
+
...
|
|
33
|
+
throw $ERR;
|
|
34
|
+
}
|
|
35
|
+
- pattern-not-inside: |
|
|
36
|
+
{
|
|
37
|
+
...
|
|
38
|
+
console.error(...)
|
|
39
|
+
...
|
|
40
|
+
return err(...);
|
|
41
|
+
}
|
|
42
|
+
- pattern-not-inside: |
|
|
43
|
+
{
|
|
44
|
+
...
|
|
45
|
+
console.warn(...)
|
|
46
|
+
...
|
|
47
|
+
return err(...);
|
|
48
|
+
}
|
|
49
|
+
- pattern-not-inside: |
|
|
50
|
+
{
|
|
51
|
+
...
|
|
52
|
+
logger.error(...)
|
|
53
|
+
...
|
|
54
|
+
return err(...);
|
|
55
|
+
}
|
|
56
|
+
- pattern-not-inside: |
|
|
57
|
+
{
|
|
58
|
+
...
|
|
59
|
+
logger.warn(...)
|
|
60
|
+
...
|
|
61
|
+
return err(...);
|
|
62
|
+
}
|
|
63
|
+
message: "Error path without logging — observability gap"
|
|
64
|
+
languages: [typescript, javascript]
|
|
65
|
+
severity: WARNING
|
|
66
|
+
metadata:
|
|
67
|
+
category: observability
|
|
68
|
+
cwe: "CWE-778: Insufficient Logging"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Test cases for function-no-debug-log Semgrep rule
|
|
2
|
+
|
|
3
|
+
// ruleid: function-no-debug-log
|
|
4
|
+
function processData(input: string) {
|
|
5
|
+
return input.trim();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// ruleid: function-no-debug-log
|
|
9
|
+
function handleRequest(req: any) {
|
|
10
|
+
const result = compute(req);
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// ruleid: function-no-debug-log
|
|
15
|
+
const transformData = (input: string) => {
|
|
16
|
+
return input.toUpperCase();
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// ok: function-no-debug-log
|
|
20
|
+
function processDataWithLog(input: string) {
|
|
21
|
+
console.log('processing data', input);
|
|
22
|
+
return input.trim();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ok: function-no-debug-log
|
|
26
|
+
function handleRequestWithDebug(req: any) {
|
|
27
|
+
console.debug('handling request', req);
|
|
28
|
+
const result = compute(req);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ok: function-no-debug-log
|
|
33
|
+
function serviceCall(params: any) {
|
|
34
|
+
logger.debug('service call', params);
|
|
35
|
+
return fetch(params.url);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ok: function-no-debug-log
|
|
39
|
+
function anotherService(params: any) {
|
|
40
|
+
logger.info('another service call', params);
|
|
41
|
+
return fetch(params.url);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ok: function-no-debug-log
|
|
45
|
+
const transformWithLog = (input: string) => {
|
|
46
|
+
console.log('transforming', input);
|
|
47
|
+
return input.toUpperCase();
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// ok: function-no-debug-log
|
|
51
|
+
const arrowWithDebug = (input: string) => {
|
|
52
|
+
logger.debug('arrow function', input);
|
|
53
|
+
return input.toLowerCase();
|
|
54
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: function-no-debug-log
|
|
3
|
+
patterns:
|
|
4
|
+
- pattern-either:
|
|
5
|
+
- pattern: |
|
|
6
|
+
function $FUNC(...) { ... }
|
|
7
|
+
- pattern: |
|
|
8
|
+
const $FUNC = (...) => { ... }
|
|
9
|
+
- pattern: |
|
|
10
|
+
let $FUNC = (...) => { ... }
|
|
11
|
+
- pattern: |
|
|
12
|
+
$FUNC(...) { ... }
|
|
13
|
+
- pattern-not: |
|
|
14
|
+
function $FUNC(...) {
|
|
15
|
+
...
|
|
16
|
+
console.log(...)
|
|
17
|
+
...
|
|
18
|
+
}
|
|
19
|
+
- pattern-not: |
|
|
20
|
+
function $FUNC(...) {
|
|
21
|
+
...
|
|
22
|
+
console.debug(...)
|
|
23
|
+
...
|
|
24
|
+
}
|
|
25
|
+
- pattern-not: |
|
|
26
|
+
function $FUNC(...) {
|
|
27
|
+
...
|
|
28
|
+
logger.debug(...)
|
|
29
|
+
...
|
|
30
|
+
}
|
|
31
|
+
- pattern-not: |
|
|
32
|
+
function $FUNC(...) {
|
|
33
|
+
...
|
|
34
|
+
logger.info(...)
|
|
35
|
+
...
|
|
36
|
+
}
|
|
37
|
+
- pattern-not: |
|
|
38
|
+
const $FUNC = (...) => {
|
|
39
|
+
...
|
|
40
|
+
console.log(...)
|
|
41
|
+
...
|
|
42
|
+
}
|
|
43
|
+
- pattern-not: |
|
|
44
|
+
const $FUNC = (...) => {
|
|
45
|
+
...
|
|
46
|
+
console.debug(...)
|
|
47
|
+
...
|
|
48
|
+
}
|
|
49
|
+
- pattern-not: |
|
|
50
|
+
const $FUNC = (...) => {
|
|
51
|
+
...
|
|
52
|
+
logger.debug(...)
|
|
53
|
+
...
|
|
54
|
+
}
|
|
55
|
+
- pattern-not: |
|
|
56
|
+
const $FUNC = (...) => {
|
|
57
|
+
...
|
|
58
|
+
logger.info(...)
|
|
59
|
+
...
|
|
60
|
+
}
|
|
61
|
+
- pattern-not: |
|
|
62
|
+
let $FUNC = (...) => {
|
|
63
|
+
...
|
|
64
|
+
console.log(...)
|
|
65
|
+
...
|
|
66
|
+
}
|
|
67
|
+
- pattern-not: |
|
|
68
|
+
let $FUNC = (...) => {
|
|
69
|
+
...
|
|
70
|
+
console.debug(...)
|
|
71
|
+
...
|
|
72
|
+
}
|
|
73
|
+
- pattern-not: |
|
|
74
|
+
let $FUNC = (...) => {
|
|
75
|
+
...
|
|
76
|
+
logger.debug(...)
|
|
77
|
+
...
|
|
78
|
+
}
|
|
79
|
+
- pattern-not: |
|
|
80
|
+
let $FUNC = (...) => {
|
|
81
|
+
...
|
|
82
|
+
logger.info(...)
|
|
83
|
+
...
|
|
84
|
+
}
|
|
85
|
+
- pattern-not: |
|
|
86
|
+
$FUNC(...) {
|
|
87
|
+
...
|
|
88
|
+
console.log(...)
|
|
89
|
+
...
|
|
90
|
+
}
|
|
91
|
+
- pattern-not: |
|
|
92
|
+
$FUNC(...) {
|
|
93
|
+
...
|
|
94
|
+
console.debug(...)
|
|
95
|
+
...
|
|
96
|
+
}
|
|
97
|
+
- pattern-not: |
|
|
98
|
+
$FUNC(...) {
|
|
99
|
+
...
|
|
100
|
+
logger.debug(...)
|
|
101
|
+
...
|
|
102
|
+
}
|
|
103
|
+
- pattern-not: |
|
|
104
|
+
$FUNC(...) {
|
|
105
|
+
...
|
|
106
|
+
logger.info(...)
|
|
107
|
+
...
|
|
108
|
+
}
|
|
109
|
+
message: "Function without debug/info logging — observability gap"
|
|
110
|
+
languages: [typescript, javascript]
|
|
111
|
+
severity: INFO
|
|
112
|
+
metadata:
|
|
113
|
+
category: observability
|
|
114
|
+
cwe: "CWE-778: Insufficient Logging"
|
|
@@ -79,10 +79,8 @@ driver_build_command() {
|
|
|
79
79
|
CLAUDE_CMD_ARGS+=("--plugin-dir" "$plugin_dir")
|
|
80
80
|
fi
|
|
81
81
|
|
|
82
|
-
# Output format
|
|
83
|
-
|
|
84
|
-
CLAUDE_CMD_ARGS+=("--output-format" "json")
|
|
85
|
-
fi
|
|
82
|
+
# Output format — always stream-json for real-time NDJSON output
|
|
83
|
+
CLAUDE_CMD_ARGS+=("--output-format" "stream-json" "--verbose" "--include-partial-messages")
|
|
86
84
|
|
|
87
85
|
# Allowed tools
|
|
88
86
|
if [[ -n "$CLAUDE_ALLOWED_TOOLS" ]]; then
|
|
@@ -123,30 +121,6 @@ driver_supports_live_output() {
|
|
|
123
121
|
return 0
|
|
124
122
|
}
|
|
125
123
|
|
|
126
|
-
# Prepare command arguments for live stream-json output
|
|
127
|
-
driver_prepare_live_command() {
|
|
128
|
-
LIVE_CMD_ARGS=()
|
|
129
|
-
local skip_next=false
|
|
130
|
-
|
|
131
|
-
for arg in "${CLAUDE_CMD_ARGS[@]}"; do
|
|
132
|
-
if [[ "$skip_next" == "true" ]]; then
|
|
133
|
-
LIVE_CMD_ARGS+=("stream-json")
|
|
134
|
-
skip_next=false
|
|
135
|
-
elif [[ "$arg" == "--output-format" ]]; then
|
|
136
|
-
LIVE_CMD_ARGS+=("$arg")
|
|
137
|
-
skip_next=true
|
|
138
|
-
else
|
|
139
|
-
LIVE_CMD_ARGS+=("$arg")
|
|
140
|
-
fi
|
|
141
|
-
done
|
|
142
|
-
|
|
143
|
-
if [[ "$skip_next" == "true" ]]; then
|
|
144
|
-
return 1
|
|
145
|
-
fi
|
|
146
|
-
|
|
147
|
-
LIVE_CMD_ARGS+=("--verbose" "--include-partial-messages")
|
|
148
|
-
}
|
|
149
|
-
|
|
150
124
|
# Stream filter for raw Claude stream-json events
|
|
151
125
|
driver_stream_filter() {
|
|
152
126
|
echo '
|