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

CopilotAI 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:

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

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

github-actionsBot 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-actionsBot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actionsBot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actionsBot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

CopilotAI 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
FileDescription
pkg/workflow/compiler_model_pricing.goRemoves make(..., len(...)) pre-sizing for the top-level clone map to avoid overflow-prone allocation math.
pkg/workflow/compiler_model_pricing_test.goAdds 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-actionsgithub-actionsBot mentioned this pull request Jul 5, 2026
@github-actions

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)
MetricValue
Analyzed13 (Go: 13, JS: 0)
✅ Design12 (92%)
⚠️ Implementation1 (8%)
Edge/error coverage8 (62%)
Duplicate clusters0
InflationYES (48 test lines : 3 prod lines = 16:1, informational)
🚨 Violations0
TestFileClassificationIssues
TestModelCostsHasPricingForcompiler_model_pricing_test.go:20design_testCovers nil map, wrong provider, any-provider match
TestMergeModelPricingIntoModelCosts_EmptyBasecompiler_model_pricing_test.go:40design_testEdge: nil base map
TestMergeModelPricingIntoModelCosts_PreservesExistingcompiler_model_pricing_test.go:58design_testBehavioral: existing entries preserved
TestMergeModelPricingIntoModelCosts_DoesNotMutateInputcompiler_model_pricing_test.go:73design_testBehavioral: immutability invariant
TestMergeModelPricingIntoModelCosts_DoesNotPreSizeTopLevelClonecompiler_model_pricing_test.go:88implementation_testAST-inspects source to assert no capacity hint in make
TestResolveEngineProviderForPricingcompiler_model_pricing_test.go:128design_testTable-driven (7 subtests), priority + normalization
TestResolveModelPricingIfMissing_NilResolvercompiler_model_pricing_test.go:148design_testEdge: nil resolver guard
TestResolveModelPricingIfMissing_AlreadyPresentcompiler_model_pricing_test.go:155design_testEdge: skips resolver when pricing already present
TestResolveModelPricingIfMissing_InjectsFromResolvercompiler_model_pricing_test.go:173design_testHappy path: resolver injects pricing
TestResolveModelPricingIfMissing_ResolverReturnsNothingcompiler_model_pricing_test.go:191design_testEdge: resolver no-op path
TestResolveModelPricingIfMissing_SplitsQualifiedModelAndNormalizesProvidercompiler_model_pricing_test.go:200design_testQualified provider/model parsing + normalization
TestResolveModelPricingIfMissing_SkipsWhenProviderCannotBeNormalizedcompiler_model_pricing_test.go:220design_testEdge: unresolvable provider skipped
TestResolveModelPricingIfMissing_SkipsMalformedQualifiedModelcompiler_model_pricing_test.go:237design_testEdge: 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-actionsgithub-actionsBot 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-actionsgithub-actionsBot 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[bot]
github-actionsBot previously requested changes Jul 5, 2026

@github-actionsgithub-actionsBot 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>

// beforecost:=make(map[string]string, len(pricing))
// aftercost:=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]**TheASTwalkonlyiteratestop-levelstatementsin`fn.Body.List`itwillmissa`result`assignmentthatappearsinsidean`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-actionsgithub-actionsBot 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>**Shallowclonedoesn&#39;tprotectdeepmutation**: thefunction&#39;sdoccommentclaimsit&quot;returnsanewmaptoavoidmutatingtheinput&quot;, butexistingmodel-entrysub-mapsarealiasednotclonedsodeepmutationsviathereturnedmapsilentlycorrupttheinput.
&lt;details&gt;
&lt;summary&gt;💡 Details&lt;/summary&gt;
Thefunctioncarefullyclonesthetop-level, `providers`, per-provider, and`models`layers:
```gomaps.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

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

Copy link
Copy Markdown
Contributor

🤖 PR Triage — Run #7

FieldValue
Categorybug (security)
Risk🟡 medium
Score70 / 100
Actionfast_track
Batchpr-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 mainJul 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

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.3

Sign up for freeto 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

@pelikhan@gh-aw-bot