mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-21 20:18:06 +02:00
* chore: Exclude CLAUDE.md from Cargo.toml * feat: add callgraph module and integrate into main analysis flow * feat: enhance CLI with new severity filtering and analysis modes * feat: update CHANGELOG with recent enhancements and fixes to severity filtering and output handling * feat: implement state-model dataflow analysis for resource lifecycle and auth state * feat: enhance diagnostic output formatting and add evidence structure * feat: implement attack surface ranking for diagnostics with scoring and sorting * feat: add comprehensive documentation for installation, usage, and rules reference * feat: add multiple language support for command execution and evaluation endpoints * feat: implement inline suppression for findings using `nyx:ignore` comments * feat: add confidence levels to AST patterns and update output structure * feat: implement low-noise prioritization system with category filtering, rollup grouping, and configurable budgets * feat: bump version to 0.4.0 and update changelog with new features and improvements * feat: add dead code allowances to various functions in mod.rs and real_world_tests.rs
161 lines
6.4 KiB
Markdown
161 lines
6.4 KiB
Markdown
# CFG Structural Analysis
|
|
|
|
## Summary
|
|
|
|
Nyx builds an intra-procedural control-flow graph (CFG) for each function and analyzes structural properties: whether sinks are guarded by sanitizers or validators, whether web handlers check authentication, whether resources are released on all exit paths, and whether error-handling code terminates properly.
|
|
|
|
These detectors use **dominator analysis** — they check whether a guard node dominates (must execute before) a sink node on the CFG.
|
|
|
|
## Rule IDs
|
|
|
|
| Rule ID | Severity | Description |
|
|
|---------|----------|-------------|
|
|
| `cfg-unguarded-sink` | High/Medium | Sink reachable without a dominating guard or sanitizer |
|
|
| `cfg-auth-gap` | High | Web handler reaches privileged sink without auth check |
|
|
| `cfg-unreachable-sink` | Medium | Dangerous function in unreachable code |
|
|
| `cfg-unreachable-sanitizer` | Low | Sanitizer in unreachable code |
|
|
| `cfg-unreachable-source` | Low | Source in unreachable code |
|
|
| `cfg-error-fallthrough` | High/Medium | Error check doesn't terminate; dangerous code follows |
|
|
| `cfg-resource-leak` | Medium | Resource acquired but not released on all exit paths |
|
|
| `cfg-lock-not-released` | Medium | Lock acquired but not released on all exit paths |
|
|
|
|
## What It Detects
|
|
|
|
### Unguarded sinks (`cfg-unguarded-sink`)
|
|
A sink call (e.g. `system()`, `eval()`, `Command::new()`) is reachable from the function entry without passing through a guard or sanitizer that matches the sink's capability.
|
|
|
|
### Auth gaps (`cfg-auth-gap`)
|
|
A function identified as a web handler (by parameter naming conventions like `req`, `res`, `ctx`, `request`) reaches a privileged sink (shell execution, file I/O) without a prior call to an authentication function (`is_authenticated`, `require_auth`, `check_permission`, etc.).
|
|
|
|
### Unreachable security code (`cfg-unreachable-*`)
|
|
Sinks, sanitizers, or sources in dead code branches. This often indicates a refactoring error where security-critical code was accidentally made unreachable.
|
|
|
|
### Error fallthrough (`cfg-error-fallthrough`)
|
|
An error check (null check, error return check) does not terminate the function or loop back. Execution continues to a dangerous operation on the error path.
|
|
|
|
### Resource leaks (`cfg-resource-leak`, `cfg-lock-not-released`)
|
|
A resource acquisition call (e.g. `File::open`, `fopen`, `socket`, `Lock`) is not matched by a release call (e.g. `close`, `fclose`, `unlock`) on all exit paths from the function.
|
|
|
|
## What It Cannot Detect
|
|
|
|
- **Inter-procedural guards**: If authentication is checked in a middleware function that calls this handler, the CFG detector cannot see it. It only analyzes one function at a time.
|
|
- **Dynamic dispatch**: Virtual method calls, function pointers, and closures are opaque to the CFG.
|
|
- **Complex guard patterns**: Only recognized guard function names are checked. Custom validation logic (e.g. `if password == expected`) is not recognized as a guard.
|
|
- **Correct sanitization**: The detector checks that *some* guard dominates the sink, not that the guard is *correct*. A guard that always passes would suppress the finding.
|
|
- **Cross-function resource flows**: If a file handle is opened in one function and closed in another, the detector will report a leak in the first function.
|
|
|
|
## Common False Positives
|
|
|
|
| Scenario | Why it fires | Mitigation |
|
|
|----------|-------------|------------|
|
|
| Framework-level auth middleware | Handler doesn't call auth directly | Document as expected; suppress with severity filter |
|
|
| Resource closed via RAII/defer | Implicit cleanup not visible to CFG | Currently not detected; known limitation |
|
|
| Custom guard function name | Function not in the recognized guard list | Add the function name as a sanitizer in config |
|
|
| Test handlers | Intentionally skip auth in tests | Default non-prod downgrade reduces severity; or exclude test dirs |
|
|
|
|
## Common False Negatives
|
|
|
|
| Scenario | Why it's missed |
|
|
|----------|----------------|
|
|
| Auth in called function | Cross-function guards not tracked |
|
|
| Guard via type system | Type-level guarantees (e.g. Rust's `AuthenticatedUser` wrapper) not analyzed |
|
|
| Resource closed in finally/defer | Some cleanup patterns not recognized |
|
|
|
|
## Confidence Signals
|
|
|
|
| Signal | Meaning |
|
|
|--------|---------|
|
|
| **Evidence lists guard nodes** | Shows which guards were checked and found missing |
|
|
| **Sink has high capability** | Shell execution or file I/O sinks are higher risk |
|
|
| **Handler detection matched** | Web handler identification is based on conventional parameter names |
|
|
|
|
## Tuning and Noise Controls
|
|
|
|
### Add custom guards/sanitizers
|
|
|
|
```toml
|
|
[[analysis.languages.python.rules]]
|
|
matchers = ["validate_request", "check_csrf"]
|
|
kind = "sanitizer"
|
|
cap = "all"
|
|
```
|
|
|
|
### Add auth rules
|
|
|
|
Auth checks are recognized by function name. If your codebase uses non-standard names:
|
|
|
|
```toml
|
|
[[analysis.languages.javascript.rules]]
|
|
matchers = ["ensureLoggedIn", "requirePermission"]
|
|
kind = "sanitizer"
|
|
cap = "all"
|
|
```
|
|
|
|
### Filter results
|
|
|
|
```bash
|
|
# Skip low-severity unreachable findings
|
|
nyx scan . --severity ">=MEDIUM"
|
|
```
|
|
|
|
### Disable CFG analysis
|
|
|
|
```bash
|
|
nyx scan . --mode ast # AST patterns only
|
|
```
|
|
|
|
## Examples
|
|
|
|
### Unguarded sink
|
|
|
|
```go
|
|
func handler(w http.ResponseWriter, r *http.Request) {
|
|
cmd := r.URL.Query().Get("cmd")
|
|
exec.Command("sh", "-c", cmd).Run() // cfg-unguarded-sink: no guard dominates
|
|
}
|
|
```
|
|
|
|
### Auth gap
|
|
|
|
```javascript
|
|
app.get('/admin/delete', (req, res) => {
|
|
// No is_authenticated() call
|
|
db.execute("DELETE FROM users WHERE id = " + req.params.id);
|
|
// cfg-auth-gap: web handler reaches privileged sink without auth
|
|
});
|
|
```
|
|
|
|
### Resource leak
|
|
|
|
```c
|
|
void process() {
|
|
FILE *f = fopen("data.txt", "r"); // acquire
|
|
if (error) {
|
|
return; // cfg-resource-leak: f not closed on this path
|
|
}
|
|
fclose(f);
|
|
}
|
|
```
|
|
|
|
## Guard Rules
|
|
|
|
Nyx recognizes these function name patterns as guards:
|
|
|
|
| Pattern | Applies to |
|
|
|---------|-----------|
|
|
| `validate*`, `sanitize*` | All sinks |
|
|
| `check_*`, `verify_*`, `assert_*` | All sinks |
|
|
| `shell_escape` | Shell execution sinks |
|
|
| `html_escape` | HTML/XSS sinks |
|
|
| `url_encode` | URL sinks |
|
|
| `which` | Shell execution (binary lookup) |
|
|
|
|
### Auth rules
|
|
|
|
| Pattern | Category |
|
|
|---------|----------|
|
|
| `is_authenticated`, `require_auth`, `check_permission` | Common |
|
|
| `authorize`, `authenticate`, `require_login` | Common |
|
|
| `check_auth`, `verify_token`, `validate_token` | Common |
|
|
| `middleware.auth`, `auth.required` | Go |
|
|
| `isAuthenticated`, `checkPermission`, `hasAuthority`, `hasRole` | Java |
|