Skip to content

fix: return real defaults when codeowners.toml fails to parse - #184

Draft
asyncawaitpromise wants to merge 1 commit into
feat/approval-retention-configfrom
fix/config-partial-parse
Draft

fix: return real defaults when codeowners.toml fails to parse#184
asyncawaitpromise wants to merge 1 commit into
feat/approval-retention-configfrom
fix/config-partial-parse

Conversation

@asyncawaitpromise

@asyncawaitpromise asyncawaitpromise commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Related PR(s)

Stacked on #183. Independent of the retention feature itself — the bug it fixes predates that work.

Summary / Background

ReadConfig handed the same default Config to the TOML parser and to its own error paths. toml.Unmarshal merges into the pointer fields already present, so a file which failed halfway left its partially parsed values in the instance the error path then returned.

The caller logs using default config and carries on — with whatever the parser managed to read before it failed.

Why it matters

A malformed codeowners.toml could turn enforcement off, or widen admin bypass, while the logs said defaults were in force. The config is a security boundary, so an error path has to return values that never reflect anything read from the file.

The fix

A fresh instance per call. The parser gets its own, and every error path builds another. The nil-section fixups go away with it, since the defaults are no longer shared and cannot be clobbered.

Note that a shallow struct copy is not enough here — the pointer fields would still be shared, which is exactly what go-toml merges into.

Verification

A test parses a malformed file and asserts every returned field equals the real default. Reverting the fix turns it red.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the 'Approval Retention' feature, which allows users to configure whether existing approvals are retained across specific types of changes, such as whitespace, comments, formatting, string literals, or renames. The implementation includes updates to the configuration parser, new helper methods for flag resolution, comprehensive unit and integration tests, and updated documentation in the README. Since there are no review comments, I have no additional feedback to provide.

ReadConfig handed the same default Config to the parser and to its own error
paths. toml.Unmarshal merges into the pointer fields already present, so a
file which failed halfway left its partially parsed values in the instance
the error path then returned. The caller logged "using default config" and
carried on with whatever the parser had managed to read.

A malformed file could therefore turn enforcement off, or widen admin bypass,
while the logs said defaults were in force.

A fresh instance per call fixes it: the parser gets its own, and every error
path builds another. The nil-section fixups go away with it, since the
defaults are no longer shared and cannot be clobbered.
@asyncawaitpromise
asyncawaitpromise force-pushed the fix/config-partial-parse branch from 0572660 to 2c52723 Compare August 19, 2026 00:50
@asyncawaitpromise asyncawaitpromise changed the title fix/config partial parse fix: return real defaults when codeowners.toml fails to parse Aug 19, 2026
@asyncawaitpromise
asyncawaitpromise changed the base branch from main to feat/approval-retention-config August 19, 2026 08:11
@asyncawaitpromise
asyncawaitpromise marked this pull request as ready for review August 20, 2026 09:19
@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown

Confidence Score: 4/5

The PR appears safe to merge, with a non-blocking opportunity to strengthen the malformed-config regression test across every default field.

The implementation discards the parser-mutated object on failure and returns a fresh default; the remaining concern is limited to incomplete regression coverage for other security-relevant fields.

Files Needing Attention: internal/config/config_test.go

Important Files Changed

Filename Overview
internal/config/config.go Correctly separates the parser target from fresh error-path defaults while preserving successful partial-config behavior.
internal/config/config_test.go Adds focused regression coverage, but checks only two nested sections rather than the complete pristine-default invariant.

Reviews (1): Last reviewed commit: "fix: return real defaults when codeowner..." | Re-trigger Greptile

Comment on lines +278 to +289
if config.Enforcement == nil {
t.Fatal("expected Enforcement to be set")
}
if config.Enforcement.Approval || !config.Enforcement.FailCheck {
t.Errorf("Enforcement: expected default {Approval:false FailCheck:true}, got %+v", *config.Enforcement)
}
if config.ApprovalRetention == nil {
t.Fatal("expected ApprovalRetention to be set")
}
if config.ApprovalRetention.Enabled {
t.Error("ApprovalRetention.Enabled: expected default false")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Incomplete default regression coverage

The malformed-config test checks only Enforcement and ApprovalRetention.Enabled, leaving fields such as AdminBypass, review limits, and slice-valued defaults outside the stated all-fields invariant. Comparing the complete result with a pristine default would prevent an untested field from leaking through a future regression while this test still passes.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@asyncawaitpromise
asyncawaitpromise marked this pull request as draft August 21, 2026 21:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant