-
-
Notifications
You must be signed in to change notification settings - Fork 13
Stabilization in Hash generation #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
❌ Deploy Preview for fingerprint-oss failed. Why did it fail? →
|
📝 WalkthroughSummary by CodeRabbit
WalkthroughIntroduces a comprehensive hashing, normalization, serialization, validation, debugging, comparison, fallback, error handling, and security framework. Expands public APIs, adds extensive tests, updates JSON and index exports, excludes tests from TS build, and ignores the local Netlify folder. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Caller
participant IDX as index.ts
participant JSON as generateJSON
participant HASH as generateId / generateIdWithDebug
participant VAL as ValidationEngine
participant FB as FallbackManager
participant NORM as Normalization
participant SER as EnhancedSerializer
participant DBG as DebugLogger
participant SEC as SecurityValidator
App->>IDX: userInfo({ hashConfig? })
IDX->>JSON: generateJSON(geo, systemInfo, combinedScore?, hashConfig?)
JSON->>HASH: generateId(systemInfo, hashConfig)
alt debugMode
HASH->>DBG: startSession()
end
HASH->>VAL: validateSystemInfo(systemInfo)
VAL-->>HASH: ValidationResult (errors, sanitizedData, security?)
HASH->>FB: populate fallbacks (if needed)
FB-->>HASH: Fallbacks applied
HASH->>NORM: normalizeValue(systemInfo, per-property)
NORM-->>HASH: normalized object
HASH->>SER: serialize(normalized, config)
SER-->>HASH: SerializationResult (string)
HASH-->>JSON: hash string (+debugInfo?)
JSON-->>IDX: JSON payload (hash, confidence, geo)
IDX-->>App: Result
opt debugMode
HASH->>DBG: endSession() -> DebugSession
end
sequenceDiagram
participant Dev as Caller
participant CMP as HashComparator
participant HASH as generateId
participant REP as Reporter
Dev->>CMP: compareSystemInfo(info1, info2, config?)
CMP->>HASH: generateId(info1, config)
CMP->>HASH: generateId(info2, config)
HASH-->>CMP: hash1
HASH-->>CMP: hash2
CMP->>CMP: diff + classify differences
CMP-->>Dev: DetailedComparisonResult
Dev->>REP: createDifferenceReport(result)
REP-->>Dev: Text report
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested labels
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
CI Feedback 🧐A test triggered by this PR failed. Here is an AI-generated analysis of the failure:
|
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
|||||||||||||||||||||
There was a problem hiding this 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 introduces comprehensive stabilization in hash generation for fingerprint consistency, focusing on security validation, threat detection, entropy analysis, and debugging capabilities for reliable fingerprint generation across different environments.
Key changes include:
- Enhanced security and validation infrastructure: New security validation system with threat detection, entropy analysis, and manipulation resistance to prevent fingerprint spoofing
- Robust error handling and debugging: Comprehensive error categorization, retry logic with exponential backoff, and detailed debug logging system for troubleshooting fingerprint variations
- Improved data processing: Enhanced normalization utilities, deterministic serialization, and fallback management for consistent fingerprint generation
Reviewed Changes
Copilot reviewed 13 out of 15 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/security.ts | Comprehensive security validation system with threat detection and entropy analysis |
| src/validation.ts | Input validation engine with sanitization and security checks |
| src/comparison.ts | Hash comparison utilities for troubleshooting fingerprint variations |
| src/fallback.ts | Fallback value management with retry logic and error categorization |
| src/serialization.ts | Enhanced deterministic object serialization with normalization |
| src/normalization.ts | Enhanced data normalization utilities for predictable fingerprint generation |
| src/debug.ts | Debug logging system with normalization step tracking |
| src/errorHandler.ts | Error handling system with categorization and retry logic |
| src/hash.ts | Enhanced hash generation with validation, fallback management, and debug capabilities |
| src/json.ts | Updated JSON generation to support hash configuration |
| src/index.ts | Enhanced main entry point with new features and exports |
| tsconfig.json | Updated TypeScript configuration to exclude test files |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
You can also share your feedback on Copilot code review for a chance to win a $100 gift card. Take the survey.
| // Fallback to basic normalization if module not available | ||
| return this.basicNormalize(data); | ||
| } | ||
| } |
Copilot
AI
Aug 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using require() in TypeScript can lead to issues with module resolution and type safety. Consider using dynamic import() instead for consistency with ES modules: const normalizationModule = await import('./normalization');
| } | |
| /** | |
| * Normalizes data for security validation (simplified normalization) | |
| * @param data - The data to normalize | |
| * @returns Promise resolving to normalized data for security analysis | |
| */ | |
| private async normalizeForSecurityCheck(data: SystemInfo): Promise<any> { | |
| // Use dynamic import to avoid circular dependencies | |
| try { | |
| const normalizationModule = await import('./normalization'); | |
| return normalizationModule.normalizeValue(data); | |
| } catch (error) { | |
| // Fallback to basic normalization if module not available | |
| return this.basicNormalize(data); | |
| } | |
| } |
| return normalizationModule.normalizeValue(data); | ||
| } catch (error) { | ||
| // Fallback to basic normalization if module not available | ||
| return this.basicNormalize(data); |
Copilot
AI
Aug 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error handling in the try-catch block silently falls back to basicNormalize() without logging the error reason. Consider adding a debug log or warning to help with troubleshooting: console.warn('Failed to load normalization module:', error);
| return this.basicNormalize(data); | |
| } catch (error) { | |
| // Fallback to basic normalization if module not available | |
| console.warn('Failed to load normalization module:', error); | |
| return this.basicNormalize(data); |
|
|
||
| // Check for suspicious numeric values (more lenient ranges) | ||
| if (typeof info.colorDepth === 'number' && (info.colorDepth < 0 || info.colorDepth > 128)) return true; | ||
| if (typeof info.hardwareConcurrency === 'number' && (info.hardwareConcurrency < 0 || info.hardwareConcurrency > 512)) return true; |
Copilot
AI
Aug 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Magic numbers like 128 and 512 are used multiple times without explanation. Consider defining these as named constants at the top of the file: const MAX_COLOR_DEPTH = 128; const MAX_HARDWARE_CONCURRENCY = 512;
| if (typeof info.hardwareConcurrency === 'number' && (info.hardwareConcurrency < 0 || info.hardwareConcurrency > 512)) return true; | |
| if (typeof info.colorDepth === 'number' && (info.colorDepth < 0 || info.colorDepth > MAX_COLOR_DEPTH)) return true; | |
| if (typeof info.hardwareConcurrency === 'number' && (info.hardwareConcurrency < 0 || info.hardwareConcurrency > MAX_HARDWARE_CONCURRENCY)) return true; |
| private hasInconsistentHardware(info: SystemInfo): boolean { | ||
| // Check for impossible hardware combinations | ||
| if (typeof info.hardwareConcurrency === 'number' && info.hardwareConcurrency > 128) return true; // Unrealistic CPU count | ||
| if (info.deviceMemory && info.deviceMemory > 32) return true; // Unrealistic memory |
Copilot
AI
Aug 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The comment says 'Unrealistic CPU count' but the threshold is 128, which may become outdated as hardware evolves. Consider making this configurable or using a more future-proof threshold with better documentation.
| if (info.deviceMemory && info.deviceMemory > 32) return true; // Unrealistic memory | |
| // Check for impossible hardware combinations | |
| // Unrealistic CPU count: configurable threshold. Default is 128, which covers virtually all consumer and most server hardware as of 2024. | |
| // Adjust MAX_REALISTIC_CPU_COUNT if hardware trends change or for specific deployment environments. | |
| if (typeof info.hardwareConcurrency === 'number' && info.hardwareConcurrency > MAX_REALISTIC_CPU_COUNT) return true; | |
| if (info.deviceMemory && info.deviceMemory > 32) return true; // Unrealistic memory |
| const multiplier = Math.pow(10, clampedPrecision); | ||
| // For negative numbers, we need to handle the epsilon differently | ||
| const epsilon = value >= 0 ? Number.EPSILON : -Number.EPSILON; | ||
| const roundedValue = Math.round((value + epsilon) * multiplier) / multiplier; |
Copilot
AI
Aug 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The epsilon addition logic for negative numbers is incorrect. Adding a negative epsilon to a negative number makes the rounding less accurate, not more. The epsilon should be added consistently regardless of sign, or the logic should be: const adjustedValue = value >= 0 ? value + epsilon : value - epsilon;
| const roundedValue = Math.round((value + epsilon) * multiplier) / multiplier; | |
| const adjustedValue = value >= 0 ? value + Number.EPSILON : value - Number.EPSILON; | |
| const roundedValue = Math.round(adjustedValue * multiplier) / multiplier; |
| // Arrays and objects are handled by the normalization utilities before this point | ||
| return value; | ||
| } | ||
|
|
Copilot
AI
Aug 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The enhancedReplacer function is defined but never used in the codebase. Consider removing it or integrating it into the serialization process to avoid dead code.
| } | ||
|
|
||
| // Handle special cases before normalization | ||
| if (obj instanceof ArrayBuffer) { |
Copilot
AI
Aug 16, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ArrayBuffer handling is duplicated in multiple places (lines 266, 289, 331). Consider extracting this logic into a shared utility function to reduce code duplication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 18
🧹 Nitpick comments (19)
tsconfig.json (1)
17-17: Tests excluded from TS build: sensible defaultExcluding "src/**/*.test.ts" from the TypeScript build keeps production artifacts clean while letting the test runner handle compilation. If you also use ".spec.ts" naming anywhere, consider adding that pattern too.
- "exclude": ["node_modules", "dist", "src/**/*.test.ts"] + "exclude": ["node_modules", "dist", "src/**/*.test.ts", "src/**/*.spec.ts"]src/comparison.test.ts (3)
128-137: Assert normalization effect for precision-only differencesYou're asserting detection of a precision difference and LOW severity. To further validate stabilization, consider asserting that such a difference is counted as normalized-away and doesn’t affect the hash impact (if your comparator exposes that info).
const audioDiff = result.differences.find(d => d.property === 'audio'); expect(audioDiff?.type).toBe(DifferenceType.PRECISION_DIFFERENCE); expect(audioDiff?.severity).toBe(DifferenceSeverity.LOW); + // Optional: verify impact analysis recognizes normalization + expect(result.impactAnalysis.normalizedAwayDifferences).toBeGreaterThan(0); + // Optional: if available on differences: affectsHash should be false + // expect(audioDiff?.affectsHash).toBe(false);
184-200: Variation analysis assertions are well chosenThe expectations on hash counts, variation rate, and presence of stability metrics/recommendations cover the critical outputs. Consider adding a negative test for the error path (inputs.length < 2) to lock in guardrails.
it('should require at least two inputs for variation analysis', async () => { await expect(analyzeHashVariations([createMockSystemInfo()])).rejects.toThrow(); });
243-277: Troubleshooter test suite generation: solid structureValidating variations and metadata totals is on point. Optionally, also assert that each variation has expectedStable reflected in downstream expectations if exposed in the generated suite.
src/json.ts (2)
73-78: Signature extension: consider typing the return shapeThe function returns a rich, structured object but doesn’t expose an explicit return type. Consider exporting a response interface to lock in the API contract for consumers.
export interface GeneratedFingerprintJSON { confidenceAssessment: { system: { score: number; rating: string; description: string; reliability: string; level: 'low' | 'medium-low' | 'medium' | 'medium-high' | 'high'; factors: string; }; combined?: { score: number; rating: string; description: string; reliability: string; level: 'low' | 'medium-low' | 'medium' | 'medium-high' | 'high'; factors: string; }; }; geolocation: { vpnStatus?: string; ip: string; city: string; region: { isoCode: string; name: string }; country: { isoCode: string; name: string }; continent: { code: string; name: string }; location: { accuracyRadius: number; latitude: number; longitude: number; timeZone: string }; traits: { isAnonymous: boolean; isAnonymousProxy: boolean; isAnonymousVpn: boolean; network: string }; } | null; systemInfo: SystemInfo; hash: string; }And then:
-export async function generateJSON(...): Promise<any> { +export async function generateJSON(...): Promise<GeneratedFingerprintJSON> {
90-93: Optional: be defensive around getVpnStatus failuresIf getVpnStatus can throw (network, parsing), consider wrapping it to avoid failing the whole response and defaulting vpnStatus to undefined.
- const vpnStatus = geolocationInfo ? await getVpnStatus({ - geoip: geolocationInfo.location?.timeZone || 'UTC', - localtime: systemInfo?.timezone || 'UTC' - }) : undefined; + let vpnStatus: string | undefined = undefined; + if (geolocationInfo) { + try { + vpnStatus = await getVpnStatus({ + geoip: geolocationInfo.location?.timeZone || 'UTC', + localtime: systemInfo?.timezone || 'UTC' + }); + } catch { + vpnStatus = undefined; + } + }src/debug.test.ts (1)
141-152: Avoid brittle assumptions on log entry countsAsserting an exact logEntries length can be fragile if the logger emits a “session started/ended” entry. Prefer asserting minimum count and presence of the test message.
- expect(parsed.logEntries).toHaveLength(2); // start + test message + expect(parsed.logEntries.length).toBeGreaterThanOrEqual(1); + expect(parsed.logEntries.some((e: any) => e.message?.includes('test message'))).toBe(true);src/security.ts (1)
1469-1478: Null-safe lowercasing for WebGL spoofing checks
webGL.vendor/rendererare checked for truthiness above, but adding defensive normalization avoids surprises if types drift.- return suspiciousVendors.includes(webGL.vendor.toLowerCase()) || - suspiciousRenderers.includes(webGL.renderer.toLowerCase()); + const vendor = String(webGL.vendor || '').toLowerCase(); + const renderer = String(webGL.renderer || '').toLowerCase(); + return suspiciousVendors.includes(vendor) || suspiciousRenderers.includes(renderer);src/index.ts (1)
110-152: LGTM overall; minor note on Toast duplicationFlow and new hash-related exports look good. The transparency branch shows two toasts when
messageis provided (brand + message). If that’s intentional for emphasis, keep it; otherwise, collapse to one toast.src/normalization.ts (1)
181-207: Consider stable, locale-independent comparator for primitive array sortCurrent sort coerces to string and compares via
</>. For more consistent cross-env ordering (especially with unicode), uselocaleComparewith fixed options.- .sort((a, b) => { - const strA = String(a); - const strB = String(b); - return strA < strB ? -1 : strA > strB ? 1 : 0; - }); + .sort((a, b) => String(a).localeCompare(String(b), 'en', { sensitivity: 'base' }));src/validation.ts (1)
933-941: Dynamic import vs require: check bundler/TS module configUsing
require('./normalization')inside TS may break in ESM builds. If you target ESM, prefer dynamicimport()and make the callsiteasync, or gate based on environment.Would you like me to provide an async-safe refactor that preserves the current sync API using a lazy singleton?
src/errorHandler.ts (2)
337-349: Avoid mutating error message to store timestampWrapping the original error message in a new Error loses the original stack and conflates message with metadata. Store a separate timestamp field or extend the stored object instead.
- this.errorHistory.get(propertyKey)!.push({ - ...errorInfo, - // Add timestamp for tracking - error: new Error(`${errorInfo.error.message} [${new Date().toISOString()}]`) - }); + this.errorHistory.get(propertyKey)!.push({ + ...errorInfo, + // Preserve original error; track timestamp separately if needed + // @ts-expect-error allow augmentation for internal history + timestamp: Date.now() + });If you prefer strict typing, extend
ErrorInfowith an optionaltimestamp?: numberand update references accordingly.
379-420: Retry success rate metric may be misleadingCounting “successful retries” as “entries where shouldRetry was true” does not reflect actual success. Consider tracking resolutions that no longer produce errors for the same property/attempt.
Would you like a follow-up patch to capture “resolved after retry” by correlating consecutive attempts per property?
src/hash.ts (3)
140-146: Avoid double-normalization to reduce CPU/time.You normalize once via
normalizeSystemInfo(...)and again insideEnhancedSerializer(enableNormalization: true). This duplicates work. Either rely on the serializer for normalization or turn it off in serialization when you already normalized.Minimal change to disable serializer normalization (keep current behavior otherwise):
- const serializationConfig: Partial<SerializationConfig> = { - enableNormalization: true, + const serializationConfig: Partial<SerializationConfig> = { + enableNormalization: false, sortKeys: true, sortArrays: true, ...config?.serializationConfig };Also applies to: 305-310
205-214: Hard-coded Brave plugin filter may create bias and instability.Filtering plugins by name ('Brave') can skew fingerprints and lead to non-portable behavior. Consider documenting this choice or making it configurable (e.g., via
HashGeneratorConfig.serializationConfigor a dedicated filter option).
399-432: Remove unused helpers to shrink surface area and bundle size.
findObjectDifferences,getNestedProperty,enhancedReplacer,replacer,deepSortObject, andlegacyDeepSortObjectare not used in this module. Removing them improves maintainability and reduces code size. If they are intended for reuse, export them from a dedicated utility module.Also applies to: 434-439, 534-551, 557-563, 575-602, 607-629
src/serialization.ts (1)
242-253: Normalizing object keys may cause collisions or unexpected merges.
normalizeStringValue(key)alters property names (Unicode normalization, whitespace collapse). If two distinct keys normalize to the same string, values will collide during serialization. Confirm this is desired; otherwise, consider restricting key normalization or using a reversible mapping.src/fallback.ts (1)
375-416: Optional: route retry logs through DebugLogger for consistency.
console.debug/warnhere bypass the debug system. Consider emitting via the global debug logger (when enabled) to centralize observability.src/comparison.ts (1)
395-399: Allow ignoring nested properties by full path.Currently, the ignore-check only considers the immediate
key. Use the fullcurrentPathso callers can ignore specific nested paths.Minimal change:
- if (this.config.ignoredProperties?.includes(key)) { + if (this.config.ignoredProperties?.includes(currentPath) || this.config.ignoredProperties?.includes(key)) { continue; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
.gitignore(1 hunks)src/comparison.test.ts(1 hunks)src/comparison.ts(1 hunks)src/debug.test.ts(1 hunks)src/debug.ts(1 hunks)src/errorHandler.ts(1 hunks)src/fallback.ts(1 hunks)src/hash.ts(2 hunks)src/index.ts(7 hunks)src/json.ts(3 hunks)src/normalization.ts(1 hunks)src/security.ts(1 hunks)src/serialization.ts(1 hunks)src/validation.ts(1 hunks)tsconfig.json(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (12)
src/comparison.test.ts (2)
src/types.ts (1)
SystemInfo(58-94)src/comparison.ts (6)
HashComparator(150-772)analyzeHashVariations(223-282)analyzeHashVariations(804-810)getHashTroubleshooter(1343-1348)compareSystemInfo(161-218)compareSystemInfo(792-799)
src/debug.test.ts (2)
src/debug.ts (4)
DebugLogger(127-505)getDebugLogger(515-523)startDebugSession(528-531)endDebugSession(536-542)src/normalization.ts (2)
reliableRound(27-74)normalizeStringValue(84-132)
src/normalization.ts (1)
src/debug.ts (1)
getDebugLogger(515-523)
src/fallback.ts (1)
src/types.ts (6)
WebGLInfo(96-100)CanvasInfo(102-106)PluginInfo(137-141)MathInfo(108-117)FontPreferencesInfo(120-122)SystemInfo(58-94)
src/validation.ts (2)
src/types.ts (1)
SystemInfo(58-94)src/security.ts (2)
SecurityValidationResult(33-39)SecurityValidator(101-1568)
src/hash.ts (8)
src/index.ts (8)
HashGeneratorConfig(208-208)HashDebugInfo(212-212)HashGenerationResult(209-209)InputComparisonResult(210-210)InputDifference(211-211)generateId(221-221)generateIdWithDebug(222-222)compareInputs(223-223)src/serialization.ts (5)
SerializationConfig(21-36)SerializationResult(54-61)serializeWithNormalization(424-426)EnhancedSerializer(84-394)enhancedReplacer(287-322)src/debug.ts (2)
DebugConfig(56-66)getDebugLogger(515-523)src/types.ts (1)
SystemInfo(58-94)src/fallback.ts (2)
FallbackManager(180-543)getFallbackValue(200-221)src/validation.ts (1)
ValidationEngine(75-996)src/normalization.ts (4)
normalizeArrayValue(142-252)normalizeStringValue(84-132)reliableRound(27-74)normalizeValue(383-395)src/comparison.ts (2)
compareSystemInfo(161-218)compareSystemInfo(792-799)
src/errorHandler.ts (3)
src/index.ts (3)
ErrorCategory(248-248)DEFAULT_ERROR_CONFIG(249-249)ErrorHandler(247-247)src/types.ts (1)
SystemInfo(58-94)src/fallback.ts (3)
FallbackResult(172-175)FallbackManager(180-543)shouldRetry(339-356)
src/serialization.ts (1)
src/normalization.ts (3)
normalizeValue(383-395)normalizeStringValue(84-132)reliableRound(27-74)
src/security.ts (2)
src/types.ts (1)
SystemInfo(58-94)src/normalization.ts (1)
normalizeValue(383-395)
src/json.ts (1)
src/hash.ts (2)
HashGeneratorConfig(28-35)generateId(91-99)
src/comparison.ts (2)
src/hash.ts (3)
HashDebugInfo(40-49)HashGeneratorConfig(28-35)generateIdWithDebug(226-359)src/types.ts (1)
SystemInfo(58-94)
src/index.ts (5)
src/hash.ts (1)
HashGeneratorConfig(28-35)src/systemInfo.ts (1)
getSystemInfo(132-210)src/geo-ip.ts (1)
fetchGeolocationInfo(106-161)src/compliance.ts (1)
Toast(13-99)src/json.ts (1)
generateJSON(73-140)
🪛 Biome (2.1.2)
src/validation.ts
[error] 889-889: Unexpected control character in a regular expression.
Control characters are unusual and potentially incorrect inputs, so they are disallowed.
(lint/suspicious/noControlCharactersInRegex)
[error] 889-889: Unexpected control character in a regular expression.
Control characters are unusual and potentially incorrect inputs, so they are disallowed.
(lint/suspicious/noControlCharactersInRegex)
🔇 Additional comments (17)
.gitignore (1)
8-9: LGTM: Ignore Netlify local artifactsAdding the local Netlify folder to .gitignore is a sensible hygiene improvement and avoids committing CLI artifacts. No further changes needed.
src/comparison.test.ts (5)
26-81: Comprehensive SystemInfo mock: great coverageThe mock covers a wide range of fields and realistic values, which strengthens the end-to-end tests. This aligns well with the SystemInfo interface shown in src/types.ts.
139-148: Whitespace difference checks are precise and usefulGood job verifying both the difference type and negligible severity for whitespace-only changes. This ensures normalization respects benign differences.
170-181: Readable report assertions ensure observabilityAsserting key sections in the generated report helps keep regressions in check for developer-facing diagnostics. Looks good.
202-209: Perfect stability case coveredGreat to see a 0 variation-rate and perfect consistency/predictability scenario validated. This anchors the upper bound.
280-289: Convenience wrapper coverageEnsures top-level helpers aren’t overlooked. Looks good.
src/json.ts (2)
12-13: Propagating hashConfig into generateId: good extensibilityImporting HashGeneratorConfig and threading the optional config through to generateId enables controlled, configurable hashing without breaking the signature for existing callers.
120-134: Geolocation shape hardening is thoughtfulProviding safe defaults for nested geolocation fields and including vpnStatus improves shape stability. The selective traits passthrough is reasonable.
Also applies to: 136-139
src/debug.test.ts (4)
35-45: Session lifecycle assertions look goodThe sessionId format and non-zero processing time checks are robust without being brittle. Nice coverage of the basics.
103-123: Log level filtering behavior validatedEnsuring DEBUG logs are filtered under a WARN threshold while WARN logs persist is the right test for level gating. Looks good.
125-139: Summary report expectations are pragmaticChecking total steps and per-type counts provides a solid invariant for summary generation. Good coverage.
172-192: Integration with normalization module is solidVerifying that named properties and types appear in normalizationSteps confirms cross-module logging coherence. Good test.
src/security.ts (1)
1154-1166: Prevent potential undefined serialization in uniform pattern checkSame
JSON.stringifyguard to avoid iterating overundefined.- const serialized = JSON.stringify(normalizedInfo); + const serialized = JSON.stringify(normalizedInfo) ?? '';src/index.ts (1)
206-250: Public surface expansion looks consistentType re-exports and named exports align with the new hashing and error-handling modules. No issues spotted.
src/serialization.ts (1)
180-188: LGTM: Depth guard and stats tracking are solid.The maxDepth guard and
maxDepthReachedupdates are clear and prevent runaway recursion.src/fallback.ts (1)
186-191: LGTM: FallbackManager state and config initialization look robust.Constructor wiring for custom fallbacks, retry config, and histories is clean and ready for extension.
src/comparison.ts (1)
200-217: LGTM: Clear separation of raw vs normalized differences and metadata.The result shape (hashesMatch, normalizedDifferences, impactAnalysis, metadata) is well structured for diagnostics.
| const entry: DebugLogEntry = { | ||
| id: `log_${++this.logCounter}`, | ||
| timestamp: performance.now(), | ||
| level, | ||
| category, | ||
| message, | ||
| data: data ? this.cloneValue(data) : undefined | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect timestamp source for console output (performance.now vs Date.now).
entry.timestamp is set via performance.now() but later formatted with new Date(entry.timestamp), yielding an epoch-relative timestamp. Use Date.now() for wall-clock timestamps, while keeping performance.now() for durations.
Apply this diff:
- const entry: DebugLogEntry = {
- id: `log_${++this.logCounter}`,
- timestamp: performance.now(),
+ const entry: DebugLogEntry = {
+ id: `log_${++this.logCounter}`,
+ timestamp: Date.now(),
level,
category,
message,
data: data ? this.cloneValue(data) : undefined
};Optionally keep both fields if you need high-resolution relative timings.
Also applies to: 483-489
🤖 Prompt for AI Agents
In src/debug.ts around lines 354-361 (and also update the similar block at
483-489), the entry.timestamp is incorrectly set using performance.now() but
later formatted with new Date(...), producing an epoch-relative timestamp;
change entry.timestamp to Date.now() to record a wall-clock timestamp for
console output, and if you still need high-resolution relative timings add a
separate field (e.g., perfTimestamp or elapsed) set to performance.now() so both
wall-clock and high-resolution times are preserved.
| if (!shouldRetry || attemptCount >= this.config.maxRetries) { | ||
| const fallbackResult = this.fallbackManager.getFallbackValue( | ||
| property, | ||
| fallbackReason, | ||
| originalValue | ||
| ); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Retry limit off-by-one: make handleSystemInfoError consistent with shouldRetry
shouldRetry allows attempts up to and including maxRetries (original + retries), but handleSystemInfoError falls back when attemptCount >= maxRetries. Use > to align both.
- if (!shouldRetry || attemptCount >= this.config.maxRetries) {
+ if (!shouldRetry || attemptCount > this.config.maxRetries) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (!shouldRetry || attemptCount >= this.config.maxRetries) { | |
| const fallbackResult = this.fallbackManager.getFallbackValue( | |
| property, | |
| fallbackReason, | |
| originalValue | |
| ); | |
| if (!shouldRetry || attemptCount > this.config.maxRetries) { | |
| const fallbackResult = this.fallbackManager.getFallbackValue( | |
| property, | |
| fallbackReason, | |
| originalValue | |
| ); |
🤖 Prompt for AI Agents
In src/errorHandler.ts around lines 121 to 127, the fallback is triggered when
attemptCount >= this.config.maxRetries which is off-by-one compared to
shouldRetry (which allows attempts up to and including maxRetries); change the
comparison to attemptCount > this.config.maxRetries so the handler only falls
back after exceeding maxRetries, and run tests to verify behavior remains
consistent with shouldRetry logic.
| webGLImageHash: getFallbackValue('webGL', systemInfo.webGL?.imageHash, FallbackReason.MISSING_PROPERTY), | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Fallback returns wrong type for webGLImageHash (object vs string).
When systemInfo.webGL?.imageHash is missing, you request a fallback for the webGL property and assign it directly to webGLImageHash. The fallback value for webGL is a WebGLInfo object, not a string. This yields an object where a string is expected, causing non-deterministic hash input and potential runtime surprises.
Apply this diff to extract the nested imageHash from the WebGL fallback:
- webGLImageHash: getFallbackValue('webGL', systemInfo.webGL?.imageHash, FallbackReason.MISSING_PROPERTY),
+ webGLImageHash: (() => {
+ const hash = systemInfo.webGL?.imageHash;
+ if (hash === null || hash === undefined || (typeof hash === 'string' && hash.trim() === '')) {
+ return fallbackManager.getFallbackValue('webGL', FallbackReason.MISSING_PROPERTY, hash).value.imageHash;
+ }
+ return hash;
+ })(),Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/hash.ts around lines 183-184, the current call uses
getFallbackValue('webGL', systemInfo.webGL?.imageHash, ...) which returns a
WebGLInfo object when falling back, producing an object where a string is
expected; change it to request the WebGL object fallback
(getFallbackValue('webGL', systemInfo.webGL, FallbackReason.MISSING_PROPERTY))
and then extract its imageHash property, ensuring the final webGLImageHash is a
string (e.g., access .imageHash and coerce or default to an empty string if
undefined).
| detectedFontsString: systemInfo.fontPreferences?.detectedFonts && systemInfo.fontPreferences.detectedFonts.length > 0 | ||
| ? normalizeArrayValue(systemInfo.fontPreferences.detectedFonts).join(',') | ||
| : getFallbackValue('fontPreferences', null, FallbackReason.MISSING_PROPERTY), | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Fallback for detectedFontsString uses object fallback instead of string list.
When fonts are missing, you return getFallbackValue('fontPreferences', ...), which is a FontPreferencesInfo object, not a comma-separated string. This breaks the shape of your stable input.
Apply this diff:
- detectedFontsString: systemInfo.fontPreferences?.detectedFonts && systemInfo.fontPreferences.detectedFonts.length > 0
- ? normalizeArrayValue(systemInfo.fontPreferences.detectedFonts).join(',')
- : getFallbackValue('fontPreferences', null, FallbackReason.MISSING_PROPERTY),
+ detectedFontsString: systemInfo.fontPreferences?.detectedFonts && systemInfo.fontPreferences.detectedFonts.length > 0
+ ? normalizeArrayValue(systemInfo.fontPreferences.detectedFonts).join(',')
+ : normalizeArrayValue(
+ fallbackManager.getFallbackValue('fontPreferences', FallbackReason.MISSING_PROPERTY).value.detectedFonts
+ ).join(','),Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/hash.ts around lines 186 to 189, the fallback returns the whole
FontPreferencesInfo object instead of a comma-separated detectedFonts string;
replace the fallback to target the detectedFonts property (e.g. call
getFallbackValue('fontPreferences.detectedFonts', null,
FallbackReason.MISSING_PROPERTY)) and ensure the returned fallback is a string
(if it can be an array, normalize and join it into a comma-separated string) so
detectedFontsString always yields a string list.
| canvasFingerprint: getFallbackValue('canvas', systemInfo.canvas?.geometry, FallbackReason.MISSING_PROPERTY), | ||
| audioFingerprint: getFallbackValue('audio', systemInfo.audio, FallbackReason.MISSING_PROPERTY), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Fallback returns wrong type for canvasFingerprint (object vs string).
Similar to webGL, the canvas fallback returns a CanvasInfo object. Assigning it directly to canvasFingerprint (expects a string/geometry) introduces a type mismatch.
Apply this diff:
- canvasFingerprint: getFallbackValue('canvas', systemInfo.canvas?.geometry, FallbackReason.MISSING_PROPERTY),
+ canvasFingerprint: (() => {
+ const geo = systemInfo.canvas?.geometry;
+ if (geo === null || geo === undefined || (typeof geo === 'string' && geo.trim() === '')) {
+ return fallbackManager.getFallbackValue('canvas', FallbackReason.MISSING_PROPERTY, geo).value.geometry;
+ }
+ return geo;
+ })(),Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/hash.ts around lines 191-192, the canvasFingerprint is being set from
getFallbackValue('canvas', systemInfo.canvas?.geometry, ...) which can return a
CanvasInfo object (not the geometry string) and causes a type mismatch; change
the assignment so that if the primary value is missing you extract the geometry
string from the fallback (i.e., if getFallbackValue returns an object, use its
geometry property) or call the fallback for a specific canvas-geometry key so
canvasFingerprint is always a string/geometry value rather than a CanvasInfo
object.
| const key = JSON.stringify(value); | ||
| valueFrequency.set(key, (valueFrequency.get(key) || 0) + 1); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure distribution keys are strings (avoid undefined keys and TS type mismatch)
JSON.stringify(value) can be undefined. Coerce to a string to satisfy Map<string, number> and avoid runtime surprises.
- const key = JSON.stringify(value);
+ const key = JSON.stringify(value) ?? '__undefined__'📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const key = JSON.stringify(value); | |
| valueFrequency.set(key, (valueFrequency.get(key) || 0) + 1); | |
| } | |
| const key = JSON.stringify(value) ?? '__undefined__'; | |
| valueFrequency.set(key, (valueFrequency.get(key) || 0) + 1); | |
| } |
🤖 Prompt for AI Agents
In src/security.ts around lines 818 to 820, JSON.stringify(value) can return
undefined which breaks the Map<string, number> invariant and can cause
runtime/key-type issues; coerce the result to a string before using it as a key
(e.g. replace the current expression with a deterministic string coercion such
as const key = String(JSON.stringify(value)) or JSON.stringify(value) ??
'undefined'), then use that key when updating valueFrequency so all keys are
guaranteed strings.
| const highEntropyFields = [ | ||
| 'webGLImageHash', | ||
| 'canvasFingerprint', | ||
| 'audioFingerprint', | ||
| 'canvasGeometry', | ||
| 'canvasText', | ||
| 'userAgent', | ||
| 'detectedFontsString' | ||
| ]; | ||
|
|
||
| for (const field of highEntropyFields) { | ||
| const value = this.getNestedValue(normalizedInfo, field); | ||
| if (value && typeof value === 'string') { | ||
| const entropy = this.calculateStringEntropy(value); | ||
| // More aggressive entropy detection - lowered threshold | ||
| if (entropy < 0.4 && value.length > 3) { | ||
| riskScore += 0.1; | ||
| } | ||
|
|
||
| // Check for obvious manipulation patterns | ||
| if (this.hasManipulationPatterns(value)) { | ||
| riskScore += 0.05; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Align entropy field paths with actual data shape and handle arrays
The current highEntropyFields uses non-nested keys (e.g., webGLImageHash, canvasFingerprint, detectedFontsString) that likely don’t exist on normalized data. Use nested paths and handle arrays for font lists.
- const highEntropyFields = [
- 'webGLImageHash',
- 'canvasFingerprint',
- 'audioFingerprint',
- 'canvasGeometry',
- 'canvasText',
- 'userAgent',
- 'detectedFontsString'
- ];
+ const highEntropyFields = [
+ 'webGL.imageHash',
+ 'canvas.geometry',
+ 'canvas.text',
+ 'userAgent',
+ 'fontPreferences.detectedFonts'
+ ];
@@
- for (const field of highEntropyFields) {
- const value = this.getNestedValue(normalizedInfo, field);
- if (value && typeof value === 'string') {
+ for (const field of highEntropyFields) {
+ const raw = this.getNestedValue(normalizedInfo, field);
+ const value = Array.isArray(raw) ? raw.join(',') : raw;
+ if (value && typeof value === 'string') {
const entropy = this.calculateStringEntropy(value);
// More aggressive entropy detection - lowered threshold
if (entropy < 0.4 && value.length > 3) {
riskScore += 0.1;
}
// Check for obvious manipulation patterns
if (this.hasManipulationPatterns(value)) {
riskScore += 0.05;
}
}
}Would you like me to update any downstream analyzers that may rely on the previous flat keys?
🏁 Script executed:
#!/bin/bash
set -e
echo "Searching for getNestedValue references and its definition..."
rg -n --color=never "getNestedValue" .
echo "Searching for normalizedInfo usage and its type/interface..."
rg -n --color=never "normalizedInfo\b" .
rg -n --color=never "interface NormalizedInfo" .Length of output: 5961
🏁 Script executed:
#!/bin/bash
set -e
echo "Searching for normalizeSystemInfo definition in src/hash.ts..."
rg -n -C3 "normalizeSystemInfo" src/hash.tsLength of output: 1092
🏁 Script executed:
#!/bin/bash
set -e
echo "### getNestedValue implementation ###"
rg -n -C5 "private getNestedValue" src/security.ts
echo "### webGL references in normalization ###"
rg -n -C3 "webGL" src/hash.ts
echo "### canvas references in normalization ###"
rg -n -C3 "canvas" src/hash.ts
echo "### font references in normalization ###"
rg -n -C3 "font" src/hash.tsLength of output: 4159
🏁 Script executed:
#!/bin/bash
set -e
echo "### Checking if 'userAgent' is normalized ###"
rg -n -C3 "userAgent:" src/hash.ts || echo "No direct 'userAgent:' key in normalizedInfo"
echo "### Checking for any 'canvasText' references ###"
rg -n canvasText src/hash.ts || echo "No 'canvasText' in normalization"Length of output: 1108
Align entropy fields with the flat normalizedInfo shape
The highEntropyFields array currently includes keys that don’t exist on the flattened normalizedInfo (e.g. canvasGeometry, canvasText). Remove those two entries so you only iterate over actual normalized properties:
• File: src/security.ts at the calculateEntropy method
• Lines: where highEntropyFields is defined
- const highEntropyFields = [
- 'webGLImageHash',
- 'canvasFingerprint',
- 'audioFingerprint',
- 'canvasGeometry',
- 'canvasText',
- 'userAgent',
- 'detectedFontsString'
- ];
+ const highEntropyFields = [
+ 'webGLImageHash',
+ 'canvasFingerprint',
+ 'audioFingerprint',
+ 'userAgent',
+ 'detectedFontsString'
+ ];No nested paths are needed—normalizeSystemInfo returns a flat object—and all of these fields are already serialized as strings (arrays like fonts are joined during normalization).
Let me know if we should update any downstream tests or analyzers that rely on the removed keys.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const highEntropyFields = [ | |
| 'webGLImageHash', | |
| 'canvasFingerprint', | |
| 'audioFingerprint', | |
| 'canvasGeometry', | |
| 'canvasText', | |
| 'userAgent', | |
| 'detectedFontsString' | |
| ]; | |
| for (const field of highEntropyFields) { | |
| const value = this.getNestedValue(normalizedInfo, field); | |
| if (value && typeof value === 'string') { | |
| const entropy = this.calculateStringEntropy(value); | |
| // More aggressive entropy detection - lowered threshold | |
| if (entropy < 0.4 && value.length > 3) { | |
| riskScore += 0.1; | |
| } | |
| // Check for obvious manipulation patterns | |
| if (this.hasManipulationPatterns(value)) { | |
| riskScore += 0.05; | |
| } | |
| } | |
| const highEntropyFields = [ | |
| 'webGLImageHash', | |
| 'canvasFingerprint', | |
| 'audioFingerprint', | |
| 'userAgent', | |
| 'detectedFontsString' | |
| ]; | |
| for (const field of highEntropyFields) { | |
| const value = this.getNestedValue(normalizedInfo, field); | |
| if (value && typeof value === 'string') { | |
| const entropy = this.calculateStringEntropy(value); | |
| // More aggressive entropy detection - lowered threshold | |
| if (entropy < 0.4 && value.length > 3) { | |
| riskScore += 0.1; | |
| } | |
| // Check for obvious manipulation patterns | |
| if (this.hasManipulationPatterns(value)) { | |
| riskScore += 0.05; | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/security.ts around lines 939 to 962, the highEntropyFields array includes
keys (canvasGeometry, canvasText) that don't exist on the flattened
normalizedInfo object; remove those two entries from the array so the loop only
iterates actual normalized properties (webGLImageHash, canvasFingerprint,
audioFingerprint, userAgent, detectedFontsString), leaving the rest of the
entropy checks intact and no nested path handling is required.
| /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, | ||
| /javascript:/gi, | ||
| /on\w+\s*=/gi, | ||
| /eval\s*\(/gi, | ||
| /Function\s*\(/gi | ||
| ]; | ||
|
|
||
| // Check for SQL injection patterns | ||
| const sqlPatterns = [ | ||
| /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION)\b)/gi, | ||
| /(';|--;|\|\||\/\*|\*\/|'.*OR.*'|'.*AND.*')/gi | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix stateful RegExp usage: remove g with .test()
The /gi flags with .test() can skip matches due to lastIndex. Drop g for reliability.
- const scriptPatterns = [
- /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
- /javascript:/gi,
- /on\w+\s*=/gi,
- /eval\s*\(/gi,
- /Function\s*\(/gi
- ];
+ const scriptPatterns = [
+ /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/i,
+ /javascript:/i,
+ /on\w+\s*=/i,
+ /eval\s*\(/i,
+ /Function\s*\(/i
+ ];
@@
- const sqlPatterns = [
- /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION)\b)/gi,
- /(';|--;|\|\||\/\*|\*\/|'.*OR.*'|'.*AND.*')/gi
- ];
+ const sqlPatterns = [
+ /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION)\b)/i,
+ /(';|--;|\|\||\/\*|\*\/|'.*OR.*'|'.*AND.*')/i
+ ];📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, | |
| /javascript:/gi, | |
| /on\w+\s*=/gi, | |
| /eval\s*\(/gi, | |
| /Function\s*\(/gi | |
| ]; | |
| // Check for SQL injection patterns | |
| const sqlPatterns = [ | |
| /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION)\b)/gi, | |
| /(';|--;|\|\||\/\*|\*\/|'.*OR.*'|'.*AND.*')/gi | |
| ]; | |
| const scriptPatterns = [ | |
| /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/i, | |
| /javascript:/i, | |
| /on\w+\s*=/i, | |
| /eval\s*\(/i, | |
| /Function\s*\(/i | |
| ]; | |
| // Check for SQL injection patterns | |
| const sqlPatterns = [ | |
| /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION)\b)/i, | |
| /(';|--;|\|\||\/\*|\*\/|'.*OR.*'|'.*AND.*')/i | |
| ]; |
🤖 Prompt for AI Agents
In src/validation.ts around lines 245 to 256, the review points out that the
regular expressions in the XSS and SQL pattern arrays use the global flag (`g`)
while being used with `.test()`, which can cause skipped matches due to the
RegExp `lastIndex` state; remove the `g` flag from these regex literals (e.g.,
change `/.../gi` to `/.../i` or `/.../` as appropriate) so `.test()` works
reliably, or alternatively if global matching is required elsewhere, reset
`regex.lastIndex = 0` before each `.test()` call — apply the simpler fix by
dropping `g` for all patterns in those arrays.
| /\.\.\//gi, | ||
| /\.\.\\/gi, | ||
| /%2e%2e%2f/gi, | ||
| /%2e%2e%5c/gi | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix stateful RegExp usage for path traversal detection
Remove g when using .test().
- const pathTraversalPatterns = [
- /\.\.\//gi,
- /\.\.\\/gi,
- /%2e%2e%2f/gi,
- /%2e%2e%5c/gi
- ];
+ const pathTraversalPatterns = [
+ /\.\.\//i,
+ /\.\.\\/i,
+ /%2e%2e%2f/i,
+ /%2e%2e%5c/i
+ ];📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /\.\.\//gi, | |
| /\.\.\\/gi, | |
| /%2e%2e%2f/gi, | |
| /%2e%2e%5c/gi | |
| ]; | |
| const pathTraversalPatterns = [ | |
| /\.\.\//i, | |
| /\.\.\\/i, | |
| /%2e%2e%2f/i, | |
| /%2e%2e%5c/i | |
| ]; |
🤖 Prompt for AI Agents
In src/validation.ts around lines 260 to 264 the regex array uses the global
flag (`g`) but the code calls `.test()` which is stateful with `g`; remove the
`g` flag from each pattern (e.g. change `/\.\.\//gi` to `/\.\.\//i`,
`/\.\.\\/gi` to `/\.\.\\/i`, etc.) so `.test()` is deterministic, or
alternatively instantiate new RegExp objects each test; update the four patterns
accordingly.
| // Helper methods for sanitization | ||
| private sanitizeString(value: string): string { | ||
| return value | ||
| .replace(/[\x00-\x1F\x7F-\x9F]/g, '') // Remove control characters | ||
| .replace(/\s+/g, ' ') // Normalize whitespace | ||
| .trim() | ||
| .substring(0, this.config.maxStringLength); // Truncate if too long | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Address linter error: control characters in regex
Biome flags control chars in /[\x00-\x1F\x7F-\x9F]/g. Use Unicode escapes to satisfy the rule without changing behavior.
- return value
- .replace(/[\x00-\x1F\x7F-\x9F]/g, '') // Remove control characters
+ return value
+ .replace(/[\u0000-\u001F\u007F-\u009F]/g, '') // Remove control characters
.replace(/\s+/g, ' ') // Normalize whitespace
.trim()
.substring(0, this.config.maxStringLength); // Truncate if too longAlternatively, if your runtime supports Unicode property escapes broadly, /\p{Cc}/gu is even clearer.
🧰 Tools
🪛 Biome (2.1.2)
[error] 889-889: Unexpected control character in a regular expression.
Control characters are unusual and potentially incorrect inputs, so they are disallowed.
(lint/suspicious/noControlCharactersInRegex)
[error] 889-889: Unexpected control character in a regular expression.
Control characters are unusual and potentially incorrect inputs, so they are disallowed.
(lint/suspicious/noControlCharactersInRegex)
🤖 Prompt for AI Agents
In src/validation.ts around lines 886 to 893, the regex /[\x00-\x1F\x7F-\x9F]/g
triggers a linter rule about control-character escapes; replace those hex
escapes with Unicode escapes (e.g. /[\u0000-\u001F\u007F-\u009F]/g) or, if your
runtime supports Unicode property escapes, use /\p{Cc}/gu instead, keep the
global (and unicode) flags as appropriate and leave the rest of the
sanitizeString logic (whitespace normalize, trim, substring) unchanged.
PR Type
Enhancement, Tests
Description
• Major hash generation stabilization: Implemented comprehensive security validation, threat detection, and entropy analysis to prevent manipulation attempts and ensure consistent fingerprint generation
• Enhanced debugging and troubleshooting: Added detailed debug logging system with normalization step tracking, hash comparison utilities, and diagnostic reporting for fingerprint variations
• Robust error handling and fallback management: Created comprehensive error categorization, retry logic with exponential backoff, and consistent fallback value management for improved reliability
• Improved data processing: Enhanced normalization utilities for predictable fingerprint generation across different environments and browser states
• Comprehensive validation engine: Added input sanitization, security checks, and configurable validation rules with strict/lenient modes
• Enhanced serialization: Implemented deterministic object serialization with performance optimization and statistics collection
• Extensive test coverage: Added comprehensive test suites for hash comparison utilities and debug logging system with 482 lines of test code
• Configuration improvements: Enhanced main entry point with new capabilities and updated TypeScript configuration for better development workflow
Diagram Walkthrough
File Walkthrough
10 files
security.ts
Comprehensive Security Validation System for Hash Generationsrc/security.ts
• Added comprehensive security validation system for fingerprint hash
generation
• Implemented threat detection for manipulation attempts,
spoofing, and collision risks
• Created entropy analysis and
preservation utilities for hash stability
• Added extensive input
validation patterns for script injection, SQL injection, and other
attacks
comparison.ts
Hash Comparison and Troubleshooting Utilitiessrc/comparison.ts
• Added detailed hash comparison utilities for troubleshooting
fingerprint variations
• Implemented difference detection and impact
analysis between SystemInfo inputs
• Created hash variation analysis
and stability metrics calculation
• Added troubleshooting tools with
diagnosis reports and test suite generation
fallback.ts
Fallback Value Management and Retry Logic Systemsrc/fallback.ts
• Added consistent fallback value management for SystemInfo properties
• Implemented retry logic with exponential backoff for temporary
failures
• Created error categorization system for appropriate failure
handling
• Added fallback history tracking and consistency validation
json.ts
Enhanced JSON Generation with Hash Configuration Supportsrc/json.ts
• Added optional
hashConfigparameter togenerateJSONfunction•
Updated hash generation call to accept configuration options
•
Enhanced function signature to support configurable hash generation
validation.ts
Comprehensive Input Validation and Security Enginesrc/validation.ts
• Added comprehensive validation engine with input sanitization and
security checks
• Implemented validation error types and detailed
error reporting system
• Created security validation integration with
threat detection capabilities
• Added configurable validation rules
with strict/lenient modes
hash.ts
Enhanced Hash Generation with Debug and Validationsrc/hash.ts
• Enhanced hash generation with predictable fingerprint capabilities
and improved normalization
• Added debug mode support with detailed
logging and processing information
• Implemented input comparison
functionality for analyzing differences between SystemInfo objects
•
Integrated validation engine, fallback management, and enhanced
serialization for better stability
debug.ts
Debug Logging System for Hash Generationsrc/debug.ts
• Created comprehensive debug logging system for fingerprint hash
generation
• Implemented normalization step tracking with before/after
value logging
• Added debug session management with statistics and
summary reporting
• Provided configurable logging levels and export
capabilities for debugging
serialization.ts
Enhanced Deterministic Object Serializationsrc/serialization.ts
• Implemented enhanced serialization utilities for deterministic
object serialization
• Added normalization integration during
serialization process
• Created performance comparison between
enhanced and legacy serialization methods
• Provided configurable
serialization behavior with statistics collection
normalization.ts
Enhanced Data Normalization Utilitiessrc/normalization.ts
• Created enhanced normalization utilities for predictable fingerprint
generation
• Implemented consistent data formatting across different
environments and browser states
• Added debug logging integration for
normalization steps tracking
• Provided optimized normalization paths
for primitive and complex data types
index.ts
Enhanced Main Entry Point with New Featuressrc/index.ts
• Enhanced main entry point with new hash generation and debug
capabilities
• Added error handling exports and configuration options
• Integrated new validation and debugging features into the public API
• Provided both default and named exports for better module
compatibility
1 files
tsconfig.json
TypeScript Configuration Update for Test File Exclusiontsconfig.json
• Added exclusion of test files from TypeScript compilation
• Updated
exclude pattern to ignore
src/**/*.test.tsfiles1 files
errorHandler.ts
Comprehensive Error Handling and Recovery Systemsrc/errorHandler.ts
• Implemented comprehensive error handling system with categorization
and retry logic
• Added error tracking and statistics collection for
debugging and analysis
• Created fallback integration for handling
permanent and temporary failures
• Provided configurable retry
mechanisms with exponential backoff
2 files
comparison.test.ts
Comprehensive test suite for hash comparison functionalitysrc/comparison.test.ts
• Added comprehensive test suite for hash comparison utilities with
290 lines of test code
• Implemented tests for
HashComparatorclasscovering identical inputs, value differences, type changes, precision
differences, and whitespace differences
• Created tests for hash
variation analysis, troubleshooting functionality, and convenience
functions
• Added mock
SystemInfoobject factory for consistent testdata generation
debug.test.ts
Complete test coverage for debug logging systemsrc/debug.test.ts
• Added complete test suite for debug logging system with 192 lines of
test code
• Implemented tests for
DebugLoggerclass covering sessionmanagement, normalization step logging, fallback logging, and
validation logging
• Created tests for log level filtering, summary
report generation, and JSON export functionality
• Added integration
tests for debug system with normalization functions