Skip to main content

🛡️ C-GULL: Code Guardian for Unchecked Logic & Leaks

Python Version License Security Standards Tests

C-GULL (Code Guardian for Unchecked Logic & Leaks) is a lightweight, modular C source code static security analyzer designed to assist with identifying common C memory vulnerabilities, buffer overflow risks, format string flaws, timing side-channel patterns, and select MISRA-C compliance guidelines.

Built for both lightweight regex scans and AST-assisted analysis (using a built-in C structural parser, cross-checked by pycparser when available -- see "AST Engine Notes" below), C-GULL generates vulnerability reports in JSON, OASIS SARIF v2.1.0, Markdown, and colored terminal formats.


🚀 Key Features

  • ⚡ Dual Analysis Engine:
    • Lightweight Regex Pattern Matching: fast first-pass scanning for banned API calls, format strings, unsafe casts, and suspicious macros. Runs against a comment-stripped and string-literal-masked view of the source to reduce basic false matches.
    • AST & CFG-Assisted Structural Analysis: structural and control-flow aware pattern checks for unchecked malloc returns, missing pointer NULL checks, use-after-free (now branch, loop, and switch-sensitive), VLAs, and control flow patterns, built on a lightweight in-repo C parser and cross-checked with pycparser where supported.
  • 🛠️ Project Configuration File (TOML): Auto-discovered .cgull.toml or pyproject.toml [tool.cgull] for project-wide rule skipping, per-rule severity overrides, path exclusions, output defaults, custom allocator/deallocator synonyms (e.g. xmalloc/xfree, kmalloc/kfree), and custom banned function wrappers.
  • 🔇 Inline Suppression: silence specific findings with // cgull-ignore, // cgull-disable-next-line CGULL-007, /* cgull-disable-line CGULL-019 */, or // cgull-ignore-next-line: CGULL-001,CGULL-003.
  • ⚙️ Parallel Scanning: -j/--jobs scans multiple files concurrently across CPU cores for larger codebases.
  • 📏 Baseline / Diff Mode: --baseline/--update-baseline let CI enforce "no new issues" on an existing, imperfect codebase instead of requiring it to already be fully clean -- see "Baseline / Diff Mode" below.
  • 📁 Recursive Directory Scanning: Automatically discovers and audits C source and header files (.c, .h, .hpp) across nested codebases.
  • 🚫 .cgullignore Support: Exclude vendor libraries, third-party dependencies, build output directories, or test mock files using standard gitignore glob patterns and negations (!).
  • 📊 Multi-Format Reporting:
    • Structured JSON for automated ingestion and reporting dashboards.
    • SARIF 2.1.0 for native integration into GitHub Code Scanning and GitLab SAST.
    • Executive Markdown summary tables with remediation guides and clear distinction between automatic fixes, suggested fixes, and manual review items.
  • 🧩 Extensible & Modular Architecture: Add new custom regex or AST rules with a clean object-oriented class interface.
  • 🧪 Test Suite: unittest suite covering security rules plus regression tests for known false-positive patterns.

AST Engine Notes (please read before trusting AST-tagged findings)

pycparser cannot parse raw, unpreprocessed C — it has no definition for size_t, uint32_t, or anything from an #included header, and chokes on macro-dependent syntax. C-GULL uses a three-tier strategy to maximize the fraction of files that get a real AST parse:

  1. pcpp + pycparser (best, when both are installed): pcpp is a pure-Python C99 preprocessor that expands #define macros and evaluates #ifdef/#if conditionals within the file, producing output that pycparser can parse. This handles the common case of code that depends on in-file macro definitions (e.g. #define BUF_SIZE 128 → char buf[BUF_SIZE];). #include directives for unavailable headers are silently passed through and stripped before parsing.

  2. Directive stripping + pycparser (good, when only pycparser is installed): Preprocessor directives are stripped and a small typedef prelude is injected. This works for code that does not structurally depend on macro expansion.

  3. Regex extractor (fallback, always available): A lightweight regex/brace-counting extractor for function and variable structure. Weaker (e.g. it cannot represent multi-declarator lines like int a, b, c; on its own) but never crashes the scan.

