Skip to content

Semantic bugs not caught by ruff/bandit (LLM audit backlog) #1070

@markuslf

Description

@markuslf

Background

While setting up the bandit/vulture pre-commit hooks (see commit f2cd0ffe), an initial pass of LLM-driven code readers found a batch of real semantic bugs that the deterministic static analyzers (ruff F/B, bandit low/low, vulture confidence 80) do not catch - they are all correctness issues that depend on understanding intent, not on patterns ruff or bandit check.

They should be triaged and either fixed or explicitly marked as "works as intended" with a short comment. Until then they remain latent bugs.

Findings

State / threshold logic

  • wildfly-memory-usage:189 - state += lib.base.get_worst(used_state, state) instead of state = .... Adding state integers corrupts the accumulation (e.g. STATE_WARN + STATE_WARN = 2 reads as STATE_CRIT, STATE_WARN + STATE_CRIT = 3 is outside the valid range)
  • wildfly-memory-usage:250 - same pattern
  • starface-java-memory-usage:197,246 - same pattern
  • wildfly-non-xa-datasource-stats:224 - calls lib.base.get_state(active_pct, ...) when the threshold at that point is supposed to check max_used_pct
  • wildfly-xa-datasource-stats:224 - same
  • network-connections:178 - function returns STATE_OK even when earlier branches set a WARN/CRIT state, discarding the calculated value
  • mysql-sorts:168 - state is reassigned from a local sort_state instead of combined via get_worst() with the accumulated value
  • matomo-reporting:202 - state variable is never updated before the lib.base.oao() call when args.METRIC is provided, so warn/crit thresholds are never evaluated
  • starface-channel-status:73,111 - --warning/--critical are strings (type=str without conversion) but lib.base.get_state(used_percent, args.WARN, args.CRIT) later expects numeric values

Early exit / fall-through

  • tuned-profile:85-88 - first oao() call is followed by unconditional code that always calls a second oao(), overwriting the first state
  • updates:117-132 - same shape (check if still present after the f-string syntax fix in commit f2cd0ff)
  • crypto-policy:85-90 - unreachable code after oao()
  • ntp-ntpd:136-143 - multiple early oao() calls with a path that becomes dead code depending on earlier branches
  • xml:201-204 - result = r[0].text is assigned inside a try that exits on exception, but the value is never used

Regex / parsing

  • keycloak-version:172 - regex r'n (.*)' contains a literal n prefix that does not match the actual /version.txt format
  • openvpn-version:135 - regex r'N (\d+\.\d+\.\d+)' contains a literal N prefix
  • axenita-stats:271 - string slice [start:end] where end = axenita_ver.find('-'); when the hyphen is missing, find returns -1 and the slice silently becomes empty instead of raising or falling back

Wrong identifier / variable

  • redis-status:382 - perfdata uses the loop variable keys from a for key, value in result.items(): loop, so it only reports the last database's key count
  • valkey-status:377 - same pattern
  • mysql-table-locks:145 - denominator in the "locks waited" message uses Table_locks_immediate twice instead of Table_locks_immediate + Table_locks_waited
  • hin-status:149 - cnt_incidents is hardcoded to 1 whenever incidents exist, instead of len(incidents)
  • openstack-swift-stat:257 - quota existence check looks up x-account-bytes-used (usage header) instead of x-account-meta-quota-bytes (quota header)
  • qts-temperatures:165-173 - perfdata for systemp receives CPUTempWarnT/CPUTempErrT thresholds and perfdata for cputemp receives SysTempWarnT/SysTempErrT thresholds - the two are swapped
  • php-fpm-ping:144-146 - perfdata value is the state integer (0/1/2/3) instead of the ping response time
  • php-status:196 - log line built with '{key} = {value}, ' (plain string, not an f-string) so placeholders are emitted literally
  • ntp-w32tm:173 - appended state label hardcodes STATE_WARN even when the state may be STATE_CRIT

Argparse / parameter wiring

  • sap-open-concur-com:111 - choices=services.append('All') - list.append() returns None, so choices=None disables argparse validation entirely
  • grafana-version:119-122 - get_installed_version() treats lib.shell.shell_exec() result as if the outer tuple unpacking already happened (misinterprets the (success, result) contract)
  • icinga-topflap-services:279 - SELECT queries a table name that does not match the CREATE TABLE earlier in the plugin
  • infomaniak-events:210 - ended_at - started_at evaluates even when ended_at is None, producing a TypeError
  • nextcloud-security-scan:197 - abs((scan_date - today).days) always measures absolute distance, so the trigger fires for any scan date (past or future) regardless of direction
  • fortios-network-io:392 - lib.base.oao() is called and then followed by another unconditional oao() that always overwrites the first

Acceptance criteria

Each item is either fixed with a short test or smoke check, or closed with a # the framework actually handles this because ... comment plus the rationale. A # nosec equivalent for semantic issues would be a plain # noqa: with a sentence explaining why the static reader was wrong.

Context

The lib-side findings (shell pipeline fd leak, human.py negative-number handling, cache.py timestamp off-by-one, url.py dead ternary) are tracked separately on Linuxfabrik/lib.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpythonPull requests that update Python code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions