# Bandit configuration for silos.
#
# YAML, not INI. Bandit 1.9 parses this file as YAML and rejects a `[bandit]`
# section header outright, so the INI form failed with
# "expected '<document start>', but found '<scalar>'".
#
# Exactly two rules are skipped, each for a reason checkable in the tree.
# Everything else Bandit can see — every `subprocess` call, every `pickle`,
# every `eval` — is enforced at medium severity and above.
#
# Keep this file free of blank lines between comments. A Markdown formatter
# reads a leading "- B608" as a list item and "helpfully" inserts a blank line
# around every line, which leaves the YAML valid but unreadable. That happened
# once; the file is verified with `python -c "import yaml; yaml.safe_load(...)"`
# whenever it changes.
#
# B608 (hardcoded_sql_expressions) — 13 sites, all of them an *identifier*.
# A table or column name is interpolated into SQL text; a value never is.
# Every site is one of three shapes:
#   * mesh_silos/connectors/sqlite_connector.py and
#     mesh_silos/connectors/sqlalchemy_connector.py pass each name through the
#     single canonical helper mesh_silos/fabric/identifiers.py::
#     quote_identifier, which refuses a name no quoting can express (empty,
#     whitespace-only, a NUL byte, over the dialect length) and then quotes it
#     with the dialect's own preparer. There is one definition of "safe
#     identifier" in the tree and these call it.
#   * mesh_silos/connectors/cloud.py calls _safe(name) first, which accepts
#     only [A-Za-z0-9_]. Quoting is therefore unnecessary rather than omitted:
#     there is no SQL syntax an accepted name could carry.
#   * mesh_silos/fabric/routes_to_dbt.py builds text for a generated dbt model
#     file. Nothing executes it. Bandit sees string formatting and cannot tell
#     the difference.
# A config-level skip rather than 13 inline `# nosec` comments: the reason is
# identical at all 13 sites, and 13 copies of it is noise a reviewer learns to
# read past. The trade-off is accepted with eyes open — add a genuinely
# interpolated *value* to a SQL string and this rule will not catch it. What
# stands in its place is tests/unit/test_sqlite_untrusted_identifiers.py and
# tests/unit/test_identifier_fuzzing.py (which assert the refusal and the
# quoting) plus code review.
#
# B615 (huggingface_unsafe_download) — 6 sites, development-only helpers.
# mesh_silos/slm/train/qlora.py, mesh_silos/slm/train/merge.py and
# mesh_silos/fixtures/download_model.py fetch a base model or dataset from the
# Hub without pinning a commit revision, so a re-tagged `main` changes what is
# fetched. That is a real supply-chain exposure — on the machines that have
# network access, which is exactly what the [train] extra is for.
# The shipped runtime never calls any of them: the engine is vendored into the
# wheel and loads a local .gguf, and `mesh_silos check-air-gap` refuses egress.
# Pinning a revision is the fix, and it needs the commit SHA of each artifact;
# that is a change to the fine-tuning path, which cannot be exercised on a
# machine without torch. Recorded here as a known gap rather than deleted or
# silently suppressed at the call sites.
skips:
  - B608
  - B615
