Contributing

Contributing Guide

How to add a new check, and the test requirements for contributions.

StackSentry is open source and contributions are welcome. This guide explains how the codebase is structured and what is required to add a new check.


Adding a New Check

Every check follows the same pattern. Here is the minimum required to add one:

1. Define the check metadata in sec_audit/config.py

"APP-NEWCHECK-001": {
    "id": "APP-NEWCHECK-001",
    "layer": "app",
    "name": "Your check name",
    "severity": "HIGH",
    "owasp": ["A02:2025"],
    "effort": "LOW",
    "impact_weight": 1.5,
    "recommendation": "What to do to fix it.",
}

2. Write the check function in checks/app_checks.py

def check_your_feature(scanner: HttpScanner, verbose: bool = False) -> CheckResult:
    meta = _meta("APP-NEWCHECK-001")
 
    # Guard clause — return WARN if context unavailable
    if not scanner.base_url:
        return CheckResult(**meta, status=Status.WARN,
                           details="No target URL provided.")
 
    # Your detection logic
    response = scanner.get_root()
    if response and "bad_pattern" in response.text:
        return CheckResult(**meta, status=Status.FAIL,
                           details="Bad pattern detected in response body.")
 
    return CheckResult(**meta, status=Status.PASS,
                       details="No issues detected.")

3. Add the check to sec_audit/cli.py

from checks.app_checks import check_your_feature
 
# In the scan execution block:
results.append(check_your_feature(http_scanner, verbose=args.verbose))

4. Write at least three tests

# tests/test_branch_logic.py
 
def test_your_feature_pass(mock_scanner):
    mock_scanner.get_root.return_value = Mock(text="clean response")
    result = check_your_feature(mock_scanner)
    assert result.status == Status.PASS
 
def test_your_feature_fail(mock_scanner):
    mock_scanner.get_root.return_value = Mock(text="bad_pattern found here")
    result = check_your_feature(mock_scanner)
    assert result.status == Status.FAIL
 
def test_your_feature_guard(mock_scanner):
    mock_scanner.base_url = None
    result = check_your_feature(mock_scanner)
    assert result.status == Status.WARN

5. Add a patch template

# remediation/templates.py
 
def _template_app_newcheck_001(stack) -> PatchArtefact:
    return PatchArtefact(
        check_id="APP-NEWCHECK-001",
        filename="fix_app_newcheck_001.py",
        file_type="py",
        content="# Your fix code here\n",
        instructions=["Step 1: ...", "Step 2: ..."],
        verify_cmd="python -c 'import yourapp; print(yourapp.check())'",
    )

Running Tests

pip install -e ".[dev]"
pytest tests/ -v

All 321 tests must pass. A new check should add at minimum 3 tests (PASS, FAIL, WARN/guard).


Code Style

  • Type hints on all function signatures
  • Docstrings on all public functions
  • No bare except: clauses — always catch specific exceptions
  • Status.WARN for missing context, Status.ERROR for unexpected exceptions

Submitting a Pull Request

  1. Fork the repository
  2. Create a branch: git checkout -b feat/your-check-name
  3. Add your check, tests, and template
  4. Run the full test suite
  5. Open a pull request with a description of what the check detects and why it matters All PRs require passing tests and a linked OWASP Top 10:2025 category for the new check.