Files
codebase-memory-mcp/.github/workflows/codeql.yml
T
Martin Vogel 27e640b3b0 fix(mcp,ci): harden get_architecture serialization + CodeQL gate
Follow-up to #281 (handle_get_architecture) plus a CodeQL workflow
upgrade developed in parallel; bundling into one commit because the
test suite had to land alongside both.

mcp.c — handle_get_architecture
- NULL-coerce every const-char* field in the architecture sections
  via `x ? x : ""`, matching the rest of mcp.c (search_graph, etc.).
  Without this, a NULL field becomes a missing JSON key instead of
  an empty string; yyjson_mut_obj_add_str returns false on NULL and
  silently no-ops, so an inconsistent omission could surprise callers.
- Serialize two more architecture aspects that #281 left on the
  floor: services (cbm_service_link_t: from/to/type/count) and
  clusters (cbm_cluster_info_t: id/label/members/cohesion plus the
  top_nodes / packages / edge_types string arrays). The store-side
  computation populates these for aspects=["all"] / explicit names,
  so dropping them in the serializer was data loss.

tests/test_mcp.c
- New tool_get_architecture_emits_populated_sections regression test.
  Uses a minimal inline fixture (single Function node tagged with
  "is_entry_point": true) since arch_entry_points reads that flag
  out of properties_json. Asserts the response contains both an
  "entry_points" array and the function name — neither would appear
  before #281 because handle_get_architecture never called
  cbm_store_get_architecture.
- extract_text_content drilled too shallow: it pulled "content" only
  from the JSON root, so it worked for cbm_mcp_handle_tool but
  silently fell through to the raw response for cbm_mcp_server_handle
  (where content lives under .result.content). Added a fallback that
  checks .result.content; both unwrappers tested by existing fixtures.

ci(codeql)
- Run on pull_request to main, not just push; surfaces findings on
  the PR instead of after merge.
- Pull custom queries from ./codeql via `queries: +./codeql`.
- Capture SARIF output and fail the job on any error-level finding,
  using jq to enumerate rule id, file:line, and message text in the
  GitHub Actions error annotation. Warnings are still reported as
  before; only errors block.

Full suite: 2842 passed, 0 failed.
2026-05-09 16:37:53 +02:00

55 lines
1.8 KiB
YAML

name: CodeQL SAST
on:
push:
branches: [main]
permissions:
security-events: write
contents: read
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install build dependencies
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev jq
- name: Initialize CodeQL
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
with:
languages: c-cpp
build-mode: manual
queries: +./codeql
- name: Build for CodeQL analysis
run: scripts/build.sh
- name: Perform CodeQL Analysis
id: analyze
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
with:
category: "/language:c-cpp"
output: codeql-results
- name: Fail on CodeQL error-level findings
run: |
sarif=$(find codeql-results -name '*.sarif' | head -1)
if [[ -z "$sarif" ]]; then
echo "::error::No SARIF output found"; exit 1
fi
err=$(jq '[.runs[].results[]? | select((.level // "warning") == "error")] | length' "$sarif")
warn=$(jq '[.runs[].results[]? | select((.level // "warning") == "warning")] | length' "$sarif")
echo "CodeQL findings: $err error(s), $warn warning(s)"
if [[ "$err" -gt 0 ]]; then
jq -r '
.runs[].results[]?
| select((.level // "warning") == "error")
| "\(.locations[0].physicalLocation.artifactLocation.uri):\(.locations[0].physicalLocation.region.startLine) [\(.ruleId)] \(.message.text)"
' "$sarif"
echo "::error::$err CodeQL error-level finding(s) — failing build."
exit 1
fi