Variables & Drop-ins¶
Gixy recognizes supported NGINX variables and uses definitions of their possible values when checking for security issues. A variable may contain numbers, fixed cache status strings, or arbitrary text. These differences determine which security checks can report a problem.
For example, the third-party variable $brotli_ratio can contain digits and dots,
or be absent. Gixy records these character restrictions as [0-9.]*: they exclude
CR/LF and slash, while retaining dots for conservative SSRF checks. This follows the
GetPageSpeed ngx_brotli v0.1.4 value-returning function.
Recognizing a name does not prove that the target module is installed or loaded,
or that its output is safe. Unknown names still produce diagnostic messages;
Gixy does not treat every name starting with brotli_ as supported or safe.
Module declarations and bundled coverage¶
Gixy 0.2.59: The module declarations and bundled definitions in this section are available in 0.2.59. The layered catalog behavior below was already available in 0.2.58.
Built-in definitions now cover compression ratios, srcache statuses and expiry,
Echo, VTS counters, the exact GeoIP and SSL names, and documented JWT claims/headers.
They describe possible characters without asserting that a module is available.
Database, certificate, JWT and script values may contain any characters, including
line breaks, so Gixy continues to check them for security issues. Unsupported names
such as $ssl_typo, $geoip_typo and $jwt_type remain unknown. See the coverage matrix with reviewed source versions.
GeoIP2 aliases come from declarations in HTTP-main or stream-main configuration:
http {
geoip2 /usr/share/GeoIP/country.mmdb {
$country default=US source=$remote_addr country iso_code;
$database_epoch metadata build_epoch;
}
server { proxy_set_header X-Country $country; }
}
$country may contain any characters, including line breaks, even with
iso_code or default=US: runtime database values need not be country codes,
and defaults are literal bytes, not expressions. Only exact metadata build_epoch,
metadata last_check and metadata last_change forms restrict values to the
characters used for decimal numbers. Data aliases include empty, missing and
default outcomes.
Gixy never opens the database or evaluates source expressions to infer types.
Local declared names are case-insensitive. The last GeoIP2 declaration in expanded source order wins throughout its main scope, including consumers before it. Outer includes retain normal policy controls; includes inside GeoIP2 are invalid and skipped before any filesystem or dump expansion. HTTP and stream scopes and separate audits stay isolated. Configuration declarations beat catalog drop-ins, even with system policy disabled.
If a main-scope map, geo, njs or Perl declaration uses the same destination name,
Gixy treats that variable as allowing any characters and reports the conflicting
declaration locations. Rename those destinations to establish unambiguous ownership. Local set assignments keep their
ordinary inherited override behavior.
set_by_lua, set_by_lua_file, set_by_lua_block, perl_set, js_set and
js_var return values that Gixy treats as unrestricted text, including line breaks.
Each definition retains the script declaration that supplies its value. Script
bodies and files are never executed or inspected for return values. A writable js_var initializer cannot
permanently constrain its output. Configuration-defined dynamic proxy hosts need
a resolver unless Gixy proves a literal IP or upstream; a regular expression
that describes possible values is never interpreted as a hostname.
Layers and precedence¶
Ordinary CLI scans, including no-argument and stdin scans, load these layers from lowest to highest precedence:
- Built-in variable definitions.
- Vendor/module files in
/usr/share/gixy/vars.d. - Administrator files in
/etc/gixy/vars.d. - Explicit directories from
--vars-dirs, in supplied order (later wins).
Each directory forms its own layer. Files ending in .cfg or .conf are read
in lexical filename order; the last valid definition of a key wins, including
duplicates within one file. Invalid entries retain any valid earlier or lower
definition and warn with the source file and line.
Lookup selects the highest layer containing any match, then an exact name,
then the longest matching prefix ending in _. Thus a higher-layer http_
can override a lower-layer http_host. Within one layer, http_host wins over
http_, and http_special_ wins over http_ for $http_special_value.
This deterministic specificity rule corrects the earlier insertion-order
ambiguity. Variables defined by the analyzed NGINX configuration retain their
existing precedence over the catalog.
Controls and isolation¶
Use one or more explicit directories with the existing comma-separated scalar syntax:
gixy --vars-dirs /etc/gixy/vars,~/.config/gixy/vars
# gixy.cfg: scalar text, without square brackets
vars-dirs = /etc/gixy/vars, ~/.config/gixy/vars
load-default-vars = false
--no-default-vars selects bundled-only policy unless explicit directories are
also supplied. --load-default-vars enables system directories. Opposite flags
cannot be combined. The canonical config key is load-default-vars, and the
environment variable is GIXY_LOAD_DEFAULT_VARS=true or false. Precedence is
CLI > environment > config > mode default, preserving explicit false values.
--write-config preserves an explicit choice using the canonical boolean key.
Explicit directories always augment enabled system layers at higher precedence.
The old /etc/gixy/vars and ~/.config/gixy/vars locations still work when
selected explicitly; they are not searched automatically.
Library Config(load_default_vars=False) and --server default to bundled-only
policy. Operators can enable discovery or pass vars_dirs at startup. Individual
server requests and uploaded NGINX text cannot change this policy or select
policy paths. Each audit gets a fresh catalog, shared with its child contexts;
one audit's custom variable definitions do not affect another. Other global
analyzer state remains, so this is not a general thread-safety guarantee.
File format¶
Each non-empty, non-comment line defines name value; name = value and
name: value also work. Names are ASCII identifiers without $; a trailing
_ declares a prefix. Integer capture variables are not catalog variables.
# /etc/gixy/vars.d/local.cfg
foo_host "example.com"
foo_literal 'a.b[$http_host]'
foo_uri r'/[^\s]*',
legacy_uri /[^\s]*
operator_asserted_safe none
Ordinary quoted text is a literal, with no variable interpolation, Python
unescaping or evaluation. Raw-prefixed quoted text (r'...' or r"...") is a
regex; legacy unquoted regexes remain supported. Regexes undergo static syntax
analysis and are never executed against strings. Trailing commas are tolerated.
none, null (case-insensitive), '' and "" are explicit trusted assertions
that a value is not user-controlled. Use them only when justified: they can
suppress security findings. A missing value, unmatched quote, malformed name or
invalid regex is rejected rather than becoming a safety assertion.
Trust and packaging¶
Automatic system policy must have root ownership and no group/world write permission on every ancestor directory, the policy directory, and each regular definition file. Symlinks, including ancestor symlinks, are rejected. Loading uses held no-follow descriptors on supported POSIX systems; unsupported safe loading warns and skips the system policy. Missing optional directories are normal and scans never create them.
Explicit paths are trusted operator choices, support ~, and work for non-root
users. Missing, unreadable and malformed selected files produce warnings. No
working-directory or implicit user-directory discovery occurs.
Module RPMs can ship ordinary root-owned 0644 vendor data without requiring
Gixy; Gixy's library package and module packages co-own the vendor directories
as root-owned 0755. Administrator overrides belong in /etc/gixy/vars.d.
Removing module metadata falls back to bundled knowledge. Policy affects only
analysis, never NGINX behavior or module activation.