Skip to content

Security hardening#3

Merged
EmmanuelAlmonte merged 51 commits intomainfrom
security-hardening
Feb 28, 2026
Merged

Security hardening#3
EmmanuelAlmonte merged 51 commits intomainfrom
security-hardening

Conversation

@EmmanuelAlmonte
Copy link
Copy Markdown
Owner

No description provided.

- add dedicated zap request/receipt event handler and validator
- extend event kinds/tags with zap-specific constants and helper extensions
- add NIP-57 feature scenarios and implementation notes
- add whitelist controller endpoints and configuration writer service
- support owner public key and live option-monitor updates in whitelist validators
- extend whitelist documentation and default configuration fields
- move stale-subscription delay to loop tail with cancellation-safe handling
- add service namespace import required by configuration-writer wiring
- update local launch profile HTTP ports for development
- add ExemptKinds to whitelist options and enforce exemptions in validator
- extend whitelist controller and docs for exempt-kind management
- keep development appsettings local-only and untracked
- add NIP-05 verification models, validator, and verification service wiring
- expand subscription/filter matching with search and metadata-aware support
- extend NIP spec coverage files and test step definitions
- refresh generated feature snapshots and test project references
- update relay-list and whitelist validator tests for recent behavior changes
- adjust test host wiring for current auth and event handler composition
- update list matching behavior and client implementation guidance docs
- add local claude test permissions profile used by test workspace
- adjust whitelist/auth test helpers and transform scaffolding
- introduce setup docs and local appsettings template for Supabase-backed deployments
- add chess event validator wiring, event kind constants, and NIP-64 feature scenario
- update startup/config registration and auth tests for the new validator set
- introduce whitelist options, event/subscription validators, and error messaging
- wire whitelist validators into dependency registration and configuration
- add whitelist docs and unit/integration test coverage
- implement IsApplicable and CanSubscribe signatures expected by subscription handlers
- update whitelist subscription validator tests to use contract method names
- keep zap event validator disabled in DI as in source patch
- keep whitelist event validator enabled in the validation pipeline
- re-enable zap event validator registration alongside whitelist validator
- stop tracking src/Netstr/appsettings.Development.json in git
- rely on .gitignore rule so local development settings remain uncommitted
…workflow, LAN runtime scripts, and system service assets - update relay validators/settings and test-generated specs - keep appsettings.Development.json untracked
- correct auth tag matching and sender extension behavior for NIP-42 flows

- add cleanup/data-retention guidance and resiliency-related fixes
… - add connection option for HTTPS redirect toggle and apply in startup pipeline - adjust subscription matching/limit behavior in relay handlers - add NIP-42 auth reference materials for implementation debugging
…roduce follow-list event validator and register it in messaging pipeline - extend event kind model for follow-list semantics - add and refresh generated feature/spec tests including NIP-02 and NIP-77
EmmanuelAlmonte and others added 20 commits February 22, 2026 18:37
Add regressions for kind 17375 replaceable conflict handling and whitelist non-exempt blocking while wallet exempt kinds remain allowed.

Add runtime NIP-11 supported_nips assertion for NIP-60 and introduce a CI migration upgrade-downgrade-reupgrade workflow.
Copilot AI review requested due to automatic review settings February 28, 2026 01:18
@EmmanuelAlmonte EmmanuelAlmonte merged commit c37fc1c into main Feb 28, 2026
1 of 4 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR focuses on “security hardening” by tightening validation paths (AUTH, subscriptions, deletion, list/set events), adding whitelist controls, and broadening NIP support (notably NIP-05, NIP-50, NIP-57, NIP-59, NIP-64, NIP-78) with accompanying tests and configuration updates.

Changes:

  • Add configurable security controls: whitelist (publish/subscribe), AUTH created-at window, dummy subscription probe handling, and expanded delete-event validation.
  • Extend protocol coverage: NIP-05 verification plumbing, NIP-50 search semantics, NIP-57 zaps, plus additional list/set validators (NIP-59/64/78, etc.).
  • Update tests and SpecFlow scenarios to reflect new behaviors and supported NIPs.

