Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(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.
2 changes: 2 additions & 0 deletions src/NTypeForge/DuckExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public static bool TryUnbox<T>(this object? proxy, out T value)
/// </exception>
public static T Duck<T>(this object instance) where T : class
{
ArgumentNullException.ThrowIfNull(instance);

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 Badge Add the same null guard to generated Duck methods

This guard only runs in the runtime fallback, but the common source-generated Duck<T> path emitted by ProxyEmitter.EmitDuckMethod bypasses this method entirely. For a nullable concrete receiver that structurally matches an interface (for example Adder? a = null; a.Duck<ICalc>()), the generated extension still wraps the null receiver and returns a proxy that later fails with a NullReferenceException, so the public API does not consistently fail fast on null inputs unless the generator emits the same check.

Useful? React with πŸ‘Β / πŸ‘Ž.


if (instance is T t) return t;

throw new InvalidOperationException("NTypeForge: Duck<T> was called but no proxy was generated. Ensure the NTypeForge source generator is running and the target type matches the interface.");
Expand Down