Code ScanningSAST by Language

C / C++ SAST

What xgrep detects in C and C++ — memory safety, unbounded/dangerous libc functions, injection, and crypto/TLS misuse — with taint analysis from untrusted input to dangerous sinks.

C / C++ SAST

xgrep parses .c .h and .cpp .cc .cxx .hpp into tree-sitter ASTs and ships roughly 35 rules, 13 of them taint-based, weighted toward the failure modes that dominate C and C++: memory safety and the unbounded libc functions that cause classic buffer overflows.

xgrep scan --include '*.c' --include '*.h' .

What xgrep detects

Memory safetybuffer-overflow, double-free, double-delete (C++ delete/delete[]), use-after-free, null-deref, unchecked-malloc-deref, array-index-out-of-bounds, alloca-untrusted-size, integer-overflow-alloc, writable-executable-memory (W^X).

Dangerous functions & format stringsdangerous-function (unbounded gets/strcpy/strcat/sprintf/vsprintf), scanf-unbounded-read (unbounded %s/%[), format-string (non-constant printf-family format, CWE-134). See Dangerous functions below.

Injectioncommand-injection, sql-injection, path-traversal, ssrf, and (C++) regex-injection. These are taint-based: untrusted input must actually reach the sink.

Crypto & TLSinsecure-tls-version (SSLv2/SSLv3/TLS 1.0/1.1), tls-verification-disabled (libcurl CURLOPT_SSL_VERIFYPEER/VERIFYHOST, Qt, cpr), openssl-verify-none (OpenSSL SSL_VERIFY_NONE, reported as a low-confidence review item — see below), insecure-randomness, and (C++) weak-cipher, weak-hash, predictable-seed.

Filesysteminsecure-file-permissions, insecure-temp-file, toctou-file-access (check-by-path then use-by-path race).

Deserialization & XML (C++)boost-deserialization, xxe.

Dangerous functions

dangerous-function (CWE-676/242/120) flags the inherently unsafe libc calls that write to a destination without regard to its size, the root of countless stack and heap overflows:

FlaggedWhySafe replacement
gets(buf)reads unbounded stdin into buf — no size argument existsfgets(buf, sizeof buf, stdin)
strcpy(dst, src)copies until src's NUL, however longstrlcpy(dst, src, sizeof dst) or snprintf
strcat(dst, src)appends with no bound on the resultstrlcat(dst, src, sizeof dst) or snprintf
sprintf(dst, fmt, …)formats into dst with no size limitsnprintf(dst, sizeof dst, …)
vsprintf(dst, fmt, …)same, for va_listvsnprintf

Prefer strlcpy/strlcat (or snprintf), which always NUL-terminate within the size. strncpy/strncat are not safe drop-ins: strncpy does not NUL-terminate when the source is longer than n, and strncat's length argument is the count to append, not the buffer size — both trade the overflow for a missing-terminator or off-by-one bug if used naively.

This is a use-of-dangerous-function detection: it fires on the call because the function is unsafe by construction, not because a specific overflow has been proven reachable. To keep it actionable rather than noisy, xgrep excludes provably-bounded calls — a string-literal source or format (strcpy(dst, "ok"), sprintf(dst, "hi")) is compile-time-constant and never flagged; only the genuinely unbounded forms (strcpy(dst, user), sprintf(dst, fmt)) are. Treat a finding as a hardening task: switch to the bounded equivalent and always pass the destination size. Where the copy length comes from untrusted input and a concrete overflow is provable, the taint-based buffer-overflow rule reports it at CRITICAL instead.

TLS certificate verification

tls-verification-disabled reports the unambiguous client-side cases at HIGH — libcurl (CURLOPT_SSL_VERIFYPEER/VERIFYHOST turned off), Qt VerifyNone, and cpr VerifySsl{false}. An OpenSSL SSL_VERIFY_NONE is split out into openssl-verify-none and reported at INFO / low confidence instead, because on a TLS server it merely means "don't require a client certificate" (normal), while only on a client is it the certificate-validation bypass the CWE describes — and the two can't be told apart without provenance dataflow, so it is surfaced for review rather than asserted as a vulnerability.

Taint analysis

The injection and memory rules marked above use taint mode: untrusted input — argv/getenv, read/recv/fread, scanf, or an HTTP body — must flow to the sink (system/popen, a SQL API, memcpy/alloca length, an OpenSSL/curl request) before the rule fires. Matching the shape of a call is not enough, which is what keeps command-injection, sql-injection, buffer-overflow, and ssrf precise rather than flagging every system() or memcpy().

See also Supported languages for the full extension matrix and the CLI reference.

On this page