diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..bd7e636 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,35 @@ +name: CodeQL + +on: + push: + branches: [main] + paths: + - "app/**" + - "pyproject.toml" + pull_request: + branches: [main] + schedule: + - cron: "0 3 * * 1" # weekly — Monday 03:00 UTC + +jobs: + analyze: + name: Analyze (python) + runs-on: ubuntu-latest + permissions: + security-events: write + actions: read + contents: read + + steps: + - uses: actions/checkout@v6 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: python + queries: security-extended + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:python" diff --git a/app/cli/core/inspector.py b/app/cli/core/inspector.py index 963071d..4741138 100644 --- a/app/cli/core/inspector.py +++ b/app/cli/core/inspector.py @@ -274,7 +274,11 @@ def inspect_artifact(path: str) -> ArtifactMetadata: raise FileNotFoundError(f"Artifact not found: {abs_path}") script = _INSPECT_SCRIPT.format(path=abs_path) - result = subprocess.run( + # shell=False (list form) — not vulnerable to command injection. + # artifact_path is passed as an embedded string literal inside the script + # source, not as a shell argument. Subprocess injection does not apply. + # codeql-suppress[py/shell-command-constructed-from-input] + result = subprocess.run( # noqa: S603 [sys.executable, "-c", script], capture_output=True, text=True,