2025-06-25 00:24:05 +02:00
# Contributing to Nyx
2026-02-25 21:16:36 -05:00
Thank you for your interest in improving Nyx. This guide covers everything you need to contribute effectively.
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
Please read our [Code of Conduct ](CODE_OF_CONDUCT.md ) before participating.
2025-06-25 00:24:05 +02:00
---
## Table of Contents
2026-02-25 21:16:36 -05:00
1. [Development Setup ](#development-setup )
2. [Project Layout ](#project-layout )
3. [How to Add a New AST Pattern ](#how-to-add-a-new-ast-pattern )
4. [How to Add a New Taint Rule ](#how-to-add-a-new-taint-rule )
5. [How to Add a New Language ](#how-to-add-a-new-language )
6. [Testing ](#testing )
7. [Pull Request Guidelines ](#pull-request-guidelines )
8. [Bug Reports ](#bug-reports )
9. [Feature Requests ](#feature-requests )
10. [Release Process ](#release-process )
2025-06-25 00:24:05 +02:00
---
2026-02-25 21:16:36 -05:00
## Development Setup
### Prerequisites
- **Rust 1.85+** (edition 2024)
- Git
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
### Building
2025-06-25 00:24:05 +02:00
```bash
2026-02-25 21:16:36 -05:00
git clone https://github.com/elicpeter/nyx.git
2025-06-25 00:24:05 +02:00
cd nyx
2026-02-25 21:16:36 -05:00
cargo build # Debug build
cargo build --release # Release build
cargo install --path . # Install as `nyx` binary
2025-06-25 00:24:05 +02:00
```
2026-02-25 21:16:36 -05:00
### Running Quality Checks
```bash
cargo test --bin nyx # Unit tests (inline in modules)
cargo clippy --all -- -D warnings # Lint — treats warnings as errors
cargo fmt # Format code
cargo fmt -- --check # Check formatting without modifying
```
> **Note**: The first build downloads and compiles tree-sitter grammars for all 10 languages. Subsequent builds are faster.
### Benchmarks
2025-06-25 00:24:05 +02:00
```bash
2026-02-25 21:16:36 -05:00
cargo bench --bench scan_bench
2025-06-25 00:24:05 +02:00
```
2026-02-25 21:16:36 -05:00
Benchmark fixtures live in `benches/fixtures/` . Criterion produces HTML reports in `target/criterion/` .
2025-06-25 00:24:05 +02:00
---
2026-02-25 21:16:36 -05:00
## Project Layout
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
```
src/
main.rs CLI entry point
lib.rs Library re-exports (benchmarks, integration tests)
cli.rs Clap command definitions
commands/
mod.rs Command dispatch
scan.rs Two-pass scan orchestration, Diag struct
ast.rs Entry points for both passes; tree-sitter parsing
cfg.rs CFG construction from AST
cfg_analysis/ CFG structural detectors
guards.rs Unguarded sink detection (dominator analysis)
auth.rs Auth gap detection
resources.rs Resource leak detection
error_handling.rs Error fallthrough detection
unreachable.rs Unreachable security code detection
rules.rs Guard rules, auth rules, resource pairs
taint/
mod.rs Taint analysis facade + JS two-level solve
domain.rs TaintState lattice (VarTaint, Cap, TaintOrigin)
transfer.rs TaintTransfer function (source/sanitizer/sink/call)
path_state.rs Predicate tracking and contradiction pruning
state/
engine.rs Generic monotone dataflow engine (Transfer< S: Lattice > )
transfer.rs DefaultTransfer — resource lifecycle + auth state
summary.rs FuncSummary, GlobalSummaries, conservative merge
labels/ Per-language label rules
mod.rs classify() dispatch, Cap bitflags, DataLabel, LabelRule
rust.rs Rust sources, sinks, sanitizers
javascript.rs JS sources, sinks, sanitizers
... (one file per language)
patterns/ Per-language AST pattern queries
mod.rs Pattern struct, Severity, SeverityFilter, registry
rust.rs Rust patterns
javascript.rs JS patterns
... (one file per language)
callgraph.rs Call graph construction (petgraph), SCC, topo sort
database.rs SQLite indexing via r2d2 pool
rank.rs Attack-surface ranking
fmt.rs Output formatting and evidence normalization
output.rs SARIF 2.1 builder
walk.rs Parallel file walker (ignore crate, respects .gitignore)
symbol.rs Symbol interning (SymbolId)
interop.rs Cross-language interop edges
errors.rs NyxError, NyxResult types
utils/
config.rs TOML config loading, merging, Config struct
```
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
---
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
## How to Add a New AST Pattern
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
AST patterns are the simplest detector to add. Each pattern is a tree-sitter query that matches a structural code construct.
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
### Step-by-step
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
1. **Pick the language file** under `src/patterns/<lang>.rs` .
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
2. **Choose the metadata** :
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
| Field | Options | Guidelines |
|-------|---------|------------|
| **ID** | `<lang>.<category>.<specific>` | e.g. `py.cmdi.os_popen` |
| **Tier** | `A` or `B` | `A` = presence alone is high-signal; `B` = query includes a heuristic guard |
| **Severity** | `High` , `Medium` , `Low` | High: command exec, deser, banned functions. Medium: SQL concat, reflection, XSS. Low: weak crypto, code quality. |
| **Category** | See `PatternCategory` enum | `CommandExec` , `CodeExec` , `Deserialization` , `SqlInjection` , `PathTraversal` , `Xss` , `Crypto` , `Secrets` , `InsecureTransport` , `Reflection` , `MemorySafety` , `Prototype` , `CodeQuality` |
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
3. **Write the tree-sitter query** :
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
```rust
Pattern {
id: "py.cmdi.os_popen",
description: "os.popen() — shell command execution",
query: r#"(call
function: (attribute
object: (identifier) @pkg (#eq ? @pkg "os")
attribute: (identifier) @fn (#eq ? @fn "popen")))
@vuln "#,
severity: Severity::High,
tier: PatternTier::A,
category: PatternCategory::CommandExec,
},
```
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
The query **must** capture a `@vuln` node. That node's span determines the reported location.
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
4. **Test it** :
2025-06-25 00:24:05 +02:00
```bash
2026-02-25 21:16:36 -05:00
cargo test --bin nyx
2025-06-25 00:24:05 +02:00
```
2026-02-25 21:16:36 -05:00
5. **Update docs** : Add the new rule to `docs/rules/<lang>.md` .
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
### Tips
- Use the [tree-sitter playground ](https://tree-sitter.github.io/tree-sitter/playground ) to develop and test queries.
- Avoid duplicating taint coverage. If the same function is already a labeled sink in `src/labels/<lang>.rs` , the AST pattern is still useful for `--mode ast` , but use a distinct ID namespace. The dedup pass prevents exact-duplicate findings at the same location.
- Test with real-world code to check false positive rates before choosing a tier.
---
## How to Add a New Taint Rule
Taint rules define sources (where untrusted data enters), sinks (where dangerous operations happen), and sanitizers (where data is made safe).
### Step-by-step
1. **Open the language file** in `src/labels/<lang>.rs` .
2. **Add an entry** to the `RULES` slice:
```rust
LabelRule {
matchers: & ["dangerouslySetInnerHTML"],
label: DataLabel::Sink(Cap::HTML_ESCAPE),
},
2025-06-25 00:24:05 +02:00
```
2026-02-25 21:16:36 -05:00
3. **Choose the right label type** :
| Type | Purpose | Example |
|------|---------|---------|
| `DataLabel::Source(cap)` | Introduces tainted data | `env::var` , `req.body` |
| `DataLabel::Sanitizer(cap)` | Strips matching capability bits | `html_escape` , `encodeURIComponent` |
| `DataLabel::Sink(cap)` | Dangerous operation requiring sanitization | `eval` , `innerHTML` , `Command::new` |
4. **Choose capabilities** :
| Capability | When to use |
|-----------|-------------|
| `Cap::all()` | Sources that produce universally dangerous data |
| `Cap::SHELL_ESCAPE` | Shell command injection sinks/sanitizers |
| `Cap::HTML_ESCAPE` | XSS sinks/sanitizers |
| `Cap::URL_ENCODE` | URL injection sinks/sanitizers |
| `Cap::JSON_PARSE` | JSON parsing sanitizers |
| `Cap::FILE_IO` | File I/O sinks |
| `Cap::FMT_STRING` | Format string sinks |
| `Cap::ENV_VAR` | Environment/config data sources |
5. **Matcher semantics** :
- Case-insensitive suffix matching by default.
- If a matcher ends with `_` , it acts as a prefix match.
- Multiple matchers in one rule are alternatives (any match triggers the rule).
### User-defined rules (no code change needed)
Users can add taint rules via config:
```toml
[[analysis.languages.javascript.rules]]
matchers = ["dangerouslySetInnerHTML"]
kind = "sink"
cap = "html_escape"
```
Or via CLI:
```bash
nyx config add-rule --lang javascript --matcher dangerouslySetInnerHTML --kind sink --cap html_escape
```
---
## How to Add a New Language
Adding a new language requires changes across several modules. Use an existing language (e.g. Go or Python) as a template.
### Checklist
1. **Tree-sitter parser** : Add `tree-sitter-<lang>` to `Cargo.toml` .
2. **Language registration** : Register the parser in `ast.rs` (language detection from file extension, parser initialization).
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
3. **CFG node kinds** : Create `src/labels/<lang>.rs` with a `KINDS` map that maps tree-sitter node types to the internal `Kind` enum (`Block` , `If` , `While` , `For` , `Return` , `CallFn` , `CallMethod` , `Assignment` , etc.).
4. **Parameter extraction** : Add a `PARAM_CONFIG` constant specifying how to extract function parameters from the AST (field name for parameter list, node type for individual parameters, extraction field for parameter names).
5. **Label rules** : Add `RULES` (sources, sinks, sanitizers) and `TERMINATORS` to the labels file.
6. **AST patterns** : Create `src/patterns/<lang>.rs` with a `PATTERNS` constant.
7. **Registry updates** :
- `src/patterns/mod.rs` — add to the `REGISTRY` HashMap
- `src/labels/mod.rs` — add to the `classify()` dispatch
8. **File extension mapping** : Add the extension in `ast.rs` .
9. **Tests** : Write unit tests and add test fixtures.
---
## Testing
### Unit Tests
All tests are inline `#[test]` blocks inside source modules. Run them with:
```bash
cargo test --bin nyx
```
### What to Test
- **New AST patterns**: Ensure the tree-sitter query matches the intended construct and does not match safe alternatives.
- **New taint rules**: Verify that source-to-sink flows are detected and that sanitizers properly neutralize findings.
- **New CFG rules**: Test that guard dominance logic correctly suppresses findings when guards are present.
- **Edge cases**: Empty files, files with syntax errors (tree-sitter is error-tolerant), deeply nested structures.
### Linting
CI runs Clippy with strict settings. Before submitting:
```bash
cargo clippy --all -- -D warnings
```
---
## Pull Request Guidelines
1. **Branch from `master`** . Use descriptive branch names: `feat/add-kotlin-support` , `fix/false-positive-sql-concat` , `docs/update-rule-reference` .
2. **Keep PRs focused** . One logical change per PR.
3. **Ensure CI passes** :
2025-06-25 00:24:05 +02:00
```bash
2026-02-25 21:16:36 -05:00
cargo test --bin nyx
cargo clippy --all -- -D warnings
cargo fmt -- --check
2025-06-25 00:24:05 +02:00
```
2026-02-25 21:16:36 -05:00
4. **Commit style** : Use [Conventional Commits ](https://www.conventionalcommits.org/ ).
```
feat(patterns): add Python subprocess.Popen pattern
fix(taint): prevent false positive on sanitized innerHTML
docs(rules): update JavaScript rule reference
```
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
5. **Document new rules** . If you add patterns or taint rules, update the corresponding `docs/rules/<lang>.md` page.
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
6. **Include test cases** for any new detection rules.
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
---
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
## Bug Reports
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
Please [open an issue ](https://github.com/elicpeter/nyx/issues ) for:
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
- **Crashes or panics** — include the backtrace (`RUST_BACKTRACE=1 nyx scan .` )
- **False positives** — include the minimal code snippet, rule ID, and Nyx version
- **False negatives** — describe what you expected Nyx to find and why
- **Documentation errors** — point to the specific page and what's wrong
2025-06-25 00:24:05 +02:00
---
2026-02-25 21:16:36 -05:00
## Feature Requests
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
We welcome well-motivated feature proposals. Please describe:
1. **Problem statement** — what pain point does this solve?
2. **Proposed solution** — high-level description, optionally with pseudo-code.
3. **Alternatives considered** — why existing functionality is not enough.
2025-06-25 00:24:05 +02:00
---
2026-02-25 21:16:36 -05:00
## Release Process
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
1. Update version in `Cargo.toml` .
2. Update `CHANGELOG.md` with the new version section.
3. Run full test suite: `cargo test --bin nyx && cargo clippy --all -- -D warnings` .
4. Create a git tag: `git tag v0.x.y` .
5. Push tag: `git push origin v0.x.y` .
6. CI builds release binaries and publishes to crates.io.
2025-06-25 00:24:05 +02:00
---
2026-02-25 21:16:36 -05:00
## Security Issues
Please do **not** open public issues for security-sensitive bugs. See [SECURITY.md ](SECURITY.md ) for our responsible disclosure process.
---
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
## License
2025-06-25 00:24:05 +02:00
2026-02-25 21:16:36 -05:00
By contributing to Nyx, you agree that your contributions will be licensed under the [GPL-3.0 ](./LICENSE ).