Skip to content

Fix allocation-size overflow risk in model pricing merge#43468

Merged
pelikhan merged 3 commits into
mainfrom
copilot/uk-ai-resilience-fix-allocation-size-overflow
Jul 5, 2026
Merged

Fix allocation-size overflow risk in model pricing merge#43468
pelikhan merged 3 commits into
mainfrom
copilot/uk-ai-resilience-fix-allocation-size-overflow

Conversation

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

CodeQL flagged pkg/workflow/compiler_model_pricing.go for allocation-size overflow in the workflow compiler’s model-pricing path. The issue was in capacity arithmetic used while cloning the top-level ModelCosts map.

  • Remove overflow-prone allocation math

    • Stop pre-sizing the top-level clone in mergeModelPricingIntoModelCosts.
    • Let the map grow normally instead of deriving capacity from input size.
  • Add a regression guard

    • Add a focused test that parses compiler_model_pricing.go and asserts the top-level result map allocation in mergeModelPricingIntoModelCosts does not reintroduce a capacity argument.
  • Preserve existing merge behavior

    • Existing tests continue to cover merge correctness, non-mutation of input, and resolver behavior; this change narrows the security fix to allocation strategy only.

Example of the change:

// before
result := make(map[string]any, len(modelCosts))

// after
result := make(map[string]any)
maps.Copy(result, modelCosts)

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix allocation-size-overflow in compiler_model_pricing.go Fix allocation-size overflow risk in model pricing merge Jul 5, 2026
Copilot AI requested a review from pelikhan July 5, 2026 05:38
@pelikhan pelikhan marked this pull request as ready for review July 5, 2026 05:42
Copilot AI review requested due to automatic review settings July 5, 2026 05:42
@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #43468 does not have the 'implementation' label and has only 51 new lines of code in business logic directories (threshold is 100).

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR Code Quality Reviewer failed during code quality review.

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request addresses a CodeQL-reported allocation-size overflow risk in the workflow compiler’s model pricing merge by removing overflow-prone map pre-sizing and adding a regression test to prevent reintroduction of capacity arithmetic.

Changes:

  • Stop pre-sizing the top-level result map in mergeModelPricingIntoModelCosts to avoid capacity arithmetic in a security-sensitive path.
  • Add an AST-based regression test that asserts the top-level clone allocation does not pass a capacity argument.
Show a summary per file
File Description
pkg/workflow/compiler_model_pricing.go Removes make(..., len(...)) pre-sizing for the top-level clone map to avoid overflow-prone allocation math.
pkg/workflow/compiler_model_pricing_test.go Adds a regression test that parses the source and checks the result map allocation does not include a capacity argument.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Low

Comment on lines +108 to +130
for _, stmt := range fn.Body.List {
assign, ok := stmt.(*ast.AssignStmt)
if !ok || len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {
continue
}
lhs, ok := assign.Lhs[0].(*ast.Ident)
if !ok || lhs.Name != "result" {
continue
}
call, ok := assign.Rhs[0].(*ast.CallExpr)
if !ok {
continue
}
fun, ok := call.Fun.(*ast.Ident)
if !ok || fun.Name != "make" {
continue
}

foundResultMake = true
assert.Len(t, call.Args, 1, "result map allocation should not pre-size capacity")
return false
}
return false
@github-actions github-actions Bot mentioned this pull request Jul 5, 2026
@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

⚠️ Test Quality Score: 75/100 — Acceptable

Analyzed 13 test(s): 12 design, 1 implementation, 0 violation(s).

