mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-04 02:31:36 +00:00
fix(csharp): keep BCL/external roots gated through scan truncation (#1908, Codex F1)
A single scan truncation (unreadable dir/file, depth/dir cap) set one repo-wide `truncated` flag that made csharpSuffixFallbackAllowed fail open for EVERY import, silently re-enabling the #1881 BCL->local suffix matches. Add a CSHARP_EXTERNAL_ROOTS denylist (System/Microsoft/...): an external-rooted using that does not align with an in-repo declared namespace stays BLOCKED even under truncation, while genuinely local-looking usings still fail open. A repo that declares the root is allowed via the alignment escape hatch. Shared predicate, so both legs inherit it.
This commit is contained in:
parent
61c0150a62
commit
dbfab7d4f8
2 changed files with 91 additions and 7 deletions
|
|
@ -11,20 +11,80 @@
|
|||
|
||||
import type { CSharpNamespaceEvidence } from './language-config.js';
|
||||
|
||||
/**
|
||||
* Top-level namespace segments that clearly belong to the BCL / runtime / a
|
||||
* ubiquitous third-party package — i.e. roots a normal repo does NOT declare.
|
||||
* These stay gated even when the namespace scan is truncated, so a single
|
||||
* unreadable file / capped subtree can't silently re-enable BCL→local suffix
|
||||
* matches repo-wide (#1881). A repo that legitimately declares one of these
|
||||
* roots is still allowed via the alignment escape hatch below.
|
||||
*/
|
||||
const CSHARP_EXTERNAL_ROOTS: ReadonlySet<string> = new Set([
|
||||
// .NET BCL / runtime
|
||||
'System',
|
||||
'Microsoft',
|
||||
'Windows',
|
||||
'Mono',
|
||||
// ubiquitous third-party NuGet roots
|
||||
'Newtonsoft',
|
||||
'Serilog',
|
||||
'AutoMapper',
|
||||
'MediatR',
|
||||
'Polly',
|
||||
'FluentValidation',
|
||||
'Grpc',
|
||||
'Google',
|
||||
'Azure',
|
||||
'Amazon',
|
||||
'AWSSDK',
|
||||
// common test frameworks
|
||||
'Xunit',
|
||||
'NUnit',
|
||||
'Moq',
|
||||
'FluentAssertions',
|
||||
'NSubstitute',
|
||||
'Shouldly',
|
||||
]);
|
||||
|
||||
/** Whether `targetRaw`'s top-level segment is a clearly-external root. */
|
||||
function isExternalRoot(targetRaw: string): boolean {
|
||||
const dot = targetRaw.indexOf('.');
|
||||
const top = dot === -1 ? targetRaw : targetRaw.slice(0, dot);
|
||||
return CSHARP_EXTERNAL_ROOTS.has(top);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the unanchored suffix fallback may run for `targetRaw`.
|
||||
*
|
||||
* Fails OPEN when the namespace scan was truncated (large repos must not
|
||||
* silently lose legitimate edges, #1881 #11) and when no evidence was
|
||||
* threaded at all (preserves legacy permissive behavior). Otherwise defers
|
||||
* to {@link importAlignsWithDeclaredNamespaces}.
|
||||
* threaded at all (preserves legacy permissive behavior). The truncation
|
||||
* fail-open is carved out for clearly-external roots (BCL / well-known
|
||||
* packages) that the repo does not declare, so one incomplete scan can't
|
||||
* re-open the #1881 hole repo-wide. Otherwise defers to
|
||||
* {@link importAlignsWithDeclaredNamespaces}.
|
||||
*/
|
||||
export function csharpSuffixFallbackAllowed(
|
||||
targetRaw: string,
|
||||
evidence: CSharpNamespaceEvidence | undefined,
|
||||
): boolean {
|
||||
if (evidence === undefined) return true;
|
||||
if (evidence.truncated) return true;
|
||||
if (evidence.truncated) {
|
||||
// Keep clearly-external roots blocked through truncation UNLESS the repo
|
||||
// actually declares an aligning namespace (the alignment check is the
|
||||
// escape hatch — a repo that declares `namespace System;` still resolves).
|
||||
if (
|
||||
isExternalRoot(targetRaw) &&
|
||||
!importAlignsWithDeclaredNamespaces(
|
||||
targetRaw,
|
||||
evidence.declaredNamespaces,
|
||||
evidence.rootNamespaces,
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return importAlignsWithDeclaredNamespaces(
|
||||
targetRaw,
|
||||
evidence.declaredNamespaces,
|
||||
|
|
|
|||
|
|
@ -467,16 +467,40 @@ describe('csharpSuffixFallbackAllowed — fail-open safety valves (#1881)', () =
|
|||
expect(csharpSuffixFallbackAllowed('System.Threading.Tasks', undefined)).toBe(true);
|
||||
});
|
||||
|
||||
it('fails OPEN (allows) when the namespace scan was truncated (#6)', () => {
|
||||
// Same non-aligned import again: an incomplete (capped/unreadable) scan
|
||||
// must not silently drop a legitimate edge, so truncation re-opens the
|
||||
// fallback for every import.
|
||||
it('keeps a clearly-external BCL root BLOCKED even when the scan was truncated (#1881, Codex F1)', () => {
|
||||
// A single truncation must NOT silently re-enable BCL→local suffix matches
|
||||
// repo-wide: System.* stays gated through truncation when the repo does not
|
||||
// declare it. (This reverses the prior blanket-fail-open for external roots.)
|
||||
expect(
|
||||
csharpSuffixFallbackAllowed('System.Threading.Tasks', {
|
||||
declaredNamespaces: declared,
|
||||
rootNamespaces: roots,
|
||||
truncated: true,
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('fails OPEN for a genuinely local-looking import when the scan was truncated (#6)', () => {
|
||||
// Non-external roots still fail open under truncation so an incomplete
|
||||
// (capped/unreadable) scan does not silently drop a legitimate in-repo edge.
|
||||
expect(
|
||||
csharpSuffixFallbackAllowed('MyApp.Internal.Widget', {
|
||||
declaredNamespaces: declared,
|
||||
rootNamespaces: roots,
|
||||
truncated: true,
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('lets an external root fail OPEN through truncation when the repo declares it (escape hatch)', () => {
|
||||
// If the repo actually declares the (normally-external) root, the alignment
|
||||
// escape hatch allows the import even under truncation.
|
||||
expect(
|
||||
csharpSuffixFallbackAllowed('System.Threading.Tasks', {
|
||||
declaredNamespaces: new Set(['System.Threading']),
|
||||
rootNamespaces: new Set(['System']),
|
||||
truncated: true,
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue