Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 135 additions & 128 deletions .github/CODEQL-WORKFLOW.md
Original file line number Diff line number Diff line change
@@ -1,136 +1,143 @@
# CodeQL Workflow Configuration

## Current State: Dual CodeQL Setup

This repository currently has **two CodeQL analysis configurations** running:

### 1. Custom CodeQL Workflow (`.github/workflows/codeql.yml`)
- **Location**: `.github/workflows/codeql.yml`
- **Status**: Active, but results NOT uploaded to GitHub Security
- **Configuration**: Custom setup with advanced features
- **Languages**: C# and JavaScript/TypeScript (matrix strategy)
- **Custom Config**: `.github/codeql/codeql-config.yml` (path filtering)
- **Upload Setting**: `upload: false` (to avoid conflicts with default setup)

### 2. GitHub Default Setup
- **Location**: Enabled in repository settings (no visible file)
- **Status**: Active (inferred from workflow comments)
- **Configuration**: GitHub-managed default
- **Languages**: Auto-detected
- **Results**: Uploaded to GitHub Security tab

## Why This Exists

The comment in `codeql.yml` lines 78-80 states:
```yaml
# Prevent workflow failure when GitHub "default setup" code scanning is enabled.
# If you want advanced results uploaded, disable default setup or set upload: true.
upload: false
Melodee uses one version-controlled CodeQL advanced setup. The workflow in
`.github/workflows/codeql.yml` uploads results for every supported application
language to GitHub code scanning.

## Scanning Coverage

The workflow runs for pushes and pull requests targeting `main`, every Sunday
at 00:00 UTC, and on manual dispatch. Its matrix analyzes:

- GitHub Actions workflows using the interpreted `none` build mode.
- C# using `autobuild`, so CodeQL observes the compiled solution.
- JavaScript and TypeScript using the interpreted `none` build mode.
- Python using the interpreted `none` build mode and its local threat model.

The shared configuration in `.github/codeql/codeql-config.yml` excludes
documentation, tests, benchmarks, and vendored minified JavaScript from
interpreted-language analysis. C#, JavaScript/TypeScript, and Actions use the
default remote threat model. Python uses
`.github/codeql/codeql-python-config.yml`, which extends the default model with
local files, command-line arguments, environment variables, and other local
sources used by maintenance scripts. Neither configuration excludes C#
findings by rule.

This split is deliberate. The compiled web application has a remote request
boundary, and treating all application-owned database and filesystem state as
attacker-controlled creates unactionable C# flows. Python maintenance tools
really do accept local files, environment variables, and command-line values
from an administrator, so the local threat model accurately represents their
trust boundary. Do not enable `threat-models: local` globally to make the two
very different execution models look uniform.

The local models are intentionally narrow. They identify
`LogSanitizer.Sanitize` as a barrier only for the `log-injection` flow kind and
`ConfigurationLogRedactor.RedactValue` as a barrier only when values would be
persisted to an external location such as a log. They are packaged under
`.github/codeql/extensions/log-sanitizer` so advanced and default setup can
discover them. The `.yml` suffix is intentional because the pack manifest only
loads model files matching `models/**/*.yml`.
Repository-wide query exclusions must not be added to hide a false positive.
Prefer a source fix or a precise CodeQL model; dismiss an individual alert only
when neither option accurately represents the behavior.

All workflow actions are pinned to verified full commit SHAs. Keep the release
comments beside those SHAs when updating the pins so reviews can identify the
corresponding upstream release.

## Required Repository Configuration

GitHub default setup must remain disabled while this advanced workflow is
active. Enabling both configurations can disable advanced uploads, duplicate
work, and leave alert status attached to a configuration that no longer runs.

To verify the setting:

1. Open the repository settings and select **Advanced Security** or
**Code security and analysis**.
2. Find **CodeQL analysis** under code scanning.
3. Confirm default setup is disabled and the checked-in advanced workflow is
enabled.

The REST API reports the same state through:

```shell
gh api repos/melodee-project/melodee/code-scanning/default-setup
```

This indicates that both setups were running, causing conflicts. The custom workflow was modified to NOT upload results to avoid workflow failures.

## Problem

Having two CodeQL setups is inefficient and confusing:
- ❌ Duplicate analysis runs (wastes CI/CD minutes)
- ❌ Custom workflow results not visible in Security tab
- ❌ Configuration spread across multiple systems
- ❌ Unclear which analysis results are authoritative

## Recommended Solution: Keep Custom Workflow

**Advantages of the custom workflow:**
1. ✅ **Path filtering** - Excludes docs, vendored JS, benchmarks (via `codeql-config.yml`)
2. ✅ **Explicit language control** - C# and JavaScript/TypeScript
3. ✅ **Version control** - Configuration tracked in Git
4. ✅ **Transparency** - Visible in repository, reviewable in PRs
5. ✅ **Flexibility** - Can add custom queries, modify triggers, adjust timeout
6. ✅ **Already proven** - Working configuration with comprehensive security audit

**Steps to migrate:**

### Step 1: Disable GitHub Default Setup
1. Go to repository **Settings** → **Code security and analysis**
2. Find **Code scanning** section
3. Locate **CodeQL analysis** → **Default setup**
4. Click **Disable** or switch to **Advanced**

> **Note**: You must have admin access to the repository to change these settings.

### Step 2: Enable Uploads in Custom Workflow
Update `.github/workflows/codeql.yml` line 78-82 to:

```yaml
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{ matrix.language }}"
# Upload results to GitHub Security tab
# Default setup has been disabled in repository settings
upload: true
```

Remove these lines (they're now unnecessary):
- `upload-database: false`
- `wait-for-processing: false`

### Step 3: Verify Configuration
1. Commit and push the workflow changes
2. Trigger a workflow run (push to main or create PR)
3. Check **Security** tab → **Code scanning** for results
4. Verify both C# and JavaScript/TypeScript results appear

## Alternative Solution: Use Default Setup Only

If you prefer GitHub's managed solution:

### Step 1: Delete Custom Workflow
```bash
git rm .github/workflows/codeql.yml
git rm .github/codeql/codeql-config.yml # optional, if not needed
The expected `state` is `not-configured`.

## Removing a Stale Configuration

An alert can remain open after its code is fixed when it belongs to an older
configuration that no longer runs. Adding the language to this workflow does
not update the stale configuration's result.

After the advanced workflow has completed successfully on `main`:

1. Open **Security and quality** > **Code scanning**.
2. Open **Tool status**, or open the alert and inspect **Affected branches** >
**Configurations analyzing** for `main`.
3. Retain `.github/workflows/codeql.yml:analyze` for each matrix language.
4. Delete the stale `dynamic/github-code-scanning/codeql:analyze` and
`dynamic/github-code-scanning/codeql:upload` configurations left by the
former default setup. Retain every checked-in workflow configuration.
5. Re-run the CodeQL workflow if an active configuration was removed by
mistake.

This administrative cleanup is required for findings that exist only in the
former default-setup configuration; a repository commit cannot delete that
server-side configuration.

The stale Python configurations must not be removed before the checked-in
Python matrix job succeeds on `main`. Otherwise, the repository can temporarily
lose active Python coverage rather than transferring it to the advanced
workflow.

## Verification

The July 2026 local verification reports:

- the six original C# alerts have source fixes and regression coverage, while
the Python setup alert has a hardened necessary sink and narrow documented
suppression;
- a fresh default-remote C# database moved from eight findings to zero;
- fresh GitHub Actions and JavaScript/TypeScript databases report zero
findings;
- a fresh workflow-equivalent Python database ran all 45 default queries with
the local threat model and reported zero findings and no SARIF warning/error
notifications; and
- the 52-query Python security-extended suite also reported zero findings.

The Python filesystem audit passes 57 focused tests, and all 109 script tests
pass with `ResourceWarning` promoted to an error. These complete-suite results,
not an earlier targeted-query result, form the local release gate.

After changing the workflow or CodeQL configuration:

1. Validate `.github/workflows/codeql.yml` with `actionlint`.
2. Run the workflow manually, or push the change to a branch with a pull
request targeting `main`.
3. Confirm all four matrix jobs complete and upload results.
4. On **Security and quality** > **Code scanning** > **Tool status**, confirm
recent GitHub Actions, C#, JavaScript/TypeScript, and Python analyses are
present.
5. Review new alerts before merging; do not dismiss them solely to make the
check pass.

Useful command-line checks:

```shell
gh run list --workflow codeql.yml --limit 10
gh api --paginate \
'repos/melodee-project/melodee/code-scanning/alerts?state=open&tool_name=CodeQL&per_page=100'
```

### Step 2: Ensure Default Setup is Enabled
1. Repository **Settings** → **Code security and analysis**
2. Ensure **CodeQL analysis** is set to **Default setup**

**Trade-offs:**
- ✅ Simpler configuration
- ✅ GitHub manages updates
- ❌ Less control over paths analyzed
- ❌ Cannot customize queries easily
- ❌ Configuration not in version control

## Current Recommendation

**Keep the custom workflow** because:
1. It already has proven security analysis (see `CODEQL-ANALYSIS.md`)
2. Path filtering reduces noise from docs and vendored code
3. Configuration is version-controlled and reviewable
4. Team has already invested in customizing it

The only change needed is to **disable GitHub default setup** and **enable uploads** in the custom workflow.

## Migration Checklist

- [ ] 1. Disable GitHub default setup in repository settings
- [ ] 2. Update `codeql.yml` to set `upload: true`
- [ ] 3. Remove `upload-database: false` and `wait-for-processing: false`
- [ ] 4. Update comments in workflow file
- [ ] 5. Commit and push changes
- [ ] 6. Verify workflow runs successfully
- [ ] 7. Check Security tab for CodeQL results
- [ ] 8. Update `CODEQL-ANALYSIS.md` if needed

## References

- [GitHub CodeQL Documentation](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/about-code-scanning-with-codeql)
- [Configuring Code Scanning](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning)
- [CodeQL Action Documentation](https://github.com/github/codeql-action)
- Project: [CODEQL-ANALYSIS.md](../CODEQL-ANALYSIS.md) - Security audit results
- Project: [SECURITY-FIXES.md](../SECURITY-FIXES.md) - Security improvements

## Questions?

If you have questions about this configuration, please open an issue or contact the maintainers.
- [Workflow configuration options for code scanning](https://docs.github.com/en/code-security/reference/code-scanning/workflow-configuration-options)
- [CodeQL for compiled languages](https://docs.github.com/en/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/codeql-for-compiled-languages)
- [Resolving code scanning alerts](https://docs.github.com/en/code-security/how-tos/manage-security-alerts/manage-code-scanning-alerts/resolve-alerts)
- [Secure use of GitHub Actions](https://docs.github.com/en/actions/reference/security/secure-use)
- [Customizing CodeQL library models for C#](https://codeql.github.com/docs/codeql-language-guides/customizing-library-models-for-csharp/)
58 changes: 10 additions & 48 deletions .github/codeql/codeql-config.yml
Original file line number Diff line number Diff line change
@@ -1,50 +1,12 @@
# CodeQL configuration
# This file configures which paths to analyze for JavaScript/TypeScript
name: CodeQL configuration

name: "CodeQL config"

# Model extensions define custom sanitizers and sinks for CodeQL analysis
model-extensions:
- ".github/codeql/extensions/log-sanitizer.model.yaml"

# Query filters to tune analysis results
query-filters:
# Exclude false positives where we have verified mitigations
- exclude:
id: cs/web/cookie-secure-not-set
# MelodeeBlazorCookieMiddleware.cs already sets Secure=true (line 29)

# Exclude log-forging alerts for code using LogSanitizer
# LogSanitizer.Sanitize() properly sanitizes log input by replacing newlines,
# but CodeQL's log-forging query doesn't recognize custom sanitizer methods.
# The sanitizer implementation (src/Melodee.Common/Utility/LogSanitizer.cs)
# uses String.Replace to remove CR, LF, NEL, LS, and PS characters.
# See: https://github.com/github/codeql/issues/15824
- exclude:
id: cs/log-forging

# Exclude cleartext-storage alerts for MpdPlaybackBackend password logging
# The password IS properly masked via GetSafeCommandForLogging() which returns
# "password ***" for any command starting with "password ". CodeQL tracks
# data flow but cannot see through the conditional masking logic.
# File: src/Melodee.Common/Services/Playback/Backends/MpdPlaybackBackend.cs
- exclude:
id: cs/cleartext-storage-of-sensitive-information

# For JavaScript/TypeScript, only analyze application code in src directory
# Exclude documentation, vendored libraries, and other non-application code
# Path filters apply to interpreted languages. The C# database is determined
# by the sources included in its build.
paths-ignore:
# Exclude entire docs directory (contains Jekyll templates and vendored JS)
- 'docs/**'
- 'melodee/docs/**'

# Exclude vendored/third-party JavaScript libraries
- '**/jquery*.js'
- '**/lunr*.js'
- '**/*.min.js'

# Exclude benchmark and test directories
- 'benchmarks/**'
- 'tests/**'
- 'melodee/benchmarks/**'
- 'melodee/tests/**'
- "benchmarks/**"
- "docs/**"
- "tests/**"
- "**/tests/**"
- "**/*.min.js"
- "**/jquery*.js"
- "**/lunr*.js"
16 changes: 16 additions & 0 deletions .github/codeql/codeql-python-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: CodeQL Python configuration

# Maintenance scripts accept files, command-line arguments, and environment
# variables. Extend Python's default remote model so those local sources are
# analyzed under the local threat model independently from the compiled web
# application's remote request boundary.
threat-models: local

paths-ignore:
- "benchmarks/**"
- "docs/**"
- "tests/**"
- "**/tests/**"
- "**/*.min.js"
- "**/jquery*.js"
- "**/lunr*.js"
23 changes: 0 additions & 23 deletions .github/codeql/extensions/log-sanitizer.model.yaml

This file was deleted.

7 changes: 7 additions & 0 deletions .github/codeql/extensions/log-sanitizer/codeql-pack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: melodee-project/log-sanitizer-models
version: 0.0.0
library: true
extensionTargets:
codeql/csharp-all: "*"
dataExtensions:
- "models/**/*.yml"
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# LogSanitizer.Sanitize replaces every supported line-ending character before
# returning its value. Model that return value only as a log-injection barrier;
# do not suppress the log-forging query or mark unrelated masking helpers safe.
# ConfigurationLogRedactor.RedactValue is deny-by-default and permits only
# sanitized operational metadata, so its return is safe for external storage.
extensions:
- addsTo:
pack: codeql/csharp-all
extensible: barrierModel
data:
- ["Melodee.Common.Utility", "LogSanitizer", false, "Sanitize", "(System.String)", "", "ReturnValue", "log-injection", "manual"]
- ["Melodee.Common.Configuration", "ConfigurationLogRedactor", false, "RedactValue", "(System.String,System.Object)", "", "ReturnValue", "file-content-store", "manual"]
Loading
Loading