In all cases, files that rely on external header definitions, project-specific typedefs, or complex macro expansion patterns may still fail the AST parse and fall back to the regex extractor. Each file's result surfaces its exact parse tier (pcpp+pycparser, directive-stripped, or regex-fallback) in file_summaries. CI pipelines can enforce full AST parsing by passing --warn-on-fallback (or setting warn_on_fallback = true in config) to fail the build whenever a file falls back to regex-fallback mode.


📦 Installation

Option 1: Direct Python Execution (Zero External Dependencies)

C-GULL's core engine runs on standard Python 3.10+ with no third-party dependencies required:

# Clone the repository
git clone https://github.com/sahebbiswas/cgull.git
cd cgull

# Run directly via Python module
python3 -m cgull scan src/

Option 2: Install via pip (Editable Development Mode)

pip install -e .

Option 3: Optional AST Parser Enhancement (pycparser + pcpp)

# Install both pycparser and pcpp for best AST analysis:
pip install -e ".[ast]"

# Or install individually:
pip install pycparser   # AST parsing
pip install pcpp        # C preprocessor (macro expansion)

💻 CLI Usage & Commands

Basic Scan

# Scan current directory recursively (auto-discovers .cgull.toml or pyproject.toml)
cgull scan .

# Scan specific source directory with an explicit config file
cgull scan src/ --config .cgull.toml

# Scan a single C file
cgull scan main.c

Generating JSON Reports

# Export audit report to JSON
cgull scan src/ -o security-report.json --format json

Generating SARIF Reports for CI/CD

# Export SARIF for GitHub Advanced Security / GitLab SAST
cgull scan src/ -o results.sarif --format sarif

Filtering by Severity & Engine

# Scan only High impact vulnerabilities using fast Regex engine
cgull scan src/ --severity high --engine regex

# Run deep AST inspection on entire codebase
cgull scan src/ --engine ast

Configuration Space Expansion Strategies

# Scan using pairwise covering array strategy over preprocessor flag pairs
cgull scan src/ --config-strategy pairwise

# Scan using bounded-exhaustive expansion
cgull scan src/ --config-strategy exhaustive --exhaustive-threshold 8

Parallel Scanning

# Scan sequentially in-process (default)
cgull scan src/ -j 1

# Scan files across 4 worker processes
cgull scan src/ -j 4

# Auto-detect and use all available CPU cores (0)
cgull scan src/ -j 0

-j/--jobs options:

  • 1: sequential in-process scanning (default)
  • N > 1: parallel scanning across N worker processes
  • 0: auto-detect and use all available CPU cores
  • Negative values (e.g. -j -1): invalid, produces an error

Project Configuration File (.cgull.toml / pyproject.toml)

C-GULL supports auto-discovered project configuration via a standalone .cgull.toml file or a [tool.cgull] table inside pyproject.toml. Explicit --config path/to/config.toml overrides auto-discovery.

# .cgull.toml (schema_version = 1)
schema_version = 1

[rules]
# Rule IDs to skip entirely, mapped to justification strings
skip = { "CGULL-019" = "team does not follow MISRA explicit-(void) style" }

[rules.severity]
# Per-rule severity overrides
CGULL-024 = "high"   # hardcoded secrets: always treat as high
CGULL-020 = "low"    # unused arguments: downgrade to low

[functions.memory]
# Extends built-in malloc/calloc/realloc/free recognition
# for unchecked allocation (CGULL-003) and use-after-free (CGULL-022 / CGULL-027)
alloc   = ["xmalloc", "kmalloc", "OPENSSL_malloc", "talloc"]
realloc = ["xrealloc", "krealloc"]
dealloc = ["xfree", "kfree", "OPENSSL_free", "talloc_free"]

[functions.banned]
# Custom project-specific banned functions (extends CGULL-001)
[functions.banned.legacy_string_copy]
reason = "in-house wrapper around strcpy() with no bounds checking"
remediation = "use safe_string_copy(dest, sizeof(dest), src) instead"

[paths]
# Combined with .cgullignore (additive)
exclude = ["third_party/", "generated/"]

[output]
default_format = "sarif"
fail_on = "high"
warn_on_fallback = true

Suppressing Findings Inline

strcpy(dest, src);                        // cgull-ignore: CGULL-001
strcpy(dest, src);                        // cgull-ignore              (suppresses every rule on this line)
// cgull-disable-next-line CGULL-007
arr[i] = 0;
/* cgull-disable-line CGULL-019 */
int my_func() {}
// cgull-ignore-next-line: CGULL-001,CGULL-003
strcpy(dest, src);

Baseline / Diff Mode

Adopting a scanner on an existing, imperfect codebase is usually impractical if --fail-on high fails the build on every pre-existing issue on day one. Baseline mode fixes that: snapshot the findings you're accepting for now, then only fail CI on findings introduced after that snapshot.

A baseline file is just an ordinary cgull scan --format json report -- there's no separate format. Findings are matched between scans by a content-based fingerprint (rule + file + normalized code snippet), not line number, so unrelated edits elsewhere in a file won't make an untouched finding look "new".

# 1. Snapshot current findings as the accepted baseline (commit this file)
cgull scan src/ --update-baseline .cgull-baseline.json

# 2. In CI: only fail on issues introduced since the baseline
cgull scan src/ --baseline .cgull-baseline.json --fail-on medium

# --update-baseline and --baseline can be combined in one invocation to
# both check against the current baseline AND immediately refresh it:
cgull scan src/ --baseline .cgull-baseline.json --update-baseline .cgull-baseline.json --fail-on medium

The report (any format) shows both the new-issue count and how many baseline findings have since been resolved:

Baseline Diff    : 12 total, 1 new, 3 resolved since baseline

Note: run the baseline snapshot and the later diffed scan with the same --severity/--engine flags. A baseline captured with --severity all compared against a later scan run with --severity high will make the filtered-out medium/low findings look "resolved" even though they were just excluded, not fixed.

CI/CD Pipeline Enforcement (Fail on Severity Threshold)

# Exits with non-zero exit code if issues at or above severity threshold are found
cgull scan src/ --fail-on high
cgull scan src/ --fail-on medium
cgull scan src/ --fail-on low

# Note: --fail-on-high is supported as a backward-compatible alias for --fail-on high
cgull scan src/ --fail-on-high

# Exits with code 1 if AST parsing falls back to regex-fallback mode for any file
cgull scan src/ --warn-on-fallback

Listing All Active Rules

cgull rules

Initializing a .cgullignore File

cgull init-ignore

🛡️ Supported Security Rules Matrix

C-GULL implements the security audit rules listed below:

