fix(nip05): add configurable limits for relays per user and identity listing
Some checks failed
Docker Compose Actions Workflow / test (push) Has been cancelled

Adds max_relays_per_user (default: 10) to prevent users from attaching
excessive relay URLs that inflate .well-known/nostr.json responses.
Enforced in both claimUsername and updateRelays.

Reduces the no-name listing limit from hardcoded 1000 to configurable
max_identities_listing (default: 100) for the /.well-known/nostr.json
endpoint when no ?name= parameter is provided.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-04-01 13:31:33 -04:00
parent 915ca667e5
commit f59073e589
2 changed files with 20 additions and 7 deletions

View file

@ -27,7 +27,9 @@ const DEFAULT_CONFIG: Required<Nip05Config> = {
min_username_length: 1, min_username_length: 1,
reserved_usernames: ['admin', 'root', 'system', 'support', 'help', 'info', 'contact', 'abuse', 'postmaster', 'webmaster', 'hostmaster', 'noreply', 'no-reply', 'null', 'undefined', 'api', 'www', 'mail', 'ftp', 'ssh', 'test', 'demo'], reserved_usernames: ['admin', 'root', 'system', 'support', 'help', 'info', 'contact', 'abuse', 'postmaster', 'webmaster', 'hostmaster', 'noreply', 'no-reply', 'null', 'undefined', 'api', 'www', 'mail', 'ftp', 'ssh', 'test', 'demo'],
include_relays: true, include_relays: true,
default_relays: [] default_relays: [],
max_relays_per_user: 10,
max_identities_listing: 100
} }
/** /**
@ -99,11 +101,15 @@ function validateUsername(username: string, config: Required<Nip05Config>): User
/** /**
* Validate relay URLs * Validate relay URLs
*/ */
function validateRelays(relays: string[]): UsernameValidation { function validateRelays(relays: string[], maxRelays?: number): UsernameValidation {
if (!Array.isArray(relays)) { if (!Array.isArray(relays)) {
return { valid: false, error: 'Relays must be an array' } return { valid: false, error: 'Relays must be an array' }
} }
if (maxRelays && relays.length > maxRelays) {
return { valid: false, error: `Too many relays (max ${maxRelays})` }
}
for (const relay of relays) { for (const relay of relays) {
if (typeof relay !== 'string') { if (typeof relay !== 'string') {
return { valid: false, error: 'Each relay must be a string' } return { valid: false, error: 'Each relay must be a string' }
@ -166,7 +172,7 @@ export class Nip05Manager {
// Validate relays if provided // Validate relays if provided
const relays = request.relays || this.config.default_relays const relays = request.relays || this.config.default_relays
if (relays.length > 0) { if (relays.length > 0) {
const relayValidation = validateRelays(relays) const relayValidation = validateRelays(relays, this.config.max_relays_per_user)
if (!relayValidation.valid) { if (!relayValidation.valid) {
throw new Error(relayValidation.error) throw new Error(relayValidation.error)
} }
@ -241,7 +247,7 @@ export class Nip05Manager {
request: UpdateRelaysRequest request: UpdateRelaysRequest
): Promise<Nip05Identity> { ): Promise<Nip05Identity> {
// Validate relays // Validate relays
const validation = validateRelays(request.relays) const validation = validateRelays(request.relays, this.config.max_relays_per_user)
if (!validation.valid) { if (!validation.valid) {
throw new Error(validation.error) throw new Error(validation.error)
} }
@ -361,10 +367,11 @@ export class Nip05Manager {
} }
} }
} else { } else {
// Return all active identities (with reasonable limit) // Return all active identities (with configurable limit)
const limit = this.config.max_identities_listing
const rows = await this.db.query<Nip05IdentityRow>( const rows = await this.db.query<Nip05IdentityRow>(
`SELECT * FROM identities WHERE application_id = ? AND is_active = 1 LIMIT 1000`, `SELECT * FROM identities WHERE application_id = ? AND is_active = 1 LIMIT ?`,
[applicationId] [applicationId, limit]
) )
for (const row of rows) { for (const row of rows) {

View file

@ -119,6 +119,12 @@ export interface Nip05Config {
/** Default relays to suggest for new users */ /** Default relays to suggest for new users */
default_relays?: string[] default_relays?: string[]
/** Maximum number of relays per user (default: 10) */
max_relays_per_user?: number
/** Maximum number of identities returned when no name query param is provided (default: 100) */
max_identities_listing?: number
} }
/** /**