diff --git a/.jules/sentinel.md b/.jules/sentinel.md index f5cf3bd..68efa39 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -2,3 +2,8 @@ **Vulnerability:** Source Generator `NTypeForge.SourceGenerator` was always emitting `public static class` for its duck-typing extensions. If the type being ducked (`target`) was `internal`, this created a `CS0051: Inconsistent accessibility` compiler error and essentially violated the principle of least privilege, potentially leaking internal types into public scope. **Learning:** Source Generators creating types meant to be used alongside user code should dynamically match the accessibility of their targets (or default to `internal` if appropriate for their use-case) to prevent scope leakage. **Prevention:** Check `DeclaredAccessibility == Accessibility.Public` (or effective accessibility for nested types) on the target type, and emit `internal static class` instead of `public static class` when the target is not public. + +## 2026-07-08 - [Fail Securely] Validate public API inputs explicitly +**Vulnerability:** The public API `Duck(this object instance)` did not validate its `instance` input for null. If null was passed, it would bypass the type check and throw a misleading `InvalidOperationException` intended for missing generator proxies. This obscures root causes and could potentially lead to unexpected application state. +**Learning:** Relying on downstream operations (or subsequent logic) to throw generic exceptions on null input is unsafe and confusing. Always validate input immediately at the entry point of a public API. +**Prevention:** Use `ArgumentNullException.ThrowIfNull()` at the very start of public-facing API methods to fail fast, securely, and with accurate error information. diff --git a/src/NTypeForge/DuckExtensions.cs b/src/NTypeForge/DuckExtensions.cs index 8909539..b0bb973 100644 --- a/src/NTypeForge/DuckExtensions.cs +++ b/src/NTypeForge/DuckExtensions.cs @@ -65,6 +65,8 @@ public static bool TryUnbox(this object? proxy, out T value) /// public static T Duck(this object instance) where T : class { + ArgumentNullException.ThrowIfNull(instance); + if (instance is T t) return t; throw new InvalidOperationException("NTypeForge: Duck was called but no proxy was generated. Ensure the NTypeForge source generator is running and the target type matches the interface.");