📊 Metrics (13 tests)
Metric Value
Analyzed 13 (Go: 13, JS: 0)
✅ Design 12 (92%)
⚠️ Implementation 1 (8%)
Edge/error coverage 8 (62%)
Duplicate clusters 0
Inflation YES (48 test lines : 3 prod lines = 16:1, informational)
🚨 Violations 0
Test File Classification Issues
TestModelCostsHasPricingFor compiler_model_pricing_test.go:20 design_test Covers nil map, wrong provider, any-provider match
TestMergeModelPricingIntoModelCosts_EmptyBase compiler_model_pricing_test.go:40 design_test Edge: nil base map
TestMergeModelPricingIntoModelCosts_PreservesExisting compiler_model_pricing_test.go:58 design_test Behavioral: existing entries preserved
TestMergeModelPricingIntoModelCosts_DoesNotMutateInput compiler_model_pricing_test.go:73 design_test Behavioral: immutability invariant
TestMergeModelPricingIntoModelCosts_DoesNotPreSizeTopLevelClone compiler_model_pricing_test.go:88 implementation_test AST-inspects source to assert no capacity hint in make
TestResolveEngineProviderForPricing compiler_model_pricing_test.go:128 design_test Table-driven (7 subtests), priority + normalization
TestResolveModelPricingIfMissing_NilResolver compiler_model_pricing_test.go:148 design_test Edge: nil resolver guard
TestResolveModelPricingIfMissing_AlreadyPresent compiler_model_pricing_test.go:155 design_test Edge: skips resolver when pricing already present
TestResolveModelPricingIfMissing_InjectsFromResolver compiler_model_pricing_test.go:173 design_test Happy path: resolver injects pricing
TestResolveModelPricingIfMissing_ResolverReturnsNothing compiler_model_pricing_test.go:191 design_test Edge: resolver no-op path
TestResolveModelPricingIfMissing_SplitsQualifiedModelAndNormalizesProvider compiler_model_pricing_test.go:200 design_test Qualified provider/model parsing + normalization
TestResolveModelPricingIfMissing_SkipsWhenProviderCannotBeNormalized compiler_model_pricing_test.go:220 design_test Edge: unresolvable provider skipped
TestResolveModelPricingIfMissing_SkipsMalformedQualifiedModel compiler_model_pricing_test.go:237 design_test Edge: malformed provider/model strings
⚠️ Flagged Tests (1)

TestMergeModelPricingIntoModelCosts_DoesNotPreSizeTopLevelClone (compiler_model_pricing_test.go:88) — implementation_test. This test uses Go AST parsing to verify that make(map[string]any) is invoked without a capacity argument in the production function body. It directly inspects source code structure rather than testing observable runtime behavior. The security rationale is sound — it guards against re-introducing the allocation-size overflow risk (the core fix in this PR), which is difficult to detect purely through behavioral tests. However, it will silently break or false-pass if the function is refactored (e.g., variable renamed from result, or the function extracted). Recommendation: add a comment explaining the security invariant and consider a complementary behavioral test.

Inflation note (informational): 48 lines added to the test file vs. 3 lines changed in production (16:1). This is expected given that the bug fix was a targeted change and the test suite was built from scratch to cover the full feature. Not a quality concern here.

Verdict

