Rules

Analysis Mode

Use xgrep's built-in native analyzers via mode: analysis rules.

Analysis Mode

Some checks are too context-dependent to express as a pattern. "Is this imported name ever used?", "does this self.x read refer to an attribute the class never defines?", "is this regex vulnerable to ReDoS?" — answering these accurately needs whole-file or semantic analysis, not text/AST shape matching. xgrep ships these as native analyzers, dispatched with mode: analysis.

A pattern rule that tries to approximate one of these checks is almost always a false-positive factory; the native analyzer is the precise implementation.

Writing an analysis-mode rule

Set mode: analysis and name an analyzer with analyzer:. The rule carries no pattern — the analyzer produces all findings:

rules:
  - id: my-undefined-attribute-rule
    languages: [python]
    severity: ERROR
    message: Attribute is never defined on the class; this raises AttributeError.
    mode: analysis
    analyzer: undefined-attribute
    metadata:
      category: correctness

Rules of the mode (enforced at load time, mirroring how mode: taint forbids pattern):

  • analyzer: is required and must name a known analyzer — an unknown name is a load error, not a silent no-op.
  • A pattern clause (pattern, pattern-regex, pattern-either, …) is not allowed alongside mode: analysis. Writing analyzer: alone implies mode: analysis.
  • languages: still scopes which files the rule runs on; each analyzer is also internally gated to the language(s) it supports.

Built-in analyzers

analyzer:LanguagesDetects
unused-importpythonImported names never referenced in the file.
unreachable-codepythonStatements after a return/raise/break/continue in the same suite.
non-callablepythonA call whose callee is provably not callable.
no-effectpythonA bare comparison/arithmetic statement whose result is discarded.
inconsistent-returnpythonA function that returns a value on some paths and None/implicitly on others.
conflicting-signaturepythonA method overriding a local base method with an incompatible arity.
equality-typespython==/!= between operands of known incompatible types (b"x" == "x").
wrong-arg-constructorpythonA constructor / self-method call with an argument count __init__ cannot accept.
wrong-arg-callpythonA module-function call with an incompatible positional-argument count.
first-param-not-selfpythonAn instance method whose first parameter is not self.
invalid-escapepythonA non-raw string/bytes literal with an unrecognized backslash escape ("\d").
undefined-attributepythonA self.x read where x is never defined on the class or a local base.
use-before-defpythonA local variable read on a path where it is never bound first (UnboundLocalError).
empty-exceptpythonA try whose except handler body is a single pass, silently swallowing the error (CWE-390).
redundant-comparisonpythonA comparison made constant by a preceding elif/guard over the same operands (CWE-570).
duplicate-bindingjavascript, typescriptA name bound twice in one parameter list or destructuring pattern.
prototype-pollutionjavascript, typescriptA for…in / Object.keys() copy into an object without prototype-key guards.
incomplete-url-schemejavascript, typescriptA scheme allowlist recognizing some but not all of javascript:/data:/vbscript:.
incomplete-url-substringjavascript, typescriptA substring hostname check not anchored to the URL's host.
incomplete-html-sanitizationjavascript, typescriptA .replace() chain missing some attribute-breaking characters.
misspelled-identifierjavascript, typescriptAn identifier word-part edit-distance-1 from a more common part used nearby.
unsafe-cert-trustjavaTLS endpoint identification disabled or never enabled.
implicit-pending-intentjavaA mutable implicit PendingIntent reaching a broadcast/activity sink (CWE-927).
sensitive-broadcastjavaSensitive data broadcast in Intent extras without a permission (CWE-927).
insecure-basic-authjavaA Basic auth header sent after a plaintext http:// URL (CWE-522).
lock-orderjavaInconsistent lock acquisition order across methods (deadlock, CWE-833).
type-narrowingjavaA compound assignment narrowing a wider type (int += long, CWE-190).
ruby-incomplete-sanitizationrubyA .sub/.sub! that removes only the first occurrence of a metacharacter.
regex-hostname-dotanyAn unescaped . in a hostname regex.
regex-unanchored-hostnameanyA hostname regex lacking start/end anchors.
regex-semi-anchoredanyAn anchor (^/$) that binds only one alternation branch (^a|b).
regex-useless-escapeanyUnnecessary backslash escapes in a regex.
regex-bad-tag-filteranyA regex HTML/XML tag filter that can be bypassed.
regex-unmatchable-caretanyA ^ in a position where it can never match.
regex-unmatchable-dollaranyA $ in a position where it can never match.
regex-suspicious-rangeanyA suspicious character range in a […] class (cross-case, digit-to-letter).
regex-suspicious-characteranyA suspicious escape such as \a (bell) or \b (backspace) in a regex.

The any-language regex analyzers are scoped by the rule's own languages: field and adapt to the language internally (for example, Ruby hostname anchors are \A/\z rather than ^/$).

Configuration

Analysis-mode rules take no parameters in metadata. When an analyzer needs to vary, it does so by analyzer identity (the regex kinds are separate analyzer: names rather than a kind option) or by intrinsic behaviour. A future structured options: block is the planned home for any genuinely rule-configurable value.

See also

On this page