Skip to content

An appsettings.json in the working directory widens the --config allow-list: its Repositories entries past the config file's count stay allowed #120

Open

Description

@matt-edmondson

What's wrong

RunAsync in GitBranchStateCache.Tool/Program.cs (~L138–152) adds the --config file after WebApplication.CreateBuilder has already loaded appsettings.json and appsettings.{Environment}.json from the working directory. The comment there gives the intent: "so an explicitly named file beats an appsettings.json that happens to be in the working directory".

For scalar settings it does win. For Upstreams:{name}:Repositories it doesn't, because .NET configuration merges arrays by index. The later file overwrites indexes 0..n-1. Any higher index from appsettings.json survives and gets bound as well.

This is the same merge that #46 fixed between --allow and --config, where ReplaceAllowLists now replaces the list instead of merging it. The appsettings.json → --config path was left as it was.

Repro (built tool, at 9912058)

Working directory appsettings.json:

{"GitBranchStateCache":{"Upstreams":{"github":{"BaseUrl":"https://github.com","Repositories":["studio/sandbox.git","studio/secret.git"]}}}}

prod.json:

{"GitBranchStateCache":{"Upstreams":{"github":{"Repositories":["studio/game.git"]}}}}

Run ktsu.GitBranchStateCache.Tool --port 18099 --mirror-root <dir> --config prod.json, then POST /v1/github/studio/<repo>.git/state with no credential. 401 means the repository passed the allow-list and reached admission; 404 means the allow-list refused it.

repo observed expected
game 401 401
sandbox 404 404
secret 401 404
other 404 404

Binding the options directly gives Repositories = [studio/game.git, studio/secret.git].

Why it matters

The README says the allow-list is what stops arbitrary callers from making the service hold a permanent mirror clone. Here the operator named one repository in the file they passed explicitly. A leftover dev or base-image appsettings.json in the working directory quietly adds more, and nothing reports it.

Suggested fix / acceptance criteria

  • Treat each upstream's Repositories as one value that the highest-precedence source replaces whole. For example, after building configuration, take each upstream's list from the last provider that sets any Upstreams:{name}:Repositories:* key, as ReplaceAllowLists already does for --allow.
  • With the files above, only studio/game.git is allowed.
  • Add a test alongside AllowFlagTests that layers an appsettings.json-style source under a --config source with a shorter list.
  • Optional: log at startup which source supplied each upstream's allow-list.

Activity

  1. matt-edmondson commented on Nov 5, 2026

    @matt-edmondson
    ContributorAuthor

    Triage


    Generated by Claude Code

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

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions