Repository navigation
test(tenancy): allow only login to call the cross-farm FindBySlugAsync - #1061
Conversation
…ntract # Conflicts: # src/Cluckwork.Application/Features/Users/IAccessLookup.cs # src/Cluckwork.Application/Features/Users/IAccessOperations.cs # src/Cluckwork.Infrastructure/Identity/AccessOperations.cs
IAccountRepository is Farm's seam, so any peer module may take it, and FindBySlugAsync ignores the tenant filter. FindBySlugCallerTests fails when any src file other than IdentityProvider references the member. Closes #1053
…aller-guard # Conflicts: # src/AGENTS.md
|
Verdict: request changes. Two P2 gaps undermine the new caller guard. There is no newly introduced production vulnerability: this diff changes tests, comments and one registry justification. The findings concern code changes that the guard promises to reject. Reviewed F1, P2: exempt the login symbol, not its entire file. FindBySlugCallerTests.cs:19–22,35–42 exempts every reference in the 1,958-line I added this helper in private async Task<string?> OtherFarmNameAsync(CancellationToken ct) =>
(await accounts.FindBySlugAsync("another-farm", ct))?.Name;The existing F2, P2: the parser omits framework symbols the compiler defines. FindBySlugCallerTests.cs:60–62 uses ModuleLedgerScanner.cs:41–48, whose symbol list omits #if NETCOREAPP3_1_OR_GREATER
await accounts.FindBySlugAsync("another-farm", ct);
#endifMSBuild's F3, P3: the shared source walk skips C# that the project compiles. FindBySlugCallerTests.cs:30 inherits GuardScanner.cs:1384–1389, which skips The 21-test set was Independent mutation results
The six author mutations were reproduced from the author's script against the review checkout and reverted individually. A5 is an acknowledged limitation, not an additional finding. Generic/interface dispatch, method groups and ordinary partial/generated source do not evade this identifier scan. A fresh raw query still requires a tenant-bypass classification. Security review, applied manually. I traced farm-code input through Registry check. The JSON edit changes exactly one Thermo-nuclear quality review, P3 and slice-scoped. The new guard is 63 lines, reuses the repository's parser and source walker, adds no dependency and introduces no product branching. No file crosses the 1,000-line threshold in this diff. The useful structural correction is F1: reuse the existing symbol extractor instead of a file exemption. A semantic call-graph analyzer or moving the query into Access would cost more and is unnecessary for the two P2 fixes. No separate maintainability blocker. Ponytail review, complexity only: Lean already. Ship. There is no worthwhile deletion finding; the correctness verdict remains request changes. Restored-head verification: 62/62 tests passed in Release across 11 classes, including all 55 TenantBypass cases. Detailed review notes, exact mutation patches/scripts, compiler-symbol output and per-class logs are retained locally in |
…with the build's symbols Review of #1061 (Astra): - F1: exempt IdentityProvider.ResolveFarmCodeAsync by enclosing symbol, not the whole file, so a second IdentityProvider method fails. - F2: the shared ModuleLedgerScanner.ParseOptions now reads the test build's own DefineConstants, embedded after the SDK's implicit framework symbols, instead of a hand list that omitted NETCOREAPP3_1_OR_GREATER. - F3: the source walk skips only a project's own bin/ and obj/, matching the SDK's default compile globs; node_modules/ under src/ is walked.
|
Reply to Astra's review of F1 (P2), fixed. The exemption is now one enclosing symbol,
F2 (P2), fixed at the source.
F3 (P3), aligned. Verification at |
|
Verdict: approve the fix round with one nonblocking P3 follow-up. No PRODUCT defect or blocking guard defect remains. F1 is fixed, F2 now uses the compiler symbols, and the original F3 Reviewed Correction to my first review. The worker is right about F1 is closed. The allowance now uses F2 is closed; the complementary inactive-code check remains necessary. At ModuleLedgerScanner.cs:42–48, the symbols come from the test assembly's metadata. The csproj target at line 29 records them after The metadata is a snapshot of the test project's build, not a per-source-project evaluation. I evaluated all five I also tested divergent source settings rather than assuming they could not occur:
These results support fail-closed handling of compiler/parser drift for scanned source. The metadata alone does not prove future projects have matching settings. I did not install or build a different TFM; a symbol unknown to the test parser is covered by the inactive-code check, as the source-only-symbol mutation demonstrates. F3's original mutation is closed; one P3 blind spot remains. A wrapper under However, GuardScanner.cs:1386–1389 treats any directory containing a This requires additional source and a nested project marker; no such caller exists at head. It is a residual form of the pre-existing directory blind spot, not a new regression or a reason to block this improvement. A follow-up should derive output exclusions from actual build-project boundaries, or prove that omitted files cannot belong to an enclosing project's compile inputs. The comment that the walk mirrors SDK exclusions is broader than this case supports. The 32-test mutant set comprised Shared-scanner and quality review. I applied the thermo-nuclear and ponytail checks to the fix delta. Reusing the existing symbol extractor and MSBuild assembly-metadata support keeps the fix small. No new package, product abstraction, or file-size threshold crossing. The changes broaden source discovery and remove the hand-maintained framework list. No new structural or over-engineering finding; the P3 exclusion-boundary issue is the only remaining guard concern. Ponytail, complexity only: Lean already. Ship.
Restored-head verification: 340/340 targeted tests passed in Debug and 340/340 in Release across 24 classes per configuration, each run separately with Exact mutation scripts, patches, per-class logs, evaluated compiler properties and the old-head reproduction are retained in |
TL;DR: a new guard,
FindBySlugCallerTests, fails CI when any member except login'sIdentityProvider.ResolveFarmCodeAsyncreferencesIAccountRepository.FindBySlugAsync. The review round also fixes two weaknesses in the shared source walk that every source guard inherits. No production code changes; login is unchanged.Closes #1053
Design choice: caller guard, not a move
FindBySlugAsyncignores the tenant filter and returns any farm's wholeAccount. It sits on Farm's seam (IAccountRepository), which every peer module may take, so the ledger guards stay green on a new caller (mutant O5 in the #1043 Opus review).IFarmDirectorywould have login name the directory, whichFarmDirectoryCallerTestskeeps off every request path.How the guard works
.csundersrc/and finds eachIdentifierNameSyntaxnamedFindBySlugAsync. Each reference is keyed by its enclosing symbol throughGuardScanner.EnclosingSymbolOf(Replace TenantBypass line-number anchors with stable query identities #632). OnlyCluckwork.Infrastructure.Identity.IdentityProvider.ResolveFarmCodeAsync(string farmCode, CancellationToken ct)may reference it. Declarations are not references.FarmDirectoryCallerTests: the guard does not follow calls (login forwards only(Id, IsActive)), and it cannot see reflection by string name.Shared-walk fixes (review F2, F3)
ModuleLedgerScanner.ParseOptionswas a hand list. It omitted the SDK's compatibility symbolsNETCOREAPP1_0_OR_GREATER…NETCOREAPP3_1_OR_GREATER. The test csproj now embeds$(DefineConstants)as assembly metadata, from a target that runs afterAddImplicitDefineConstants.ParseOptionsreads that list and throws if it is missing.SourcePreprocessorTests.RealSourceTree_HasNoInactiveCodegoes red on A2 in Debug and Release and namesCreateProductHandler.cs:21. The review's 21-test set did not include that class. The tripwire works; the gap was the incomplete symbol list. A branch the compiler builds was reported as inactive, and the guards parsed it as dead code.GuardScanner.EnumerateSourceFilesskipped everybin/,obj/andnode_modules/directory. The SDK skips only a project's ownbin/andobj/. The walk now does the same, so a.csfile undersrc/<project>/node_modules/is walked. Nosrc/file count changes today.Change map
tests/.../TenantBypass/FindBySlugCallerTests.cstests/.../Cluckwork.Application.Tests.csprojEmbedDefineConstantstargettests/.../Architecture/ModuleLedgerScanner.csParseOptionsreads the embedded list; fails closed if it is missingtests/.../Architecture/SourcePreprocessorTests.csActiveFrameworkRegion_IsAcceptedbecomes a theory withNETCOREAPP3_1_OR_GREATERtests/.../TenantBypass/GuardScanner.csbin/objonly directly under a project directory; stop skippingnode_modulestests/.../TenantBypass/Data/tenant-bypass-allowlist.jsonFindBySlugAsyncjustificationsrc/Cluckwork.Application/Features/Accounts/IAccountRepository.cssrc/AGENTS.md,docs/decisions/843-*.md,docs/decisions/846-*.mdMutation table (real tree, each mutant reverted after its run)
CreateProductHandlercallsFindBySlugAsync("another-farm", ct)CreateProductHandler.HandleAsyncFuncCreateProductHandler.HandleAsyncAccountLookupExtensions.OtherFarmAsyncRequestServices.GetRequiredService<IAccountRepository>().FindBySlugAsync(…)in an endpoint filterReportConcurrencyLimitFilter.InvokeAsync#if NET10_0CreateProductHandler.HandleAsyncIdentityProvidermethodIdentityProvider.OtherFarmNameAsyncIdentityProvider.FindAsync#if NETCOREAPP3_1_OR_GREATERCreateProductHandler.HandleAsyncCreateProductHandler.HandleAsyncsrc/Cluckwork.Application/node_modules/, called from the handlerHiddenLookup.ReadAsyncEmbedDefineConstantstarget deletedTests (measured locally,
Cluckwork.Application.Tests)--list-tests: 703 atmain(cd1de3a8), 713 at head.SchemaDocsTestsImagePin tests pass. Not run locally: the full suite and the integration suite.