diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 086da0d043..e0d49be55d 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -57,7 +57,7 @@ jobs: cache: false - name: Initialize CodeQL C# analysis - uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 with: languages: csharp build-mode: none @@ -69,7 +69,7 @@ jobs: - Assets/Tests - name: Analyze C# security code with CodeQL - uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 with: category: codeql-csharp-security output: ${{ runner.temp }}/codeql-results diff --git a/cli/release-automation/internal/automation/codeql_sarif_guard.go b/cli/release-automation/internal/automation/codeql_sarif_guard.go index a39c392599..a67d670b4d 100644 --- a/cli/release-automation/internal/automation/codeql_sarif_guard.go +++ b/cli/release-automation/internal/automation/codeql_sarif_guard.go @@ -11,7 +11,7 @@ const ( expectedCodeQLSARIFSchema = "https://json.schemastore.org/sarif-2.1.0.json" expectedCodeQLSARIFVersion = "2.1.0" expectedCodeQLToolName = "CodeQL" - expectedCodeQLToolSemanticVersion = "2.26.0" + expectedCodeQLToolSemanticVersion = "2.27.1" // These hard floors detect extractor collapse without rejecting normal Unity-source composition changes. minimumCodeQLCallTargetPercentage = 55 minimumCodeQLKnownTypePercentage = 70 @@ -151,7 +151,7 @@ func validateCodeQLSARIFQuality(run codeQLSARIFRun) (CodeQLSARIFValidationResult // helper: those three notification kinds are one scan, and leaving the loop // inline kept validateCodeQLSARIFQuality over the cyclop limit. func scanCodeQLQualityNotifications(invocation codeQLSARIFInvocation) (codeQLQualityScan, error) { - // CodeQL 2.26.0 emits the database-quality diagnostic only below its own 85% thresholds. The strict semantic-version check above freezes that behavior, so absence means both metrics are at least 85%, not that evidence was silently lost. + // CodeQL 2.27.1 emits the database-quality diagnostic only below its own 85% thresholds. The strict semantic-version check above freezes that behavior, so absence means both metrics are at least 85%, not that evidence was silently lost. scan := codeQLQualityScan{ callTargets: 100, knownTypes: 100, diff --git a/cli/release-automation/internal/automation/codeql_sarif_guard_test.go b/cli/release-automation/internal/automation/codeql_sarif_guard_test.go index 63824b329f..808ed7b1d4 100644 --- a/cli/release-automation/internal/automation/codeql_sarif_guard_test.go +++ b/cli/release-automation/internal/automation/codeql_sarif_guard_test.go @@ -15,7 +15,7 @@ func TestValidateCodeQLSARIFAcceptsNonEmptyFindingAtQualityBaseline(t *testing.T "$schema":"https://json.schemastore.org/sarif-2.1.0.json", "version":"2.1.0", "runs":[{ - "tool":{"driver":{"name":"CodeQL","semanticVersion":"2.26.0"}}, + "tool":{"driver":{"name":"CodeQL","semanticVersion":"2.27.1"}}, "properties":{"metricResults":[{"ruleId":"cs/summary/lines-of-code","value":119122}]}, "invocations":[{"executionSuccessful":true,"toolExecutionNotifications":[ {"descriptor":{"id":"csharp/autobuilder/buildless/complete"},"message":{"text":"C# analysis with build-mode 'none' completed."}}, @@ -36,8 +36,8 @@ func TestValidateCodeQLSARIFAcceptsNonEmptyFindingAtQualityBaseline(t *testing.T } func TestValidateCodeQLSARIFAcceptsCodeQLCLIProofFixture(t *testing.T) { - // Verifies parsing stays compatible with the SARIF shape emitted by the approved CodeQL 2.26.0 proof scan. - sarifPath := filepath.Join("testdata", "codeql-cli-2.26.0-sarif.json") + // Verifies parsing stays compatible with the SARIF shape emitted by the approved CodeQL 2.27.1 proof scan. + sarifPath := filepath.Join("testdata", "codeql-cli-2.27.1-sarif.json") sarif, err := os.ReadFile(sarifPath) if err != nil { t.Fatalf("read CodeQL proof fixture: %v", err) @@ -50,7 +50,7 @@ func TestValidateCodeQLSARIFAcceptsCodeQLCLIProofFixture(t *testing.T) { func TestCodeQLCLIProofFixtureContainsSecurityFinding(t *testing.T) { // Verifies the pinned CodeQL security-extended probe produced a real command-injection finding. - sarifPath := filepath.Join("testdata", "codeql-cli-2.26.0-security-probe-sarif.json") + sarifPath := filepath.Join("testdata", "codeql-cli-2.27.1-security-probe-sarif.json") sarif, err := os.ReadFile(sarifPath) if err != nil { t.Fatalf("read CodeQL security probe fixture: %v", err) @@ -104,7 +104,7 @@ func TestValidateCodeQLSARIFRejectsUnexpectedSchema(t *testing.T) { func TestValidateCodeQLSARIFRejectsUnexpectedCodeQLVersion(t *testing.T) { // Verifies a CodeQL tool replacement is surfaced rather than silently changing the approved scanner. sarif := validCodeQLSARIF(`"CodeQL"`, true, 55, 70) - sarif = []byte(strings.Replace(string(sarif), `"semanticVersion":"2.26.0"`, `"semanticVersion":"2.25.0"`, 1)) + sarif = []byte(strings.Replace(string(sarif), `"semanticVersion":"2.27.1"`, `"semanticVersion":"2.25.0"`, 1)) if err := ValidateCodeQLSARIF(sarif); err == nil { t.Fatal("expected unexpected CodeQL version to fail") @@ -195,7 +195,7 @@ func TestValidateCodeQLSARIFRejectsCompilationCollapse(t *testing.T) { "$schema":"https://json.schemastore.org/sarif-2.1.0.json", "version":"2.1.0", "runs":[{ - "tool":{"driver":{"name":"CodeQL","semanticVersion":"2.26.0"}}, + "tool":{"driver":{"name":"CodeQL","semanticVersion":"2.27.1"}}, "properties":{"metricResults":[{"ruleId":"cs/summary/lines-of-code","value":75000}]}, "invocations":[{"executionSuccessful":true,"toolExecutionNotifications":[ {"descriptor":{"id":"csharp/autobuilder/buildless/complete"},"message":{"text":"C# analysis with build-mode 'none' completed."}}, @@ -228,7 +228,7 @@ func validCodeQLSARIFWithExtractedFilesAndValues(toolName string, executionSucce "$schema":"https://json.schemastore.org/sarif-2.1.0.json", "version":"2.1.0", "runs":[{ - "tool":{"driver":{"name":` + toolName + `,"semanticVersion":"2.26.0"}}, + "tool":{"driver":{"name":` + toolName + `,"semanticVersion":"2.27.1"}}, "properties":{"metricResults":[{"ruleId":"cs/summary/lines-of-code","value":75000}]}, "invocations":[{"executionSuccessful":` + boolJSON(executionSuccessful) + `,"toolExecutionNotifications":[ {"descriptor":{"id":"csharp/autobuilder/buildless/complete"},"message":{"text":"C# analysis with build-mode 'none' completed."}}, @@ -244,7 +244,7 @@ func validCodeQLSARIFWithoutCompletion() []byte { "$schema":"https://json.schemastore.org/sarif-2.1.0.json", "version":"2.1.0", "runs":[{ - "tool":{"driver":{"name":"CodeQL","semanticVersion":"2.26.0"}}, + "tool":{"driver":{"name":"CodeQL","semanticVersion":"2.27.1"}}, "properties":{"metricResults":[{"ruleId":"cs/summary/lines-of-code","value":75000}]}, "invocations":[{"executionSuccessful":true,"toolExecutionNotifications":[ {"descriptor":{"id":"csharp/diagnostic/database-quality"},"message":{"text":"Some metrics of the database quality are: Percentage of calls with call target: 55 % (threshold 85 %). Percentage of expressions with known type: 70 % (threshold 85 %)."}}, @@ -260,7 +260,7 @@ func validCodeQLSARIFWithoutQualityDiagnostic() []byte { "$schema":"https://json.schemastore.org/sarif-2.1.0.json", "version":"2.1.0", "runs":[{ - "tool":{"driver":{"name":"CodeQL","semanticVersion":"2.26.0"}}, + "tool":{"driver":{"name":"CodeQL","semanticVersion":"2.27.1"}}, "properties":{"metricResults":[{"ruleId":"cs/summary/lines-of-code","value":75000}]}, "invocations":[{"executionSuccessful":true,"toolExecutionNotifications":[ {"descriptor":{"id":"csharp/autobuilder/buildless/complete"},"message":{"text":"C# analysis with build-mode 'none' completed."}}, diff --git a/cli/release-automation/internal/automation/testdata/codeql-cli-2.26.0-sarif.json b/cli/release-automation/internal/automation/testdata/codeql-cli-2.27.1-sarif.json similarity index 82% rename from cli/release-automation/internal/automation/testdata/codeql-cli-2.26.0-sarif.json rename to cli/release-automation/internal/automation/testdata/codeql-cli-2.27.1-sarif.json index bd43c2d1b4..361b2ff4bb 100644 --- a/cli/release-automation/internal/automation/testdata/codeql-cli-2.26.0-sarif.json +++ b/cli/release-automation/internal/automation/testdata/codeql-cli-2.27.1-sarif.json @@ -1 +1 @@ -{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"CodeQL","semanticVersion":"2.26.0"}},"invocations":[{"executionSuccessful":true,"toolExecutionNotifications":[{"descriptor":{"id":"csharp/autobuilder/buildless/complete","index":16},"message":{"text":"C# analysis with build-mode 'none' completed.","markdown":"C# analysis with build-mode 'none' completed."}},{"descriptor":{"id":"csharp/diagnostic/database-quality","index":6},"message":{"text":"Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 68 % (threshold 85 %). Percentage of expressions with known type: 82 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes)."}},{"descriptor":{"id":"cs/diagnostics/successfully-extracted-files","index":3},"message":{"text":""}}]}],"properties":{"metricResults":[{"ruleId":"cs/summary/lines-of-code","value":119122}]},"results":[]}]} +{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[{"tool":{"driver":{"name":"CodeQL","semanticVersion":"2.27.1"}},"invocations":[{"executionSuccessful":true,"toolExecutionNotifications":[{"descriptor":{"id":"csharp/autobuilder/buildless/complete","index":13},"message":{"text":"C# analysis with build-mode 'none' completed.","markdown":"C# analysis with build-mode 'none' completed."}},{"descriptor":{"id":"csharp/diagnostic/database-quality","index":6},"message":{"text":"Scanning C# code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 69 % (threshold 85 %). Percentage of expressions with known type: 82 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning C# using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes)."}},{"descriptor":{"id":"cs/diagnostics/successfully-extracted-files","index":3},"message":{"text":""}}]}],"properties":{"metricResults":[{"ruleId":"cs/summary/lines-of-code","value":311595}]},"results":[]}]} diff --git a/cli/release-automation/internal/automation/testdata/codeql-cli-2.26.0-security-probe-sarif.json b/cli/release-automation/internal/automation/testdata/codeql-cli-2.27.1-security-probe-sarif.json similarity index 78% rename from cli/release-automation/internal/automation/testdata/codeql-cli-2.26.0-security-probe-sarif.json rename to cli/release-automation/internal/automation/testdata/codeql-cli-2.27.1-security-probe-sarif.json index dd7b1e6408..42752c9e82 100644 --- a/cli/release-automation/internal/automation/testdata/codeql-cli-2.26.0-security-probe-sarif.json +++ b/cli/release-automation/internal/automation/testdata/codeql-cli-2.27.1-security-probe-sarif.json @@ -6,14 +6,14 @@ "tool": { "driver": { "name": "CodeQL", - "semanticVersion": "2.26.0" + "semanticVersion": "2.27.1" } }, "results": [ { "ruleId": "cs/command-line-injection", "message": { - "text": "This command line depends on a user-provided value." + "text": "This command line depends on a [user-provided value](1)." }, "locations": [ { @@ -27,7 +27,7 @@ } ], "properties": { - "probeQueryPack": "codeql/csharp-queries@1.7.5", + "probeQueryPack": "codeql/csharp-queries@1.10.0", "probeQuerySuite": "security-extended" } } diff --git a/docs/adr/0001-csharp-security-scan-engine.md b/docs/adr/0001-csharp-security-scan-engine.md index 9c78559b38..2abcad1804 100644 --- a/docs/adr/0001-csharp-security-scan-engine.md +++ b/docs/adr/0001-csharp-security-scan-engine.md @@ -4,10 +4,12 @@ Date: 2026-07-15 ## Decision -The repository uses GitHub CodeQL for C# security analysis with `build-mode: none` and the `security-extended` query suite. The workflow pins `github/codeql-action` to the full commit SHA for v4.37.0 and selects its linked CodeQL 2.26.0 toolchain. A repository-owned Go guard validates the scanner identity, tool version, SARIF structure, successful no-build completion, extracted-source evidence, and database-quality floor before upload. +The repository uses GitHub CodeQL for C# security analysis with `build-mode: none` and the `security-extended` query suite. The workflow pins `github/codeql-action` to the full commit SHA for v4.38.2 and selects its linked CodeQL 2.27.1 toolchain. A repository-owned Go guard validates the scanner identity, tool version, SARIF structure, successful no-build completion, extracted-source evidence, and database-quality floor before upload. The approved local proof analyzed all 975 C# files under `Packages/src` and `Assets/Tests`, generated SARIF 2.1.0, and reported call-target resolution of 68% and known-type resolution of 82%. A second proof added a temporary command-injection source outside the repository checkout. CodeQL 2.26.0 with `codeql/csharp-queries@1.7.5` and `security-extended` reported `cs/command-line-injection`. Only the resulting minimal SARIF evidence is retained under release-automation test data; the vulnerable source is not part of the repository. +The 2026-10-05 toolchain bump to CodeQL 2.27.1 repeated both proofs on the then-current corpus of 2,224 C# files. The no-build scan still emitted the database-quality diagnostic in the same format (69% call targets, 82% known types), and `codeql/csharp-queries@1.10.0` with `security-extended` again reported `cs/command-line-injection` for a temporary probe outside the repository checkout. The recorded test data was regenerated from those runs. + The probe is recorded rather than rerun as a separate CodeQL job on every pull request because a second database extraction and 70-query analysis would approximately double C# scan time. The production workflow fixes both the action commit and reported CodeQL semantic version, while tests reject scanner-version drift and require the recorded probe finding to remain non-empty. ## Alternatives diff --git a/scripts/test-security-scan-workflow.sh b/scripts/test-security-scan-workflow.sh index 3ea171abe2..39db039d82 100755 --- a/scripts/test-security-scan-workflow.sh +++ b/scripts/test-security-scan-workflow.sh @@ -34,8 +34,8 @@ assert_step_order() { assert_contains " contents: read" assert_contains " security-events: write" assert_absent " actions: read" -assert_contains "uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0" -assert_contains "uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0" +assert_contains "uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2" +assert_contains "uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2" assert_contains "uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9" assert_contains " build-mode: none" assert_contains " tools: linked"