Rule ID Rule Name Impact Category CWE ID Method Description
CGULL-001 Banned Functions High Strings CWE-676 / 120 / 377 Regex Flags insecure legacy functions (gets, strcpy, strcat, sprintf, scanf %s, mktemp, tmpnam, tempnam).
CGULL-002 Format String Vulnerabilities High Strings CWE-134 Hybrid Detects non-literal format strings in printf(buf) and syslog.
CGULL-003 Unchecked Dynamic Allocations High Memory CWE-476 / 252 AST Ensures malloc, calloc, realloc returns are checked against NULL before dereference.
CGULL-004 Missing Null Check on Params High Memory CWE-476 AST Flags pointer parameters dereferenced without prior NULL verification.
CGULL-005 Non-Constant Time Comparison High Crypto CWE-208 / 385 Hybrid Flags memcmp()/strcmp() in cryptographic & authentication contexts (timing attack).
CGULL-006 Arithmetic Integer Overflow High Arithmetic CWE-190 / 680 Hybrid Detects unchecked arithmetic (*, +) in allocation sizes or offsets.
CGULL-007 Array Index Out of Bounds High Memory CWE-129 / 125 Hybrid Identifies unconstrained or out-of-range array index operations.
CGULL-008 Unsafe Sensitive Memory Clearing High Crypto CWE-14 Hybrid Detects memset() on sensitive local buffers before return (Dead Store Elimination).
CGULL-009 Stripping Volatile Qualifiers High Control Flow CWE-562 / 704 Hybrid Prevents unsafe casts removing volatile from MMIO registers or shared state.
CGULL-010 Variable Length Arrays (VLAs) High Memory CWE-400 / 787 AST Forbids runtime variable-sized stack arrays (char buf[len]) to prevent stack smashing.
CGULL-011 Illegal Func Pointer Conversions High Control Flow CWE-843 / 588 Hybrid Prevents function pointer conversions to void* or integers (ROP / CFI mitigation).
CGULL-012 Unsafe Integer Conversions Medium Arithmetic CWE-704 Regex Flags atoi, atol, atoll which lack overflow and error handling (suggests strtol).
CGULL-013 Naked Control Flow Statements Medium Control Flow CWE-483 Hybrid Enforces curly braces {} for if, else, for, while statements (MISRA C:2012).
CGULL-014 Use of Magic Numbers Medium Code Quality CWE-1094 Hybrid Flags hardcoded magic numbers in array declarations and memory allocations.
CGULL-015 Bitwise Ops on Signed Integers Medium Arithmetic CERT INT13-C Hybrid Flags bitwise shifts and masks performed on signed integers (Undefined Behavior).
CGULL-016 Single-Point-of-Failure Control Medium Control Flow CWE-1240 AST Flags simple boolean returns (1/0) in security/auth functions vulnerable to fault glitching.
CGULL-017 Missing default in Switch Low MISRA MISRA 16.4 AST Enforces mandatory default: label in all switch statements.
CGULL-018 Use of goto Statements Low MISRA MISRA 15.1 Regex Flags unstructured goto jumps that bypass initializations.
CGULL-019 Missing void in Parameter Lists Low MISRA MISRA 8.2 AST Requires explicit (void) for functions with empty argument lists (int init(void)).
CGULL-020 Unused Arguments Low Code Quality CWE-563 AST Detects function parameters declared but never referenced in function bodies.
CGULL-021 Uninitialized Pointers High Memory CWE-457 Hybrid Detects wild pointers declared without explicit NULL or memory initialization.
CGULL-022 Use-After-Free High Memory CWE-416 AST Tracks pointer lifecycle to flag dereferencing of freed memory addresses.
CGULL-023 Uninitialized Memory Use High Memory CWE-457 AST Identifies local variables read before explicit assignment.
CGULL-024 Insecure Data Storage Medium Crypto CWE-312 / 798 Regex Flags hardcoded passwords, tokens, and encryption keys in plaintext static memory.
CGULL-025 Missing Assertions Low Code Quality CWE-617 AST Enforces assert() validations for state invariants in critical complex routines.
CGULL-026 Unchecked snprintf() Return Value High Strings CWE-131 Regex Flag the direct accumulation of snprintf() return value into an offset without checking for truncation. snprintf() returns the number of bytes it would have written, leading to underflow if used directly on truncation.
CGULL-027 Double Free High Memory CWE-415 Ast Detect calling free() on a pointer that has already been freed.
CGULL-028 Insecure PRNG for Security Use High Crypto CWE-338 Hybrid Flags rand(), random(), or srand(time(NULL)) calls in security-sensitive contexts.
CGULL-029 sizeof() on Pointer Type High Arithmetic CWE-467 AST Flags use of sizeof() on a pointer variable, which returns the size of the pointer instead of the memory block.
CGULL-030 Command Injection Vulnerability High Control Flow CWE-78 Hybrid Flags use of system(), popen(), or PATH-searching exec variants (execlp/execvp/execvpe/execlpe) with non-literal string arguments.
CGULL-031 Weak/Broken Crypto Primitives High Crypto CWE-327 Hybrid Detects calls to weak or broken cryptographic algorithms (MD5, SHA-1 in security contexts, DES_*, RC4, EVP_* ECB cipher mode variants).
CGULL-032 Realloc-Overwrite Memory Leak High Memory CWE-401 Hybrid Detects x = realloc(x, ...) directly overwriting pointer variable before NULL check, leaking memory on OOM.
CGULL-033 Signed/Unsigned Comparison Medium Arithmetic CWE-195 / INT02-C Hybrid Detects signed/unsigned integer comparisons and loop-bound mismatches causing infinite loops or promotion bugs.
CGULL-034 Division or Modulo by Zero High Arithmetic CWE-369 Hybrid Detect division (/) or modulo (%) operations where the divisor might be zero, causing a crash or undefined behavior.
CGULL-035 TOCTOU File Access High Control Flow CWE-367 Hybrid Detects time-of-check to time-of-use race conditions where file checks (access, stat) are followed by file operations (open, fopen, chmod).
CGULL-036 Memory Leak High Memory CWE-401 Hybrid Detects dynamically allocated memory (malloc, calloc, realloc, strdup, aligned_alloc) assigned to local pointers that is not freed or transferred before function exit paths.
CGULL-037 Improper Null Termination (strncpy) High Strings CWE-170 Regex strncpy() does not guarantee null termination if the source string is larger than or equal to the specified length. This causes out-of-bounds reads/writes.
CGULL-038 Return Stack Variable High Memory CWE-562 AST Detects return statements that expose the address of an automatic-storage local variable or function parameter after the function returns.
CGULL-041 Unused Local Variables Low Code Quality CWE-563 AST Detects local variables declared in function body or nested block scopes that are never referenced anywhere in their scope.
CGULL-042 Dead Stores Low Code Quality CWE-563 AST Detects local variables assigned but never read afterward before reassignment or scope exit (-Wunused-but-set-variable).
CGULL-043 Variable Shadowing Across Nested Scopes Low Code Quality CWE-398 / MISRA Rule 5.3 AST Detects variable declarations in inner scopes (parameters, nested blocks, or local variables) that shadow identifiers in outer scopes (MISRA C:2012 Rule 5.3).