Reviewed changes

Copilot reviewed 140 out of 298 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
test/Netstr.Tests/NIPs/Steps/RelayListSteps.cs Refactors NIP-65 steps to publish fully signed events via WS clients.
test/Netstr.Tests/NIPs/Steps/42.cs Aligns AUTH event Kind typing with long-based Kind model.
test/Netstr.Tests/NIPs/Steps/17.cs Adds SpecFlow steps for DM relay list (kind 10050) acceptance/rejection.
test/Netstr.Tests/NIPs/Steps/11.cs Sends HTTP requests to configured websocket path via ConnectionOptions.
test/Netstr.Tests/NIPs/Steps/05.cs Adds SpecFlow steps for metadata events with/without NIP-05 identifier.
test/Netstr.Tests/NIPs/Steps/01.cs Adjusts message assertion semantics and adds optional debug logging.
test/Netstr.Tests/NIPs/Nip11SupportedNipsTests.cs Adds runtime test for supported_nips advertising NIP-60.
test/Netstr.Tests/NIPs/Nip11NonRootPathTests.cs Validates NIP-11 + WS upgrade on non-root websocket path.
test/Netstr.Tests/NIPs/Helpers.cs Uses NostrJsonEncoder when generating event ids for hashing parity.
test/Netstr.Tests/NIPs/78.feature Adds SpecFlow coverage for NIP-78 set event validation via d-tag.
test/Netstr.Tests/NIPs/77.feature Adds SpecFlow coverage for NIP-77 negentropy behavior.
test/Netstr.Tests/NIPs/65.feature Updates NIP-65 scenarios to new unified publish/subscribe steps.
test/Netstr.Tests/NIPs/64.feature Adds SpecFlow coverage for NIP-64 chess content validation.
test/Netstr.Tests/NIPs/59.feature Adds SpecFlow coverage for NIP-59 kind 13 tags requirement.
test/Netstr.Tests/NIPs/50.feature Adds SpecFlow coverage for NIP-50 search scenarios.
test/Netstr.Tests/NIPs/45.feature Adjusts NIP-45 DM scenarios to avoid leaking plaintext and reduce brittleness.
test/Netstr.Tests/NIPs/17.feature Fixes author ids and adds NIP-17 kind 10050 relay-tag rules scenarios.
test/Netstr.Tests/NIPs/11.feature Extends relay info document tests (CORS headers, Accept variants).
test/Netstr.Tests/NIPs/05.feature Adds SpecFlow coverage for NIP-05 behavior (non-rejecting validation).
test/Netstr.Tests/NIPs/04.feature Updates NIP-04 DM content expectations and wildcard ids.
test/Netstr.Tests/NIPs/01.feature Adds dummy connectivity probe scenario and relaxes brittle id expectations.
test/Netstr.Tests/MultiFilterLimitSemanticsTests.cs Adds integration tests for multi-filter limit union/ranking semantics.
test/Netstr.Tests/MessageDispatcherTests.cs Updates handler DI signatures (WhitelistOptions, FiltersOptions).
test/Netstr.Tests/LimitsTests.cs Adds empty subscription id test case.
test/Netstr.Tests/Events/WhitelistValidatorTests.cs Adds unit tests for whitelist publishing validator and exempt kinds.
test/Netstr.Tests/Events/SealEventValidatorTests.cs Adds unit tests for kind 13 tags rule.
test/Netstr.Tests/Events/ListEventValidatorTests.cs Expands list/set validators test coverage (NIP-78, kind 10050 relay tags).
test/Netstr.Tests/Events/FilterEventMatchingTests.cs Updates SubscriptionFilter ctor usage to include Search param.
test/Netstr.Tests/Events/EventVerificationTests.cs Registers stub INip05VerificationService required by new Nip05Validator.
test/Netstr.Tests/Events/DbFilterEventMatchingTests.cs Updates DB filter extension method name; adds wallet/nutzap filter test.
test/Netstr.Tests/Events/ClientContextTests.cs Adds unit tests for multi-pubkey authentication tracking.
test/Netstr.Tests/Events/AuthCreatedAtValidatorTests.cs Adds unit tests for AUTH created_at window enforcement.
test/Netstr.Tests/CountSemanticsTests.cs Adds integration tests for COUNT semantics across filters and limits.
test/Netstr.Tests/ConfigurationExtensions.cs Improves test config flattening for bool and collections.
test/Netstr.Tests/Bob.cs Adds deterministic test keypair constants.
test/Netstr.Tests/AuthTests.cs Expands AUTH tests and aligns kind typing with long.
src/Netstr/appsettings.local.json.example Adds local secrets template for DB connection string.
src/Netstr/appsettings.json Adds whitelist/auth/search/https settings; expands supported NIPs list.
src/Netstr/appsettings.example.json Updates example config (filters, search limits, whitelist, DB conn format).
src/Netstr/Views/Home/Index.cshtml Updates logo URL to new repository location.
src/Netstr/Services/Nip05VerificationService.cs Implements NIP-05 verification service with caching and HTTP fetch.
src/Netstr/Services/ConfigurationWriter.cs Adds service to update config sections on disk.
src/Netstr/RelayInformation/RelayInformationDefaults.cs Updates software URL to new repository location.
src/Netstr/Properties/launchSettings.json Updates local listen addresses/ports for dev profiles.
src/Netstr/Program.cs Loads appsettings.local.json; conditionally applies HTTPS redirection; adds DB retry options.
src/Netstr/Options/WhitelistOptions.cs Adds whitelist feature options.
src/Netstr/Options/LimitsOptions.cs Adds Search limits to LimitsOptions.
src/Netstr/Options/Limits/SearchLimits.cs Adds NIP-50 search limits/options model.
src/Netstr/Options/FiltersOptions.cs Adds feature flag for non-standard AND-tag filters.
src/Netstr/Options/ConnectionOptions.cs Adds UseHttpsRedirection option.
src/Netstr/Options/AuthOptions.cs Adds AuthCreatedAtWindowSeconds setting.
src/Netstr/Middleware/WebSocketsMiddleware.cs Serves NIP-11 metadata at websocket path and upgrades WS; adds Accept parsing & CORS headers.
src/Netstr/Middleware/NegentropyBackgroundWatcher.cs Improves cancellation behavior in background polling loop.
src/Netstr/Messaging/WebSockets/WebSocketAdapter.cs Reduces allocations in ReceiveAsync by reusing a single buffer.
src/Netstr/Messaging/UserCache.cs Tracks vanish-deleted event ids to reject republishing deleted events.
src/Netstr/Messaging/Subscriptions/Validators/WhitelistSubscriptionValidator.cs Enforces whitelist on subscriptions when enabled.
src/Netstr/Messaging/Subscriptions/Validators/SubscriptionLimitsValidator.cs Rejects empty subscription ids explicitly.
src/Netstr/Messaging/Subscriptions/SubscriptionsAdapterFactory.cs Passes LimitsOptions to SubscriptionsAdapter for queue sizing.
src/Netstr/Messaging/Subscriptions/SubscriptionsAdapter.cs Uses LimitsOptions to size per-subscription queue.
src/Netstr/Messaging/Subscriptions/SubscriptionFilterMatcher.cs Adds NIP-50 search term matching and safer tag matching length checks.
src/Netstr/Messaging/Subscriptions/SubscriptionAdapter.cs Replaces ConcurrentQueue with bounded Channel to limit memory and drop oldest.
src/Netstr/Messaging/Subscriptions/SearchQueryParser.cs Parses NIP-50 query into basic terms + extensions without reducing recall.
src/Netstr/Messaging/Subscriptions/SearchMatcher.cs Implements in-memory search matching and ignores unsupported extensions.
src/Netstr/Messaging/SenderExtensions.cs Adds SendEose helper.
src/Netstr/Messaging/Models/ZapEventExtensions.cs Adds helpers for extracting NIP-57 zap related tags.
src/Netstr/Messaging/Models/UserMetadata.cs Adds metadata model for kind 0 content parsing.
src/Netstr/Messaging/Models/SubscriptionFilter.cs Adds Search property to filter request and filter model.
src/Netstr/Messaging/Models/Nip05/Nip05Result.cs Adds NIP-05 verification result model.
src/Netstr/Messaging/Models/Nip05/Nip05Response.cs Adds NIP-05 nostr.json response model.
src/Netstr/Messaging/Models/EventTag.cs Adds tag constants for zaps and AUTH relay tag, etc.
src/Netstr/Messaging/Models/EventKind.cs Expands enum with additional NIP kinds (zaps, wallet, chess, app data, etc.).
src/Netstr/Messaging/Models/Event.cs Updates event type classification + relay normalization helpers.
src/Netstr/Messaging/Models/ClientContext.cs Supports multi-pubkey AUTH and exposes authenticated key set.
src/Netstr/Messaging/Messages.cs Adds new error strings for subscription id, whitelist, probe ignore, DB errors, etc.
src/Netstr/Messaging/MessageHandlers/SubscribeMessageHandler.cs Adds dummy probe detection + uses AuthenticatedPublicKeys for filtering.
src/Netstr/Messaging/MessageHandlers/Negentropy/NegentropyOpenHandler.cs Allows multiple filters in NEG-OPEN and uses AuthenticatedPublicKeys.
src/Netstr/Messaging/MessageHandlers/EventMessageHandler.cs Adds per-pubkey rate limit exemptions.
src/Netstr/Messaging/MessageHandlers/CountMessageHandler.cs Fixes COUNT semantics: distinct event ids over ORed filters.
src/Netstr/Messaging/MessageHandlers/AuthMessageHandler.cs Normalizes/relaxes relay tag matching (port/trailing slash tolerance).
src/Netstr/Messaging/Events/Validators/ZapEventValidator.cs Adds validation rules for zap requests/receipts.
src/Netstr/Messaging/Events/Validators/WhitelistValidator.cs Adds whitelist publishing validator with exempt kinds.
src/Netstr/Messaging/Events/Validators/UserVanishedValidator.cs Rejects events deleted via vanish tracking.
src/Netstr/Messaging/Events/Validators/SealEventValidator.cs Enforces kind 13 tags-empty requirement (NIP-59).
src/Netstr/Messaging/Events/Validators/RelayListValidator.cs Fixes kind comparison against long-valued event kind.
src/Netstr/Messaging/Events/Validators/RelayListEventValidator.cs Fixes kind comparison against long-valued event kind.
src/Netstr/Messaging/Events/Validators/ProtectedEventValidator.cs Uses multi-pubkey context authentication check.
src/Netstr/Messaging/Events/Validators/Nip05Validator.cs Adds async NIP-05 validation/monitoring for metadata events.
src/Netstr/Messaging/Events/Validators/Nip04DirectMessageValidator.cs Validates NIP-04 DM format & recipient tag.
src/Netstr/Messaging/Events/Validators/ListEventValidator.cs Tightens set-kind whitelist and adds kind 10050 relay tag requirement.
src/Netstr/Messaging/Events/Validators/FollowListValidator.cs Adds NIP-02 follow list validation.
src/Netstr/Messaging/Events/Validators/ChessEventValidator.cs Adds NIP-64 chess/PGN validation.
src/Netstr/Messaging/Events/Validators/AuthCreatedAtValidator.cs Enforces AUTH-specific created_at window.
src/Netstr/Messaging/Events/Handlers/ZapEventHandler.cs Handles zap receipts (stores/broadcasts) and rejects zap requests to relay.
src/Netstr/Messaging/Events/Handlers/VanishEventHandler.cs Uses execution strategy + tracks deleted ids for vanish; improves relay tag normalization.
src/Netstr/Messaging/Events/Handlers/TestRelayListEventHandler.cs Cleans up handler implementation details and returns completed task.
src/Netstr/Messaging/Events/Handlers/Replaceable/ReplaceableEventHandlerBase.cs Adds same-timestamp tie-break behavior for replaceables.
src/Netstr/Messaging/Events/Handlers/RegularEventHandler.cs Adds DB save timing logs and warning thresholds.
src/Netstr/Messaging/Events/Handlers/EventHandlerBase.cs Adds robust DB error handling returning standardized errors.
src/Netstr/Messaging/Events/Handlers/DeleteEventHandler.cs Harden delete validation: malformed refs, cashu kind marker requirements, execution strategy.
src/Netstr/Messaging/Events/DbExtensions.cs Adds NIP-50 DB-side search filtering + ranking ordering helpers.
src/Netstr/Messaging/Events/CleanupService.cs Adds execution strategy + detailed structured cleanup metrics.
src/Netstr/Extensions/OptionsExtensions.cs Registers new Filters + Whitelist options.
src/Netstr/Extensions/MessagingExtensions.cs Registers NIP-05 HttpClient (no redirects), new validators, new zap handler.
src/Netstr/Extensions/HttpExtensions.cs Adds relay URL normalization utility.
src/Netstr/Data/Migrations/NetstrDbContextModelSnapshot.cs Adds EventJson column to model snapshot.
src/Netstr/Data/Migrations/20260221020205_LocalModelSync.cs Adds migration introducing EventJson column.
src/Netstr/Data/EventEntity.cs Adds EventJson column property.
src/Netstr/Data/EntityMapping.cs Serializes EventJson and deduplicates tags during entity mapping.
src/Netstr/Data/DbUpdateExceptionExtensions.cs Adds TagValueIndexName to unique index violation detection list.
src/Netstr/Controllers/HomeController.cs Removes NIP-11 Accept header handling from MVC endpoint (moved to middleware).
scripts/probe-relay.ps1 Adds WS probe script for REQ/COUNT flows.
scripts/check-no-connection-secrets.ps1 Adds guard script to prevent hardcoded DB passwords in appsettings.
docs/test-stabilization-baseline.md Adds test stabilization baseline notes.
docs/nip-validation-2026-02-16.md Adds NIP conformance audit notes.
docker-compose.yml Adds compose definition for running relay with env-provided secrets/certs.
SETUP.md Adds setup guide emphasizing local secrets file and env vars.
README.md Updates badges, adds NIP support list, documents whitelist, updates setup instructions.
NIP57Zaps.md Documents NIP-57 implementation details.
DATABASE_RETENTION.md Documents data retention and cleanup policies.
CLAUDE.md Adds repo guidance for Claude Code tooling.
AGENTS.md Adds repo-level engineering guidelines and commands.
.github/workflows/secret-guard.yml Adds CI step to detect hardcoded DB passwords.
.github/workflows/migration-safety.yml Adds CI migration upgrade/downgrade/upgrade safety check.
.github/workflows/deploy.yml Adds a CI workflow skeleton for test + deploy.
.env.example Adds example env vars for Docker deployment.
Files not reviewed (23)
  • src/Netstr/Data/Migrations/20260221020205_LocalModelSync.Designer.cs: Language not supported
  • test/Netstr.Tests/NIPs/01.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/02.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/04.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/05.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/09.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/11.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/119.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/13.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/17.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/40.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/42.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/45.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/50.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/51.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/57.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/59.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/62.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/64.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/65.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/70.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/77.feature.cs: Language not supported
  • test/Netstr.Tests/NIPs/78.feature.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +71 to +74