Passed. 8% implementation tests (threshold: 30%). No guideline violations detected. Build tag (//go:build !integration) present on line 1. No mock libraries used. The AST-inspection test is unusual but justified by the security context of this PR.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 41.8 AIC · ⌖ 8.37 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Test Quality Sentinel: 75/100. 8% implementation tests (threshold: 30%). No violations detected.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Fix allocation-size overflow risk in model pricing merge

This is a clean, focused security fix. The change is minimal, correct, and well-tested.

What was fixed: CodeQL flagged make(map[string]any, len(modelCosts)) in mergeModelPricingIntoModelCosts. The old code even had a comment acknowledging the overflow risk (Do not add +1 here...) but still passed len(modelCosts) as a capacity hint — the new fix removes it entirely, letting the map grow naturally via maps.Copy.

Test quality: The AST-based regression guard is an excellent choice. It verifies the actual source at the syntax level so any future re-introduction of a capacity argument will be caught at test time, regardless of the value passed.

Scope is appropriate: The fix targets only the CodeQL-flagged allocation. The cost := make(map[string]string, len(pricing)) on line 159 uses a similar pattern but is bounded by trusted internal data (pricing is a typed map[string]float64 argument, not externally sized), so excluding it is reasonable.

✅ Correctness: removing the capacity hint does not change observable behavior — Go maps grow on demand.
✅ Security: allocation-size arithmetic derived from the modelCosts input is eliminated.
✅ Tests: existing merge-correctness tests are unchanged; new AST guard prevents regression.

No blocking issues found. LGTM.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 42.1 AIC · ⌖ 5.98 AIC · ⊞ 4.8K

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — requesting changes on two small but meaningful issues.

📋 Key Themes & Highlights

Key Themes

  • Incomplete scope of the fix: the cost map on line 159 still uses len(pricing) capacity hint — the same pattern CodeQL flagged on result. Both allocations live in the same security-sensitive function and should be treated consistently.
  • Fragile test infrastructure: the regression guard relies on runtime.Caller(0) to locate the source file, which breaks in environments where source and binary paths diverge (Docker, module cache, go install). It also only scans top-level statements in the function body, so a future refactor that moves the assignment inside any compound block would silently pass the test.

Positive Highlights

  • ✅ The core fix is minimal and correct — removing the capacity argument eliminates the overflow risk for the top-level clone
  • ✅ Adding an AST-based regression test is an excellent pattern for enforcement of security invariants — the intent is right even if the implementation needs tightening
  • ✅ The PR description is clear about root cause, scope, and what behavior is preserved

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 62.9 AIC · ⌖ 6.33 AIC · ⊞ 6.6K
Comment /matt to run again

Comments that could not be inline-anchored

pkg/workflow/compiler_model_pricing.go:159

[/diagnosing-bugs] The cost map still pre-sizes with len(pricing) — the same allocation-size pattern CodeQL flagged on result. Since pricing comes from parsed model data (potentially untrusted), this is the same category of risk.

<details>
<summary>💡 Suggested change</summary>

// before
cost := make(map[string]string, len(pricing))

// after
cost := make(map[string]string)

For consistency with the fix applied to result, removing the capacity hint here ensures the who…

pkg/workflow/compiler_model_pricing_test.go:97

[/tdd] runtime.Caller(0) returns the path to the compiled test binary, not the source file — this test will fail or produce wrong results in environments where source paths differ from build paths (e.g., Docker, CI with module cache, or after go install).

<details>
<summary>💡 Safer alternative: embed the source via go:embed</summary>

Using //go:embed compiler_model_pricing.go instead of runtime.Caller ensures the source is always available regardless of build environment:

</details>

<details><summary>pkg/workflow/compiler_model_pricing_test.go:108</summary>

**[/tdd]** The AST walk only iterates top-level statements in `fn.Body.List`it will miss a `result` assignment that appears inside an `if` block, a `switch`, or any other nested compound statement, silently setting `foundResultMake = false` and causing the test to pass without actually verifying anything.

&lt;details&gt;
&lt;summary&gt;💡 Use recursive AST inspection instead&lt;/summary&gt;

Replace the manual loop with a nested `ast.Inspect` call scoped to the function body:

```go
ast.Inspect(fn.Body, func…

</details>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

COMMENT — Security fix correct; two issues need attention

The core fix (removing the capacity hint from the top-level map allocation) correctly addresses the CodeQL finding. The logic of the function and its mutation-isolation guarantees are intact.

Findings

1. Medium — runtime.Caller(0) breaks under -trimpath (test file, line 97)
The new AST regression guard resolves the source file path using runtime.Caller(0). Under -trimpath builds this returns a module-relative path, not an absolute filesystem path; parser.ParseFile then fails and require.NoError kills the test with a confusing error. The guard should check filepath.IsAbs and skip, or use //go:embed to avoid the runtime path lookup entirely.

2. Low — Inconsistent capacity removal (production file, line 158)
make(map[string]string, len(pricing)) retains a capacity hint derived from a resolver-supplied map. The PR's stated goal is to eliminate allocation-size arithmetic in this path; this line is the next candidate for the same CodeQL rule. Removing the hint is a one-line change with negligible performance impact.

The first finding is a real latent flake; the second is a correctness-of-intent gap. Neither blocks the security fix from being correct, but both should be addressed before merge.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • proxy.golang.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "proxy.golang.org"

See Network Configuration for more information.

🔎 Code quality review by PR Code Quality Reviewer · 204.6 AIC · ⌖ 5.73 AIC · ⊞ 5.4K
Comment /review to run again

Comments that could not be inline-anchored

pkg/workflow/compiler_model_pricing_test.go:97

runtime.Caller(0) breaks under -trimpath builds — this test will panic/fail in any CI step that strips file paths, producing a confusing no such file or directory error rather than a meaningful test failure.

<details>
<summary>💡 Details and suggested fix</summary>

Under -trimpath (used in release builds and many CI pipelines), runtime.Caller(0) returns a module-relative path such as github.com/github/gh-aw/pkg/workflow/compiler_model_pricing_test.go, not an absolute filesyste…

pkg/workflow/compiler_model_pricing.go:158

Inconsistent capacity removal: make(map[string]string, len(pricing)) still uses a capacity hint from the caller-controlled pricing map, which CodeQL or a future audit may flag by the same logic that motivated this PR.

<details>
<summary>💡 Details</summary>

The PR description says the goal is to eliminate "allocation-size arithmetic in this security-sensitive path". The production code removes make(map[string]any, len(modelCosts)) (correct) but leaves:

cost := make(map[string</details>

<details><summary>pkg/workflow/compiler_model_pricing.go:187</summary>

**Shallow clone doesn&#39;t protect deep mutation**: the function&#39;s doc comment claims it &quot;returns a new map to avoid mutating the input&quot;, but existing model-entry sub-maps are aliasednot clonedso deep mutations via the returned map silently corrupt the input.

&lt;details&gt;
&lt;summary&gt;💡 Details&lt;/summary&gt;

The function carefully clones the top-level, `providers`, per-provider, and `models` layers:

```go
maps.Copy(modelsMap, mm)   // copies key→value pairs from original models map

But `maps.C…

pkg/workflow/compiler_model_pricing_test.go:90

DoesNotMutateInput test uses an empty models map — the aliasing bug it claims to test is unexercised.

<details>
<summary>💡 Details</summary>

The test constructs:

base := map[string]any{
    &quot;providers&quot;: map[string]any{
        &quot;openai&quot;: map[string]any{
            &quot;models&quot;: map[string]any{},  // empty!
        },
    },
}

Because openai.models is empty, maps.Copy(modelsMap, mm) copies zero entries, so no aliasing ever occurs and the assertion passes vacuously regardless…

@pelikhan

pelikhan commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

@copilot invoke pr-finisher skill

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address any remaining review feedback, and rerun checks once the branch is ready.

Generated by 👨‍🍳 PR Sous Chef · 12.3 AIC · ⌖ 5.1 AIC · ⊞ 3.5K ·
Comment /souschef to run again

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

🤖 PR Triage — Run #7

Field Value
Category bug (security)
Risk 🟡 medium
Score 70 / 100
Action fast_track
Batch pr-batch:security-fixes

Score breakdown: impact 35 + urgency 22 + quality 13

Fixes CodeQL-flagged allocation-size overflow in compiler_model_pricing.go: removes capacity argument from make(map[string]any, len(...)), adds regression guard test. 2 files, +51/-3. Clean targeted fix — expedite review alongside #43467.

Generated by 🔧 PR Triage Agent · 56.3 AIC · ⌖ 8.84 AIC · ⊞ 5.5K ·

@pelikhan pelikhan merged commit 80c8b87 into main Jul 5, 2026
29 checks passed
@pelikhan pelikhan deleted the copilot/uk-ai-resilience-fix-allocation-size-overflow branch July 5, 2026 07:29
Copilot stopped work on behalf of pelikhan due to an error July 5, 2026 07:50
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[uk-ai-resilience] [security] Fix allocation-size-overflow in compiler_model_pricing.go (CodeQL HIGH #638, CWE-190)

4 participants