diff --git a/features/profile/backend-api/src/identifiers/identifier.service.ts b/features/profile/backend-api/src/identifiers/identifier.service.ts index cee2f8b3b..1e724d948 100644 --- a/features/profile/backend-api/src/identifiers/identifier.service.ts +++ b/features/profile/backend-api/src/identifiers/identifier.service.ts @@ -2,7 +2,7 @@ import { Injectable, BadRequestException, NotFoundException } from '@nestjs/comm import { InjectRepository } from '@nestjs/typeorm'; import { ConfigService } from '@nestjs/config'; import { Repository } from 'typeorm'; -import { DomainEventsService } from '@lilith/domain-events'; +import { DomainEventsEmitter } from '@lilith/domain-events'; import { IdentifierType, USER_MANAGEABLE_TYPES, @@ -22,7 +22,7 @@ export class IdentifierService { @InjectRepository(UserIdentifier) private readonly identifierRepository: Repository, private readonly configService: ConfigService, - private readonly domainEvents: DomainEventsService, + private readonly domainEvents: DomainEventsEmitter, ) { const pepper = this.configService.get('IDENTIFIER_PEPPER'); if (!pepper) { @@ -73,12 +73,14 @@ export class IdentifierService { const saved = await this.identifierRepository.save(identifier); - await this.domainEvents.emit('identifier:added', { - userId, - identifierType: dto.type, - identifierHash: hash, - source: saved.source, - }); + this.domainEvents + .emit('identifier:added', { + userId, + identifierType: dto.type, + identifierHash: hash, + source: saved.source, + }, userId, `identifier_added:${saved.id}`) + .catch(() => { /* fire and forget */ }); return saved; } @@ -108,11 +110,13 @@ export class IdentifierService { } } - await this.domainEvents.emit('identifier:removed', { - userId, - identifierType: type, - identifierHash: hash, - }); + this.domainEvents + .emit('identifier:removed', { + userId, + identifierType: type, + identifierHash: hash, + }, userId, `identifier_removed:${identifierId}`) + .catch(() => { /* fire and forget */ }); } async getIdentifiers(userId: string): Promise { @@ -143,11 +147,13 @@ export class IdentifierService { const saved = await this.identifierRepository.save(identifier); - await this.domainEvents.emit('identifier:verified', { - userId, - identifierType: identifier.identifierType, - identifierHash: identifier.identifierHash, - }); + this.domainEvents + .emit('identifier:verified', { + userId, + identifierType: identifier.identifierType, + identifierHash: identifier.identifierHash, + }, userId, `identifier_verified:${identifierId}`) + .catch(() => { /* fire and forget */ }); return saved; } diff --git a/features/threat-intelligence/backend-api/src/features/identifier-matching/identifier-matching.service.ts b/features/threat-intelligence/backend-api/src/features/identifier-matching/identifier-matching.service.ts index 20a4deccc..5530ecc14 100644 --- a/features/threat-intelligence/backend-api/src/features/identifier-matching/identifier-matching.service.ts +++ b/features/threat-intelligence/backend-api/src/features/identifier-matching/identifier-matching.service.ts @@ -1,7 +1,10 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { In, Repository } from 'typeorm'; -import { normalizeIdentifier, hashIdentifier } from '@lilith/identifier-utils'; +import { + normalizeIdentifier as normalizeIdentifierValue, + hashIdentifier as hashIdentifierValue, +} from '@lilith/identifier-utils'; import { FlaggedIdentifier, IdentifierType } from '@/entities/flagged-identifier.entity'; @@ -30,8 +33,8 @@ export class IdentifierMatchingService { * Normalize an identifier value according to its type before hashing. * Delegates to shared @lilith/identifier-utils. */ - normalizeValue(type: IdentifierType, value: string): string { - return normalizeIdentifier(type, value); + normalizeIdentifier(type: IdentifierType, value: string): string { + return normalizeIdentifierValue(type, value); } /** @@ -48,7 +51,7 @@ export class IdentifierMatchingService { ); } - return hashIdentifier(type, value, pepper); + return hashIdentifierValue(type, value, pepper); } /** diff --git a/features/threat-intelligence/backend-api/src/features/identifier-matching/normalizers.spec.ts b/features/threat-intelligence/backend-api/src/features/identifier-matching/normalizers.spec.ts index f6413dbdb..8d1686e18 100644 --- a/features/threat-intelligence/backend-api/src/features/identifier-matching/normalizers.spec.ts +++ b/features/threat-intelligence/backend-api/src/features/identifier-matching/normalizers.spec.ts @@ -16,7 +16,7 @@ import { normalizeLegalName, normalizeCardNumber, normalizePaymentAppId, -} from './normalizers'; +} from '@lilith/identifier-utils'; describe('normalizers', () => { describe('existing normalizers', () => { diff --git a/features/threat-intelligence/frontend-public/src/shared/IdentifierInput.tsx b/features/threat-intelligence/frontend-public/src/shared/IdentifierInput.tsx index 3f04bcc4a..92292d208 100644 --- a/features/threat-intelligence/frontend-public/src/shared/IdentifierInput.tsx +++ b/features/threat-intelligence/frontend-public/src/shared/IdentifierInput.tsx @@ -1,17 +1,22 @@ /** @jsxImportSource react */ import type { FC } from 'react'; -import { IdentifierType } from '@lilith/risk-assessment-shared'; +import { IdentifierType, USER_MANAGEABLE_TYPES } from '@lilith/risk-assessment-shared'; -/** User-visible identifier types (excludes browser fingerprint types) */ -const USER_VISIBLE_TYPES: { value: IdentifierType; label: string; placeholder: string }[] = [ - { value: IdentifierType.EMAIL, label: 'Email', placeholder: 'name@example.com' }, - { value: IdentifierType.PHONE, label: 'Phone', placeholder: '+1 555-123-4567' }, - { value: IdentifierType.USERNAME, label: 'Username', placeholder: 'username123' }, - { value: IdentifierType.LEGAL_NAME, label: 'Legal Name', placeholder: 'Full legal name' }, - { value: IdentifierType.PAYMENT_APP_ID, label: 'Payment App ID', placeholder: 'CashApp, Venmo, etc.' }, - { value: IdentifierType.CARD_HASH, label: 'Card Hash', placeholder: 'Last 4 digits or hash' }, -]; +const IDENTIFIER_LABELS: Record = { + [IdentifierType.EMAIL]: { label: 'Email', placeholder: 'name@example.com' }, + [IdentifierType.PHONE]: { label: 'Phone', placeholder: '+1 555-123-4567' }, + [IdentifierType.USERNAME]: { label: 'Username', placeholder: 'username123' }, + [IdentifierType.LEGAL_NAME]: { label: 'Legal Name', placeholder: 'Full legal name' }, + [IdentifierType.PAYMENT_APP_ID]: { label: 'Payment App ID', placeholder: 'CashApp, Venmo, etc.' }, +}; + +/** User-visible identifier types derived from shared USER_MANAGEABLE_TYPES */ +const USER_VISIBLE_TYPES = USER_MANAGEABLE_TYPES.map((type) => ({ + value: type, + label: IDENTIFIER_LABELS[type]?.label ?? type, + placeholder: IDENTIFIER_LABELS[type]?.placeholder ?? 'Enter value', +})); interface IdentifierInputProps { type: string; diff --git a/features/threat-intelligence/shared/src/index.ts b/features/threat-intelligence/shared/src/index.ts index 86cf9e952..2ec1fc51b 100644 --- a/features/threat-intelligence/shared/src/index.ts +++ b/features/threat-intelligence/shared/src/index.ts @@ -5,6 +5,8 @@ export { type CheckResult, } from './types/identifier.types'; +export { USER_MANAGEABLE_TYPES, type IdentifierCategory } from '@lilith/identifier-utils'; + export { ThreatSeverity, ThreatSource, diff --git a/tools/nightcrawler/crawl-config.yaml b/tools/nightcrawler/crawl-config.yaml index 12b3ff5de..5486eb16c 100644 --- a/tools/nightcrawler/crawl-config.yaml +++ b/tools/nightcrawler/crawl-config.yaml @@ -27,7 +27,7 @@ proxy: instances: 10 maxInstances: 10 cooldownMs: 600000 - startPort: 18118 + startPort: 28118 host: 127.0.0.1 managerUrl: http://localhost:7710 circuitBreaker: