Sonar Vulnerability Disposition¶
This document records the reviewed disposition for the legacy SonarQube Cloud
vulnerability backlog tracked by issue #755. It is intentionally narrow: new
Sonar findings still fail the normal quality/security gates and this document is
not a rule exclusion, NOSONAR mechanism, or Quality Gate override.
Reviewed baseline¶
The public SonarQube Cloud issues API on main@601d1831c6e5991805021147128c2c969f6fd7ce
reported 32 open/confirmed vulnerability findings before the transport hardening
change:
- 25
pythonsecurity:S8707path-construction findings; - 7
python:S5332HTTP findings.
The HTTP family includes a real security boundary: a bearer token protects authentication but does not provide confidentiality when the server is bound to a non-loopback interface over cleartext HTTP. The implementation therefore fails closed unless a non-loopback deployment has either direct TLS certificate termination or a declared protected public endpoint. Remote public endpoints must be HTTPS; loopback HTTP is allowed for local/container port publishing. The dashboard CLI is loopback-only.
S8707 disposition¶
The 25 remaining S8707 flows are not path components derived from model,
provider, artifact, or request content. They are explicit filesystem roots/files
selected by repository CLI entry points. Those commands are intentionally able
to evaluate caller-selected fixtures, policies, manifests, evidence directories,
and output files outside the repository checkout. Confining them mechanically to
REPO_ROOT or the current working directory would break supported CLI behavior
without creating a real trust boundary.
Where an untrusted child path exists inside a selected root, it is handled by a separate boundary. For example, release checksum artifact identities are validated as basenames before joining them to the selected artifact directory (the traversal fix delivered in #789). The rows below therefore accept only the specific top-level caller-selected path flow that Sonar reports; they do not accept traversal inside those roots.
| Sonar location | Flow source | Disposition | Technical evidence |
|---|---|---|---|
scripts/check_live_model_release_policy.py:59 |
--github-output |
Accepted CLI contract | GitHub Actions explicitly supplies the output file path; model/provider data is never used as a path component. |
scripts/generate_gui_release_evidence.py:26 |
--cargo-lock |
Accepted CLI contract | Standalone packaging verifier intentionally reads a caller-selected Cargo lockfile. |
scripts/generate_gui_release_evidence.py:67 |
--cargo-lock |
Accepted CLI contract | Same explicit lockfile root; dependency parsing does not derive a new filesystem path from package data. |
scripts/generate_gui_release_evidence.py:188 |
--output-dir |
Accepted CLI contract | The caller intentionally selects the release-evidence output directory. |
scripts/generate_gui_release_evidence.py:218 |
--evidence |
Accepted CLI contract | Verification intentionally accepts a caller-selected evidence file. Evidence-declared artifact names are separately inventory-constrained before hashing. |
scripts/generate_release_evidence.py:44 |
--pyproject |
Accepted CLI contract | The CLI supports verification of an explicitly selected Python project file. |
scripts/generate_release_evidence.py:133 |
--checksums |
Accepted CLI contract | The checksum manifest itself is caller-selected; entries inside it are independently basename-validated before artifact access. |
scripts/generate_release_evidence.py:166 |
--output |
Accepted CLI contract | The output evidence path is explicitly selected by the caller. |
scripts/kicad11_headless_canary.py:221 |
--artifacts |
Accepted CLI contract | Canary artifacts are intentionally written below the caller-selected artifacts root. |
scripts/kicad_canary.py:748 |
analyzer flow from run CLI parse |
Analyzer limitation | The sink is _resolve_cli() over fixed environment/PATH candidates and does not receive the parsed --artifacts/--kicad-range values reported as the source. Candidates are required to exist and be files before resolution. |
scripts/kicad_canary.py:912 |
--artifacts |
Accepted CLI contract | The run command intentionally owns a caller-selected artifact directory. |
scripts/validate_mcp_manifest.py:317 |
positional manifest / registry publisher manifest | Accepted CLI contract | Validating an arbitrary supplied manifest is the command's documented purpose; registry publication separately validates repository metadata and package contracts. |
src/kicad_mcp/evals/baseline_promotion.py:65 |
--aggregate-report |
Accepted CLI contract | Baseline generation intentionally consumes a caller-selected aggregate evidence file. |
src/kicad_mcp/evals/baseline_promotion.py:71 |
--baseline-template |
Accepted CLI contract | Promotion can operate on an explicitly selected baseline template; no model value becomes a path component. |
src/kicad_mcp/evals/baseline_promotion.py:164 |
--aggregate-report |
Accepted CLI contract | Same caller-selected aggregate evidence boundary during promotion validation. |
src/kicad_mcp/evals/baseline_promotion.py:179 |
--output |
Accepted CLI contract | The protected workflow deliberately selects the candidate-baseline output path. |
src/kicad_mcp/evals/live_runner.py:422 |
--output |
Accepted CLI contract | Evaluation output location is a caller-selected evidence destination; model output is serialized as content, not a path. |
src/kicad_mcp/evals/nvidia_nim_adapter.py:299 |
--tools-reference |
Accepted CLI contract | Adapter/eval CLIs intentionally support an explicit tools-reference fixture; model/provider content does not select the file. |
src/kicad_mcp/evals/release_gate.py:31 |
--baseline |
Accepted CLI contract | Release evaluation accepts an explicitly selected reviewed baseline file. |
src/kicad_mcp/evals/release_gate.py:393 |
--output |
Accepted CLI contract | The gate writes its report to the caller-selected protected evidence path. |
src/kicad_mcp/evals/release_policy.py:116 |
--policy |
Accepted CLI contract | Policy classification deliberately supports an explicitly supplied policy file for local/protected evaluation. |
src/kicad_mcp/evals/release_policy.py:146 |
--baseline |
Accepted CLI contract | The approved baseline file is a caller/workflow-selected input, not a path derived from untrusted evidence. |
src/kicad_mcp/evals/smoke_assurance.py:285 |
--output |
Accepted CLI contract | Smoke assurance writes one sanitized report to the explicitly selected output path. |
src/kicad_mcp/evals/tool_selection.py:255 |
--cases |
Accepted CLI contract | Eval runners intentionally load a caller-selected benchmark case file. |
src/kicad_mcp/evals/tool_selection.py:616 |
--thresholds |
Accepted CLI contract | Eval/release runners intentionally load a caller-selected threshold contract. |
S5332 disposition¶
The seven pre-hardening python:S5332 findings were reviewed individually. The
real risk was not the existence of local HTTP itself; it was that a non-loopback
bind could previously carry a bearer token over plaintext. The transport policy
now removes that state from the supported configuration space.
| Pre-hardening location | Disposition after this change | Technical evidence |
|---|---|---|
src/kicad_mcp/config.py URL-scheme text |
Analyzer-only/message construction | The wording no longer embeds an http:// transport example and does not initiate network I/O. |
src/kicad_mcp/server.py auth/resource base URL (two flows) |
Remediated | Auth metadata uses advertised_http_base_url; non-loopback direct mode requires TLS, tls-proxy requires HTTPS, and loopback-proxy permits HTTP only on a loopback public origin. |
src/kicad_mcp/server_info.py advertised endpoint (two flows) |
Remediated | Discovery uses the same validated advertised base URL; remote plaintext advertisement is not a valid configuration. |
src/kicad_mcp/server.py dashboard/browser URLs (two flows) |
Accepted loopback-only HTTP | dashboard --host now rejects non-loopback hosts. Local dashboard HTTP remains intentionally supported and never represents a remote cleartext transport. |
This is an intentional security compatibility break for deployments that directly
bound remote plaintext HTTP with only a bearer token. Migration is explicit:
terminate TLS directly with KICAD_MCP_TLS_CERT_FILE and
KICAD_MCP_TLS_KEY_FILE, use tls-proxy with an HTTPS public base URL, or use
loopback-proxy only when a bind-all container port is published to host
loopback.
Review rules¶
These dispositions are valid only while the corresponding data flow remains a top-level caller-selected CLI path. Re-review is required if any of the following changes occur:
- model/provider/request/artifact content becomes part of a filesystem path;
- a selected root begins accepting unvalidated child names;
- one of these CLIs is exposed as a remote request handler with a filesystem sandbox promise;
- a workflow begins passing untrusted event text into one of these path options.
No Sonar rule, severity, exclusion, Quality Gate, CodeQL query, or coverage threshold is changed by this disposition.