Metadata-Version: 2.4
Name: silentguard
Version: 0.1.0
Summary: Detect silent failures and unexpected runtime side effects during development and CI.
Author: SilentGuard Contributors
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: click>=8.0
Requires-Dist: rich>=13.0
Provides-Extra: dev
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: pyperf>=2.7; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1; extra == 'dev'
Provides-Extra: test
Requires-Dist: pytest-cov>=4.0; extra == 'test'
Requires-Dist: pytest>=7.0; extra == 'test'
Description-Content-Type: text/markdown

# SilentGuard

> `pip install silentguard` — detect silent failures and unexpected runtime side effects during local development and CI.

[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)

---

## The Problem

Developers often catch exceptions too broadly, suppress failures unintentionally, or trigger hidden side effects — global state mutation, file I/O, network calls, subprocess execution, environment changes. These issues are hard to spot in tests and code review.

SilentGuard surfaces these behaviours with clear, actionable runtime reports.

## Quick Start

```bash
pip install silentguard
```

### As a Decorator

```python
from silentguard import guard

@guard()
def process_data():
    try:
        result = risky_operation()
    except Exception:
        pass  # SilentGuard flags this
    
    os.environ["API_KEY"] = "secret"  # flagged
```

### As a Context Manager

```python
from silentguard import guard

with guard(report_format="markdown", output_path="report.md") as session:
    do_something_risky()

print(f"Detected {len(session.events)} events")
```

### CLI — Run Under Instrumentation

```bash
# Run a script with full instrumentation
silentguard guard my_script.py

# Run a module
silentguard guard my_package.module

# Hide LOW-confidence noise
silentguard guard my_script.py --min-confidence medium

# Lower overhead (disable trace hook)
silentguard guard my_script.py --no-trace
```

### CLI — Static Audit

```bash
# Scan a file
silentguard audit app.py

# Scan a directory
silentguard audit src/

# Output as HTML
silentguard audit src/ --format html --output audit.html
```

### CLI — Run Tests

```bash
# Run pytest with SilentGuard
silentguard test

# With pytest args
silentguard test -- -x -v tests/

# With report format
silentguard test --format markdown --output test_report.md

# Reduce noise and fail CI on actionable findings
silentguard test --min-confidence medium --fail-on high
```

### pytest Integration

```bash
# Directly with pytest (plugin auto-discovered)
pytest --silentguard

# With format options
pytest --silentguard --sg-format=html --sg-output=report.html
```

## What It Detects

| Category | Detection Method | Confidence |
|---|---|---|
| **Suppressed exceptions** | `sys.monitoring` (3.12+) or `sys.settrace` fallback (3.11) | HIGH for broad `except Exception`, MEDIUM for specific types, skips control-flow (`StopIteration`, etc.) |
| **File I/O** | `sys.addaudithook` — `open` events | HIGH for writes, MEDIUM for reads |
| **Network calls** | `sys.addaudithook` — `socket.connect`, `socket.getaddrinfo` | HIGH |
| **Subprocess execution** | `sys.addaudithook` — `subprocess.Popen`, `os.system` | HIGH |
| **Env variable mutation** | `sys.addaudithook` — `os.putenv`, `os.unsetenv` | HIGH |
| **Thread spawning** | `sys.addaudithook` — `_thread.start_new_thread` | MEDIUM |
| **Global state mutation** | AST analysis — `global` statements | MEDIUM (audit only) |

### Static Audit (AST-based)

The `audit` command performs additional best-effort checks:

- Bare `except:` clauses
- Broad `except Exception:` with silent bodies (`pass` / `...`)
- Calls to `exec()`, `eval()`, `os.system()`, `subprocess.*`
- `global` statements indicating module state mutation
- Imports of networking modules (`socket`, `requests`, etc.)

## Output Formats

| Format | Use Case |
|---|---|
| `terminal` | Interactive development (default, Rich-powered) |
| `markdown` | Issue trackers, PR comments, postmortems |
| `html` | Self-contained single-file reports for sharing |
| `sarif` | GitHub Code Scanning / CI pipelines |

## Project Configuration

SilentGuard auto-discovers a `.silentguardrc` file by walking up from the
current working directory. This lets a project tune noise levels and ignore
known-benign patterns without changing application code.

```toml
[runtime]
detect_asyncio = true

[allow]
files = ["*.log", "*/.pytest_cache/*"]
network_hosts = ["localhost"]

[filters]
min_confidence = "medium"
ignore_file_patterns = ["*/vendor/*"]
ignore_function_patterns = ["legacy_*"]

[categories]
disabled_events = ["thread_spawn"]
disabled_scan = ["bare_except"]
```

CLI flags override config values when you need a one-off run:

```bash
silentguard guard app.py --min-confidence high
silentguard audit src/ --min-confidence medium
silentguard test --min-confidence medium --fail-on high
```

## CI / Code Scanning

```bash
# Emit SARIF for GitHub Code Scanning or other CI integrations
silentguard ci src/ --output silentguard.sarif --fail-on high
```

The repository also includes a GitHub Actions workflow in
`.github/workflows/silentguard.yml` that runs both static audit and pytest
instrumentation and uploads SARIF results.

## Configuration

All detection categories can be toggled:

```python
from silentguard import guard

@guard(
    detect_suppressed_exceptions=True,   # default: True
    detect_file_io=False,                # disable file I/O detection
    detect_network=True,
    detect_subprocess=True,
    detect_env_mutation=True,
    detect_threading=True,
    report_format="markdown",
    output_path="report.md",
    allowed_file_patterns=[".log", "/tmp/"],  # ignore these paths
    allowed_network_hosts=["localhost"],       # ignore these hosts
)
def my_function():
    ...
```

Runtime reports automatically drop SilentGuard internals and stdlib/importlib-only
frames from stack summaries so common interpreter noise does not dominate output.

## Architecture

```
silentguard/
├── __init__.py          # Public API
├── __version__.py       # Version metadata
├── core.py              # GuardSession + SourceScanner
├── decorators.py        # @guard() decorator / context manager
├── hooks.py             # sys.addaudithook + sys.settrace management
├── reporter.py          # Terminal / Markdown / HTML formatters
├── cli.py               # Click CLI
├── pytest_plugin.py     # pytest plugin (auto-discovered)
└── utils.py             # Shared data structures and helpers
```

## Limitations & Known Issues

### By Design

- **Best-effort diagnostics, not a formal verifier.** SilentGuard uses runtime heuristics. It will miss some issues and may flag intentional behaviour.
- **Confidence levels are heuristic.** A HIGH confidence event is not *certain* to be a bug — it means the structural pattern is commonly problematic.
- **`sys.addaudithook` is permanent.** Once installed, CPython audit hooks cannot be removed for the lifetime of the process. SilentGuard minimises impact with a fast no-op guard when inactive.
- **Tracing adds overhead.** On Python 3.11, suppressed-exception detection uses `sys.settrace`; on 3.12+, SilentGuard uses `sys.monitoring` fast-path where available and falls back to `sys.settrace`.

#### Overhead Benchmarks (pyperf)

Measured on Python 3.13 (Windows laptop), workload: 1000 pure-Python function calls (`benchmarks/workload.py`), with and without instrumentation.

- Baseline (no instrumentation): ~3.97 ms
- SilentGuard with `sys.monitoring`: ~6.48 ms (about 1.6x baseline)
- SilentGuard forced `sys.settrace`: ~418.97 ms (about 105x baseline)

For this workload, `sys.monitoring` is roughly **65x faster than `sys.settrace`** (`418.97 / 6.48`).

### Detection Limits

- **Suppressed exceptions:** Cannot distinguish between intentional handling (e.g. `try: d[k] except KeyError: default`) and genuine suppression. Mitigated by confidence levels.
- **Global state mutation at runtime:** Not detected beyond environment variables. The AST scanner catches `global` statements but cannot track arbitrary attribute mutations.
- **Asyncio side effects:** Limited to thread-spawning detection. Full coroutine-level instrumentation is out of scope for v1.
- **Indirect network calls:** Calls through C extensions or FFI that bypass Python's socket layer are invisible to audit hooks.
- **File I/O noise:** Python's own import machinery triggers `open` events. SilentGuard filters `.pyc`/`.pyo`/`.so` files and its own internals, but some noise may remain.

### Platform & Version

- Requires **Python 3.11+**.
- `sys.addaudithook` is available since Python 3.8.
- `sys.settrace` behaviour may vary between CPython versions; not supported on alternative interpreters (PyPy, GraalPy).

## Development

```bash
# Clone and install in editable mode
git clone https://github.com/your-org/silentguard.git
cd silentguard
pip install -e ".[dev]"

# Run tests
pytest

# Run with SilentGuard's own plugin
pytest --silentguard

# Type checking
mypy src/

# Linting
ruff check src/ tests/

# Benchmarks
pip install pyperf
python benchmarks/pyperf_silentguard.py
```

## License

MIT — see [LICENSE](LICENSE).