🚫 .cgullignore Specification

Create a .cgullignore file in your project root to exclude directories, files, or specific pattern masks:

# Exclude vendor dependencies and third-party code
vendor/
third_party/
deps/

# Exclude build artifacts
build/
dist/
*.o
*.obj
*.so

# Exclude generated code and temporary test files
generated_*.c
test/mocks/
temp_*.c

# Negation rule: re-include a specific critical security file inside vendor
!vendor/crypto/secure_memcmp.c

Negation Semantics and Traversal Behavior

  • .cgullignore supports gitignore-style negation rules (!). Later patterns override earlier matching rules for the same path (last match wins).
  • Directory Traversal Note: Unlike native git (which prunes excluded directories during directory walking and thus cannot re-include files inside an ignored parent directory), C-GULL traverses nested directories so that negation rules (!) can re-include specific files or subdirectories underneath an ignored parent directory (e.g. !vendor/crypto/secure_memcmp.c inside vendor/).

🧩 Extending Ruleset: Creating Custom Rules

C-GULL's modular API allows you to implement custom organization-specific security rules in minutes:

# my_custom_rule.py
from cgull.rules.base import BaseRule
from cgull.models import Severity, RuleCategory, Issue, AnalysisEngine

class NoInsecureRandRule(BaseRule):
    rule_id = "CUSTOM-001"
    name = "Insecure PRNG rand() Call"
    impact = Severity.HIGH
    category = RuleCategory.CRYPTO
    description = "rand() and srand() are not cryptographically secure."
    cwe_id = "CWE-338"
    remediation_suggestion = "Use getrandom(), arc4random(), or OpenSSL RAND_bytes()."
    analysis_engine = AnalysisEngine.REGEX

    def scan_line(self, file_path, line_number, line_content, full_code, source_lines):
        issues = []
        if "rand()" in line_content:
            issues.append(self.create_issue(
                file_path=file_path,
                line_number=line_number,
                code_snippet=line_content,
                message="Use of predictable PRNG 'rand()'. Vulnerable to seed recovery.",
                engine="Regex"
            ))
        return issues

Register it in cgull.rules.ALL_RULES to activate it globally across CLI and Web tools!


🧪 Running the Test Suite & Coverage Standards

C-GULL enforces a dual test coverage policy in CI to guarantee both Python code implementation quality and static security rule analyzer correctness.

1. Raw Line Coverage Threshold

CI enforces a minimum code line coverage threshold of 88.0% across the cgull package (configured via pyproject.toml and --cov-fail-under=88).

# Run unit test suite with coverage enforcement
pytest -v --cov=cgull --cov-report=term-missing --cov-fail-under=88

# Or run specific test modules
python3 -m unittest tests/test_scanner.py

2. Security Rule Behavioral Coverage

While raw line coverage ensures execution paths are exercised, rule behavioral coverage is the primary quality metric for analyzer correctness. Behavioral tests verify that each rule correctly detects true positives and avoids false positives on real C code samples annotated with exact line expectations (// expect: CGULL-xxx).

CI enforces a minimum Rule Behavioral Coverage threshold of 40.0% across all registered rules (with rule suites in tests/rules/).

# Run standalone Security Rule Behavioral Corpus runner with coverage threshold
python3 tests/run_corpus.py --min-coverage 40.0

# Verify a single rule in isolation
python3 tests/run_corpus.py --rule CGULL-003

Coverage thresholds are maintained as minimum baselines and are increased deliberately over time as new rules and corpus test suites are implemented.


🎯 NIST Juliet Detection-Quality Benchmark

C-GULL includes an automated, manifest-driven NIST Juliet static analysis detection quality benchmark suite (benchmarks/run_juliet.py) to measure vulnerability detection precision, recall, and F1 score against an independent ground-truth oracle across 4 core CWE scope areas (CWE-476, CWE-690, CWE-416, CWE-457) and 9 control-flow categories (baseline, if/else, nested conditionals, loops, switch, fallthrough, break / continue, goto, interprocedural cases).

Running the Benchmark

# Run full Juliet benchmark suite (36 test cases)
python3 benchmarks/run_juliet.py

# Run focused subset in CI
python3 benchmarks/run_juliet.py --ci

# Filter by specific CWE or category
python3 benchmarks/run_juliet.py --cwe CWE-476
python3 benchmarks/run_juliet.py --category if/else

# Reproduce an individual Juliet test ID
python3 benchmarks/run_juliet.py --test-id CWE476_NULL_Pointer_Dereference__01_baseline

# Export markdown or JSON formatted reports
python3 benchmarks/run_juliet.py --format markdown --output juliet_report.md

📌 Versioning Policy & Output Schema Versioning

C-GULL maintains clear stability boundaries by separating the Package Version from the Finding / Output Schema Version:

Package Versioning (cgull.__version__)

  • Follows Semantic Versioning (SemVer 2.0.0) (MAJOR.MINOR.PATCH).
  • PATCH increments for bug fixes, performance improvements, and internal refactoring that do not break CLI options or python API behavior.
  • MINOR increments for new static analysis rules, features, CLI options, or backward-compatible API additions.
  • MAJOR increments for breaking changes to Python module APIs or CLI flag semantics.

Output Schema Versioning (schema_version)

  • The JSON output schema includes an explicit top-level "schema_version" field (currently "1").
  • Finding / Output Schema Version changes independently from the package version.
  • Backward-compatible schema additions (such as adding new optional fields to issue or summary blocks) do not increment schema_version.
  • Backward-incompatible schema changes (such as removing fields, renaming top-level keys, or restructuring issue lists) will increment schema_version (e.g. from "1" to "2").

📄 Output Schema (JSON Example)

{
  "schema_version": "1",
  "meta": {
    "tool": "C-GULL",
    "version": "<VERSION>",
    "timestamp": "2026-08-15T21:45:00Z",
    "target_path": "src/",
    "scan_duration_seconds": 0.0124
  },
  "analysis": {
    "parser": "pycparser",
    "status": "success",
    "status_counts": {
      "pycparser-success": 4,
      "fallback-parser": 0,
      "parse-failed": 0
    }
  },
  "summary": {
    "files_discovered": 6,
    "files_analyzed": 4,
    "files_ignored": 2,
    "files_failed": 0,
    "scanned_files_count": 4,
    "total_lines_of_code": 348,
    "total_issues_count": 5,
    "high_severity_count": 3,
    "medium_severity_count": 1,
    "low_severity_count": 1,
    "rules_applied_count": 25,
    "ignored_paths_count": 2,
    "failed_paths_count": 0
  },
  "issues": [
    {
      "rule_id": "CGULL-001",
      "rule_name": "Banned Functions",
      "impact": "High",
      "file_path": "src/packet.c",
      "line_number": 42,
      "column_number": 5,
      "code_snippet": "strcpy(dest_buffer, user_input);",
      "message": "Banned insecure function call 'strcpy': strcpy() does not check destination buffer size.",
      "remediation": "Replace with snprintf(dest, sizeof(dest), \"%s\", src)",
      "cwe_id": "CWE-676 / CWE-120",
      "engine": "Regex",
      "fix_type": "suggested_fix",
      "auto_fix_replacement": null,
      "suggested_fix_replacement": "strncpy_s(dest, dest_size, src, _TRUNCATE) or snprintf(dest, sizeof(dest), \"%s\", src)",
      "confidence": "FULL"
    }
  ]
}

🛠️ Remediation & Fix Classification (fix_type)

C-GULL strictly separates mechanically safe transformations from code suggestions and manual architectural reviews:

  • safe_fix (FixType.SAFE_FIX): Mechanically safe transformations that preserve exact program control flow and semantics (e.g., converting printf(user_input) to printf("%s", user_input), adding default: labels to switch blocks, or adding (void) to empty parameter signatures). auto_fix_replacement contains the exact replacement code.
  • suggested_fix (FixType.SUGGESTED_FIX): Illustrative code suggestions or replacement patterns (e.g., replacing strcpy with strncpy_s, converting atoi to strtol, or inserting OOM error checks) that provide actionable guidance but require human evaluation of buffer boundaries or error handling logic. suggested_fix_replacement holds the code suggestion, while auto_fix_replacement remains null.
  • manual_review (FixType.MANUAL_REVIEW): Vulnerabilities requiring human inspection, secret management redesign, or architectural changes where automatic code replacement is unsafe (e.g., hardcoded secrets where automatically replacing with getenv() is not universally correct, or stripping volatile qualifiers). Explanatory text is provided in the remediation field.

⚠️ Known Limitations

  • C++ is NOT supported: C-GULL is strictly designed for C source code (.c, .h, .hpp). C++ features (classes, namespaces, templates, references, operator overloading, etc.) are not supported by the parser or rules engine.
  • Heuristic Static Analysis: C-GULL relies on regex pattern matching and lightweight AST structural checks. It is not a formal verification tool or full symbolic execution solver and may produce false positives or miss complex interprocedural control flow vulnerabilities.
  • Performance on very large or macro-heavy codebases is not yet optimized. Function/variable extraction is currently regex-based (O(n) per file but with a real constant-factor cost), and the pycparser cross-check adds further parse time on top of that. On pathological inputs (e.g. thousands of small functions in one file) total scan time can be significant. Use -j/--jobs to parallelize across files in the meantime; a follow-up pass on the extraction/parse hot path is planned.
  • AST-tagged rules degrade gracefully when pycparser can't parse a file (see "AST Engine Notes" above) -- the report surfaces each file's exact AST parse_tier (pcpp+pycparser, directive-stripped, or regex-fallback) in file_summaries, and CI pipelines can enforce non-degraded AST analysis using --warn-on-fallback.
  • Several rules have a documented high false-positive rate by design trade-off -- use inline suppression (// cgull-ignore) rather than disabling the rule entirely if it's noisy on your codebase but still valuable.
  • Pattern-based rules lack interprocedural taint tracking: Rules operating per-line/per-statement (such as BannedFunctionsRule CGULL-001 and FormatStringRule CGULL-002) flag syntactically risky sinks (like strcpy(data, src) or printf(data)) based on local statement shape. Because they do not perform interprocedural dataflow analysis or track taint across function boundaries, they fire on synthetic benchmark suites (such as Juliet's GoodSource/BadSink or BadSource/GoodSink variants) whenever the sink syntax is unchanged, even if the buffer or format string variable originates from a safe literal or adequately-sized buffer in the "good" variant.
  • Baseline fingerprints are content-based, not a cryptographic identity. Two textually-identical findings from the same rule in the same file (e.g. the same unsafe call copy-pasted at two call sites) share a fingerprint; baseline diffing correctly counts "how many instances are new" via multiset comparison, but if you rename/move code such that the normalized snippet text changes, the finding will look new even though it's the same underlying issue moved elsewhere. Re-running --update-baseline after intentional refactors is the expected workflow.

🔒 Security & Safe Coding Best Practices

C-GULL encourages defense-in-depth secure coding for embedded, systems, and low-level software engineering:

  1. Always compile with -Wall -Wextra -Werror -Wformat=2 -D_FORTIFY_SOURCE=2 -fstack-protector-strong.
  2. Pair static analysis with dynamic fuzzing (AFL++, LibFuzzer) and runtime sanitizers (ASan, UBSan, MSan).
  3. Enforce automated static checks in pre-commit hooks and Pull Request CI workflows.

⚖️ License

Distributed under the Apache License 2.0. See LICENSE for details.

Metadata

Release files for cgull 0.9.8

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cgull 0.9.8
File Size Uploaded
cgull-0.9.8.tar.gz 291.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cgull 0.9.8
File Interpreter ABI Platform
cgull-0.9.8-py3-none-any.whl Python 3 none any Details

Total release size: 605.7 kB

Release files / cgull-0.9.8.tar.gz

Download URL cgull-0.9.8.tar.gz
Size 291.2 kB
Tags Source
SHA-256 checksum
How to use checksums
fb8d37a57489b60144dfb798c931dd23d3e2c734132ebbc271ac3fffde5d39cd
BLAKE2b-256 checksum
How to use checksums
2edbdc8d6a02bf36983bddc91a2d7d86f8d18a0ea34942bad947f97c5e44ca46
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 30, 2026.

Transparency log

Release files / cgull-0.9.8-py3-none-any.whl

Download URL cgull-0.9.8-py3-none-any.whl
Size 314.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ce64cc2d7794390985bb6c899145e055f938bbf2265c366afef73dab0988053c
BLAKE2b-256 checksum
How to use checksums
ef58cfed52b69ee8f4820213b7232ef513089c89163cdfe34317ed245f0bac20
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.9.8 This release

2 release files

0.8.28

2 release files

0.6.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page