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
4 changes: 2 additions & 2 deletions .github/workflows/security-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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."}},
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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."}},
Expand Down Expand Up @@ -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."}},
Expand All @@ -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 %)."}},
Expand All @@ -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."}},
Expand Down
Original file line number Diff line number Diff line change
@@ -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":[]}]}
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
{
Expand All @@ -27,7 +27,7 @@
}
],
"properties": {
"probeQueryPack": "codeql/csharp-queries@1.7.5",
"probeQueryPack": "codeql/csharp-queries@1.10.0",
"probeQuerySuite": "security-extended"
}
}
Expand Down
4 changes: 3 additions & 1 deletion docs/adr/0001-csharp-security-scan-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions scripts/test-security-scan-workflow.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading