🛡️ C-GULL: Code Guardian for Unchecked Logic & Leaks
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
mallocreturns, 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 withpycparserwhere supported.
- 🛠️ Project Configuration File (TOML): Auto-discovered
.cgull.tomlorpyproject.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/--jobsscans multiple files concurrently across CPU cores for larger codebases. - 📏 Baseline / Diff Mode:
--baseline/--update-baselinelet 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:
unittestsuite 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:
-
pcpp+pycparser(best, when both are installed):pcppis a pure-Python C99 preprocessor that expands#definemacros and evaluates#ifdef/#ifconditionals within the file, producing output thatpycparsercan 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];).#includedirectives for unavailable headers are silently passed through and stripped before parsing. -
Directive stripping +
pycparser(good, when onlypycparseris installed): Preprocessor directives are stripped and a small typedef prelude is injected. This works for code that does not structurally depend on macro expansion. -
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 acrossNworker processes0: 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/--engineflags. A baseline captured with--severity allcompared against a later scan run with--severity highwill 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
.cgullignoresupports 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.cinsidevendor/).
🧩 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., convertingprintf(user_input)toprintf("%s", user_input), addingdefault:labels to switch blocks, or adding(void)to empty parameter signatures).auto_fix_replacementcontains the exact replacement code.suggested_fix(FixType.SUGGESTED_FIX): Illustrative code suggestions or replacement patterns (e.g., replacingstrcpywithstrncpy_s, convertingatoitostrtol, or inserting OOM error checks) that provide actionable guidance but require human evaluation of buffer boundaries or error handling logic.suggested_fix_replacementholds the code suggestion, whileauto_fix_replacementremainsnull.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 withgetenv()is not universally correct, or strippingvolatilequalifiers). Explanatory text is provided in theremediationfield.
⚠️ 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 thepycparsercross-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/--jobsto parallelize across files in the meantime; a follow-up pass on the extraction/parse hot path is planned. - AST-tagged rules degrade gracefully when
pycparsercan't parse a file (see "AST Engine Notes" above) -- the report surfaces each file's exact ASTparse_tier(pcpp+pycparser,directive-stripped, orregex-fallback) infile_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
BannedFunctionsRuleCGULL-001 andFormatStringRuleCGULL-002) flag syntactically risky sinks (likestrcpy(data, src)orprintf(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'sGoodSource/BadSinkorBadSource/GoodSinkvariants) 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-baselineafter 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:
- Always compile with
-Wall -Wextra -Werror -Wformat=2 -D_FORTIFY_SOURCE=2 -fstack-protector-strong. - Pair static analysis with dynamic fuzzing (AFL++, LibFuzzer) and runtime sanitizers (ASan, UBSan, MSan).
- 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)
| File | Size | Uploaded | |
|---|---|---|---|
| cgull-0.9.8.tar.gz | 291.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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