mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-18 20:15:14 +02:00
Phase 1 (#33)
* 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
This commit is contained in:
parent
19b578c5c4
commit
1bbe4b1cfb
456 changed files with 25628 additions and 1228 deletions
25
tests/fixtures/real_world/python/state/branch_leak.expect.json
vendored
Normal file
25
tests/fixtures/real_world/python/state/branch_leak.expect.json
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"description": "File handle leaked in else branch of conditional",
|
||||
"tags": [
|
||||
"state",
|
||||
"resource-leak",
|
||||
"branch",
|
||||
"file-io"
|
||||
],
|
||||
"modes": [
|
||||
"full"
|
||||
],
|
||||
"expected": [
|
||||
{
|
||||
"rule_id": "state-resource-leak-possible",
|
||||
"severity": null,
|
||||
"must_match": false,
|
||||
"line_range": [
|
||||
1,
|
||||
13
|
||||
],
|
||||
"evidence_contains": [],
|
||||
"notes": "File handle closed in if branch but leaked in else branch"
|
||||
}
|
||||
]
|
||||
}
|
||||
11
tests/fixtures/real_world/python/state/branch_leak.py
vendored
Normal file
11
tests/fixtures/real_world/python/state/branch_leak.py
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import os
|
||||
|
||||
def conditional_open(path, flag):
|
||||
f = open(path, 'r')
|
||||
if flag:
|
||||
data = f.read()
|
||||
f.close()
|
||||
return data
|
||||
else:
|
||||
return "skipped"
|
||||
# f leaked in else branch
|
||||
48
tests/fixtures/real_world/python/state/file_lifecycle.expect.json
vendored
Normal file
48
tests/fixtures/real_world/python/state/file_lifecycle.expect.json
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"description": "File handle lifecycle patterns: leak, proper close, double close, use after close",
|
||||
"tags": [
|
||||
"state",
|
||||
"resource-leak",
|
||||
"double-close",
|
||||
"use-after-close",
|
||||
"file-io"
|
||||
],
|
||||
"modes": [
|
||||
"full"
|
||||
],
|
||||
"expected": [
|
||||
{
|
||||
"rule_id": "state-resource-leak",
|
||||
"severity": null,
|
||||
"must_match": false,
|
||||
"line_range": [
|
||||
1,
|
||||
6
|
||||
],
|
||||
"evidence_contains": [],
|
||||
"notes": "read_and_leak opens file but never closes it"
|
||||
},
|
||||
{
|
||||
"rule_id": "state-double-close",
|
||||
"severity": null,
|
||||
"must_match": false,
|
||||
"line_range": [
|
||||
10,
|
||||
17
|
||||
],
|
||||
"evidence_contains": [],
|
||||
"notes": "double_close calls f.close() twice"
|
||||
},
|
||||
{
|
||||
"rule_id": "state-use-after-close",
|
||||
"severity": null,
|
||||
"must_match": false,
|
||||
"line_range": [
|
||||
15,
|
||||
23
|
||||
],
|
||||
"evidence_contains": [],
|
||||
"notes": "use_after_close reads from file handle after closing it"
|
||||
}
|
||||
]
|
||||
}
|
||||
21
tests/fixtures/real_world/python/state/file_lifecycle.py
vendored
Normal file
21
tests/fixtures/real_world/python/state/file_lifecycle.py
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
def read_and_leak(path):
|
||||
f = open(path, 'r')
|
||||
data = f.read()
|
||||
return data
|
||||
|
||||
def read_and_close(path):
|
||||
f = open(path, 'r')
|
||||
data = f.read()
|
||||
f.close()
|
||||
return data
|
||||
|
||||
def double_close(path):
|
||||
f = open(path, 'r')
|
||||
f.close()
|
||||
f.close()
|
||||
|
||||
def use_after_close(path):
|
||||
f = open(path, 'r')
|
||||
f.close()
|
||||
data = f.read()
|
||||
return data
|
||||
24
tests/fixtures/real_world/python/state/socket_lifecycle.expect.json
vendored
Normal file
24
tests/fixtures/real_world/python/state/socket_lifecycle.expect.json
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"description": "Socket resource lifecycle - leaked vs properly closed with try/finally",
|
||||
"tags": [
|
||||
"state",
|
||||
"resource-leak",
|
||||
"socket"
|
||||
],
|
||||
"modes": [
|
||||
"full"
|
||||
],
|
||||
"expected": [
|
||||
{
|
||||
"rule_id": "state-resource-leak",
|
||||
"severity": null,
|
||||
"must_match": false,
|
||||
"line_range": [
|
||||
1,
|
||||
10
|
||||
],
|
||||
"evidence_contains": [],
|
||||
"notes": "connect_and_leak creates socket but never closes it"
|
||||
}
|
||||
]
|
||||
}
|
||||
18
tests/fixtures/real_world/python/state/socket_lifecycle.py
vendored
Normal file
18
tests/fixtures/real_world/python/state/socket_lifecycle.py
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import socket
|
||||
|
||||
def connect_and_leak(host, port):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect((host, port))
|
||||
s.send(b'hello')
|
||||
data = s.recv(1024)
|
||||
return data
|
||||
|
||||
def connect_and_close(host, port):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect((host, port))
|
||||
try:
|
||||
s.send(b'hello')
|
||||
data = s.recv(1024)
|
||||
return data
|
||||
finally:
|
||||
s.close()
|
||||
25
tests/fixtures/real_world/python/state/with_statement.expect.json
vendored
Normal file
25
tests/fixtures/real_world/python/state/with_statement.expect.json
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"description": "Context manager vs manual open - else branch leaks file handle",
|
||||
"tags": [
|
||||
"state",
|
||||
"resource-leak",
|
||||
"context-manager",
|
||||
"file-io"
|
||||
],
|
||||
"modes": [
|
||||
"full"
|
||||
],
|
||||
"expected": [
|
||||
{
|
||||
"rule_id": "state-resource-leak",
|
||||
"severity": null,
|
||||
"must_match": false,
|
||||
"line_range": [
|
||||
12,
|
||||
19
|
||||
],
|
||||
"evidence_contains": [],
|
||||
"notes": "else branch opens file manually and never closes it"
|
||||
}
|
||||
]
|
||||
}
|
||||
17
tests/fixtures/real_world/python/state/with_statement.py
vendored
Normal file
17
tests/fixtures/real_world/python/state/with_statement.py
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
def safe_with(path):
|
||||
with open(path, 'r') as f:
|
||||
return f.read()
|
||||
|
||||
def nested_with(src, dst):
|
||||
with open(src, 'r') as reader:
|
||||
with open(dst, 'w') as writer:
|
||||
writer.write(reader.read())
|
||||
|
||||
def conditional_with(path, mode):
|
||||
if mode == 'read':
|
||||
with open(path, 'r') as f:
|
||||
return f.read()
|
||||
else:
|
||||
f = open(path, 'w')
|
||||
f.write('default')
|
||||
# f not closed in else branch
|
||||
Loading…
Add table
Add a link
Reference in a new issue