if (_cache.TryGetValue(cacheKey, out Nip05CacheEntry? cached) && cached?.Response != null)
{
_logger.LogDebug($"NIP-05 cache hit for {identifier}");
return ValidateResponse(cached.Response, user, pubkey);
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failed-result caching is currently ineffective: CacheFailedResult stores entries with Response = null, but the cache lookup ignores those entries because it checks cached?.Response != null. This means repeated failures will still trigger network calls. Consider treating a cached entry with null Response as a negative cache hit and returning a deterministic Invalid result (or storing a separate sentinel/status) for FAILED_CACHE_DURATION.

Suggested change
if (_cache.TryGetValue(cacheKey, out Nip05CacheEntry? cached) && cached?.Response != null)
{
_logger.LogDebug($"NIP-05 cache hit for {identifier}");
return ValidateResponse(cached.Response, user, pubkey);
if (_cache.TryGetValue(cacheKey, out Nip05CacheEntry? cached) && cached != null)
{
if (cached.Response != null)
{
_logger.LogDebug($"NIP-05 cache hit for {identifier}");
return ValidateResponse(cached.Response, user, pubkey);
}
// Cached failed result (negative cache hit) - avoid repeated network calls
_logger.LogDebug($"NIP-05 failed cache hit for {identifier}");
return Nip05Result.Invalid("Previous NIP-05 verification failed");

Copilot uses AI. Check for mistakes.
Comment on lines +54 to +79
// Parse identifier (user@domain.com or _@domain.com)
var parts = identifier.Split('@');
if (parts.Length != 2)
{
return Nip05Result.Invalid("Invalid identifier format - must be user@domain");
}

var (user, domain) = (parts[0], parts[1]);

// Validate domain format
if (string.IsNullOrWhiteSpace(domain) || domain.Contains(' '))
{
return Nip05Result.Invalid("Invalid domain format");
}

// Check cache first
var cacheKey = $"{CACHE_KEY_PREFIX}:{domain}:{user}";
if (_cache.TryGetValue(cacheKey, out Nip05CacheEntry? cached) && cached?.Response != null)
{
_logger.LogDebug($"NIP-05 cache hit for {identifier}");
return ValidateResponse(cached.Response, user, pubkey);
}

// Fetch .well-known/nostr.json
var url = $"https://{domain}/.well-known/nostr.json?name={user}";
_logger.LogDebug($"Fetching NIP-05 verification from {url}");
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This builds the fetch URL from user-controlled identifier parts with minimal validation. Two concrete risks: (1) SSRF (e.g., domains like localhost, IP literals, or internal hosts) and (2) query injection / invalid URLs because user is not URL-encoded. Consider constructing the URI via UriBuilder, URL-encoding the name parameter (Uri.EscapeDataString), and validating the host (e.g., Uri.CheckHostName + blocking IP literals/localhost/private ranges) before issuing the request.

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +36
// NIP-05 validation is async, so we'll do it in a background task
// to avoid blocking event processing
_ = Task.Run(async () => await ValidateNip05Async(e));

// Never reject events based on NIP-05 validation
// This is for verification tracking only
return null; // Success - always allow events to pass through
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Task.Run here can create unbounded thread-pool work under load (every kind-0 event schedules a new work item). Since ValidateNip05Async is already async and internally catches exceptions, consider invoking it without Task.Run (e.g., fire-and-forget the returned Task) or routing it through a bounded background queue to prevent runaway concurrency.

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +32
Tags = e.Tags
.GroupBy(x => new { Name = x.First(), Value = x.Skip(1).FirstOrDefault() })
.Select(g => new TagEntity
{
Name = g.Key.Name,
Value = g.Key.Value,
OtherValues = g.First().Skip(2).ToArray()
})
.ToArray(),
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grouping tags by only the first two fields (name/value) drops information when the same name/value repeats with different additional fields (e.g., relay URL + marker, multiple 'e' references with additional metadata). This changes persisted event semantics and can cause data loss. If the goal is to avoid duplicate-row violations, consider deduplicating only exact tag sequences (all fields), or adjust the DB uniqueness/index model so tags with distinct OtherValues can coexist.

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +16
public static string GetNormalizedUrl(this HttpRequest ctx)
{
return NormalizeRelay(ctx.Host.ToString());
}

private static string NormalizeRelay(string? relayUrl)
{
return NormalizeRelayUrl(relayUrl, removePort: true);
}
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetNormalizedUrl now normalizes using only ctx.Host and always strips the port (removePort: true). This is a behavior change from the previous host+path normalization and can break validations that require matching the actual relay endpoint (especially when Connection:WebSocketsPath is non-root and/or when the client includes ports in relay tags). Consider preserving the websocket path (and supporting both port/no-port variants) when comparing relay tags for AUTH/vanish flows.

Copilot uses AI. Check for mistakes.
Comment on lines 37 to 48
var path = ctx.GetNormalizedUrl();
var relays = e.GetNormalizedRelayValues();
var relays = e.GetTagValues(EventTag.Relay)
.Concat(e.GetTagValues(EventTag.AuthRelay))
.Select(x => HttpExtensions.NormalizeRelayUrl(x))
.Distinct();

// check 'relay' tag matches current url or is set to ALL_RELAYS
if (!relays.Any(x => x == path || x == AllRelaysValue))
{
throw new EventProcessingException(e, string.Format(Messages.InvalidWrongTagValue, EventTag.Relay));
sender.SendNotOk(e.Id, string.Format(Messages.InvalidWrongTagValue, EventTag.AuthRelay));
return;
}
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comparison is inconsistent with the new normalization rules: path comes from GetNormalizedUrl (currently host-only with port removed), but relay tag normalization keeps ports by default (NormalizeRelayUrl removePort defaults to false). As a result, valid relay tags that include a port (or a non-root path) can be rejected and/or incorrect tags can be accepted. Consider normalizing both sides with the same rules and/or accepting both removePort true/false variants (similar to AuthMessageHandler).

Copilot uses AI. Check for mistakes.
Comment on lines +93 to 100
"Whitelist": {
"Enabled": true,
"AllowedPublicKeys": [],
"RestrictPublishing": true,
"RestrictSubscribing": false,
"OwnerPublicKey": "",
"ExemptKinds": [ 375, 9735, 17375 ]
}
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enabling the whitelist by default with an empty AllowedPublicKeys list will block all publishing (except ExemptKinds) out-of-the-box. That’s a high-risk default because it can effectively lock operators out immediately after deploy. Consider defaulting Whitelist.Enabled to false in appsettings.json/example files, or requiring OwnerPublicKey/AllowedPublicKeys to be set when Enabled=true (fail fast on startup with a clear error).

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +76
this.logger.LogInformation($"Found {entities.Length} stored events for subscription {subscriptionId}");
if (entities.Length > 0)
{
this.logger.LogInformation($"First event: {entities[0].EventId}, Kind: {entities[0].EventKind}");
}
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These logs were added at Information level and include event ids/kinds for every subscription, which can be noisy and potentially sensitive on production relays (especially for high-volume subscriptions). Consider downgrading to Debug/Trace, adding sampling, or removing the per-subscription 'First event' log line.

Suggested change
this.logger.LogInformation($"Found {entities.Length} stored events for subscription {subscriptionId}");
if (entities.Length > 0)
{
this.logger.LogInformation($"First event: {entities[0].EventId}, Kind: {entities[0].EventKind}");
}
this.logger.LogDebug($"Found {entities.Length} stored events for subscription {subscriptionId}");

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +105
private object GetValue(JsonElement element)
{
return element.ValueKind switch
{
JsonValueKind.String => element.GetString() ?? string.Empty,
JsonValueKind.Number => element.TryGetInt64(out long l) ? l : element.GetDouble(),
JsonValueKind.True => true,
JsonValueKind.False => false,
JsonValueKind.Null => null,
_ => element.ToString()
};
}
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetValue returns null for JsonValueKind.Null but the method return type is object (non-nullable). With nullable enabled, this will produce warnings and makes downstream typing ambiguous. Consider changing the signature to object? (and similarly updating Dictionary value types as needed) or mapping nulls to a sentinel type explicitly.

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +52
public async Task<Nip05Result> VerifyIdentifierAsync(string identifier, string pubkey)
{
try
{
if (string.IsNullOrWhiteSpace(identifier) || string.IsNullOrWhiteSpace(pubkey))
{
return Nip05Result.Invalid("Invalid identifier or pubkey");
}
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nip05VerificationService introduces several important security/behavior branches (domain parsing/validation, no-redirect behavior, caching of success/failure, and error handling for HTTP/timeout/JSON). Given the repo has broad automated test coverage, this service likely needs dedicated unit tests using a stubbed HttpMessageHandler to assert: URL encoding, SSRF/host rejection logic (if added), cache hit behavior (including negative-cache), and correct Invalid results on exceptions.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants