Skip to content

Conversation

@transphorm
Copy link
Member

@transphorm transphorm commented Nov 27, 2025

Related pr:
https://github.com/selfxyz/self/pulls

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced support for TD1 format travel documents with improved overflow handling and automatic validation.
    • Better document number validation and processing for various complex document formats.
  • Bug Fixes

    • Resolved issues with scanning documents that have longer document numbers.
  • Chores

    • Version updated to 2.9.6 for Android and iOS platforms.

✏️ Tip: You can customize this high-level summary in your review settings.


Note

Improves MRZ handling for TD1 IDs where document numbers overflow beyond 9 chars and ensures correct NFC BAC key generation.

  • iOS: LiveMRZScannerView adds manual TD1 line-1 extraction/check-digit validation, overrides documentNumber in results, and only applies when TD1 parsing is needed
  • iOS: PassportReader.pad no longer truncates values ≥ field length to preserve full TD1 document numbers for MRZ key derivation
  • JS SDK: relaxes checkScannedInfo to allow document numbers > 9 (TD1 overflow per ICAO 9303)
  • Versioning: bumps Android versionName and iOS CFBundleShortVersionString/MARKETING_VERSION to 2.9.6

Written by Cursor Bugbot for commit 9ff8bef. This will update automatically on new commits. Configure here.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 27, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Version bump from 2.9.5 to 2.9.6 across Android and iOS platforms. Introduces TD1 overflow-aware MRZ document handling with check digit validation, extraction logic, and non-truncating padding. Removes passportNumber length validation guard in mobile SDK.

Changes

Cohort / File(s) Summary
Version Bump
app/android/app/build.gradle, app/ios/OpenPassport/Info.plist, app/ios/Self.xcodeproj/project.pbxproj
Updated versionName/MARKETING_VERSION/CFBundleShortVersionString from 2.9.5 to 2.9.6 across Android and iOS build configurations.
TD1 Overflow MRZ Handling
app/ios/LiveMRZScannerView.swift
Added overrideDocumentNumber state and TD1-specific processing (processTD1DocumentWithOverflow, extractAndValidateTD1DocumentNumber, calculateMRZCheckDigit). Routes three-line 30-char MRZ blocks to new validation flow with debug logging. Replaced Belgium-specific correction with generalized handler.
MRZ Validation & Padding
packages/mobile-sdk-alpha/src/processing/mrz.ts, app/ios/PassportReader.swift
Removed passportNumber length >9 guard in checkScannedInfo. Changed pad() logic to preserve original values instead of truncating when length exceeds target fieldLength; now only pads with "<" when value is shorter.

Sequence Diagram(s)

sequenceDiagram
    participant Scanner as MRZ Scanner
    participant Detector as TD1 Detector
    participant Extractor as Document Extractor
    participant Parser as MRZ Parser
    participant Validator as Check Digit Validator
    participant Result as Result Mapper

    Scanner->>Detector: Detect MRZ block (3 lines, 30 chars)
    alt TD1 Overflow Format
        Detector->>Extractor: Route to TD1 handler
        Extractor->>Extractor: Extract document number from line 1
        Extractor->>Validator: Validate check digit
        Validator-->>Extractor: ✓ Valid / ✗ Invalid
        Extractor->>Parser: Parse remaining MRZ fields<br/>(dateOfBirth, dateOfExpiry)
        Parser-->>Extractor: Parsed fields
        Extractor->>Extractor: Store in overrideDocumentNumber
        Extractor-->>Result: Full validated document number
    else Standard Format
        Detector->>Result: Use standard processing
    end
    Result->>Result: Map to result dictionary<br/>using overrideDocumentNumber if present
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • seshanthS

Poem

🎫 From 2.9.5 to 2.9.6 we climb,
TD1 documents now handle overflow time,
Check digits dance, padding won't truncate,
MRZ fields validated—now that's first-rate! ✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description provides a detailed overview of changes but lacks the structured template sections (Description, Tested, How to QA). Add proper template sections: formally describe what changed and why, explain testing methodology, and provide QA steps for validating the TD1 overflow handling.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately references the main feature (TD1 document overflow format handling) and includes a clear ticket identifier.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch arman/belgian-id-overflow-format

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff97882 and 9ff8bef.

📒 Files selected for processing (4)
  • app/android/app/build.gradle
  • app/ios/OpenPassport/Info.plist
  • app/ios/PassportReader.swift
  • app/ios/Self.xcodeproj/project.pbxproj
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/ios/OpenPassport/Info.plist
  • app/ios/Self.xcodeproj/project.pbxproj
🧰 Additional context used
📓 Path-based instructions (4)
**/{mobile,client,app,time,verification}/**/*.{ts,tsx,js,swift,kt}

📄 CodeRabbit inference engine (.cursor/rules/compliance-verification.mdc)

Use server-signed time tokens or chain block timestamps for trusted time in mobile clients, do not trust device wall-clock alone

Files:

  • app/ios/PassportReader.swift
**/{mobile,client,app,proof,zk}/**/*.{ts,tsx,js,swift,kt}

📄 CodeRabbit inference engine (.cursor/rules/compliance-verification.mdc)

**/{mobile,client,app,proof,zk}/**/*.{ts,tsx,js,swift,kt}: Include trusted time anchor in proof generation and verify time anchor authenticity before proof generation in mobile implementations
Achieve proof generation in <60 seconds on mid-tier mobile devices

Files:

  • app/ios/PassportReader.swift
app/ios/**/*

📄 CodeRabbit inference engine (app/AGENTS.md)

Ensure iOS build succeeds with yarn ios before creating a PR

Files:

  • app/ios/PassportReader.swift

⚙️ CodeRabbit configuration file

app/ios/**/*: Review iOS-specific code for:

  • Platform-specific implementations
  • Performance considerations
  • Security best practices for mobile

Files:

  • app/ios/PassportReader.swift
app/android/**/*

📄 CodeRabbit inference engine (app/AGENTS.md)

Ensure Android build succeeds with yarn android before creating a PR

Files:

  • app/android/app/build.gradle

⚙️ CodeRabbit configuration file

app/android/**/*: Review Android-specific code for:

  • Platform-specific implementations
  • Performance considerations
  • Security best practices for mobile

Files:

  • app/android/app/build.gradle
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: selfxyz/self PR: 0
File: packages/mobile-sdk-alpha/AGENTS.md:0-0
Timestamp: 2025-12-25T19:19:35.339Z
Learning: Applies to packages/mobile-sdk-alpha/**/*.test.{ts,tsx} : Verify `extractMRZInfo()` using published sample MRZ strings (e.g., ICAO examples)
Learnt from: CR
Repo: selfxyz/self PR: 0
File: app/AGENTS.md:0-0
Timestamp: 2025-12-25T19:19:04.943Z
Learning: Address CodeRabbitAI feedback during PR review process
Learnt from: seshanthS
Repo: selfxyz/self PR: 1337
File: packages/mobile-sdk-alpha/src/processing/mrz.ts:189-194
Timestamp: 2025-11-18T12:17:14.819Z
Learning: In packages/mobile-sdk-alpha/src/processing/mrz.ts, the checkScannedInfo function and related TD1 extraction/validation logic are only reached on Android. iOS uses native Swift parsing (LiveMRZScannerView.swift) that bypasses this TypeScript layer.
Learnt from: shazarre
Repo: selfxyz/self PR: 1236
File: packages/mobile-sdk-alpha/src/flows/onboarding/document-nfc-screen.tsx:356-378
Timestamp: 2025-10-23T12:08:55.529Z
Learning: In packages/mobile-sdk-alpha/src/flows/onboarding/document-nfc-screen.tsx, the NFC native events emitted via NativeEventEmitter are generic status strings (e.g., "PACE succeeded", "BAC failed", "Reading DG1 succeeded") and do not contain sensitive MRZ data or passport numbers, so they do not require sanitization before logging.
📚 Learning: 2025-11-18T12:17:14.819Z
Learnt from: seshanthS
Repo: selfxyz/self PR: 1337
File: packages/mobile-sdk-alpha/src/processing/mrz.ts:189-194
Timestamp: 2025-11-18T12:17:14.819Z
Learning: In packages/mobile-sdk-alpha/src/processing/mrz.ts, the checkScannedInfo function and related TD1 extraction/validation logic are only reached on Android. iOS uses native Swift parsing (LiveMRZScannerView.swift) that bypasses this TypeScript layer.

Applied to files:

  • app/ios/PassportReader.swift
📚 Learning: 2025-07-29T01:08:28.530Z
Learnt from: transphorm
Repo: selfxyz/self PR: 795
File: app/android/app/build.gradle:157-158
Timestamp: 2025-07-29T01:08:28.530Z
Learning: For this React Native project, the team prefers build flexibility over fail-fast behavior for release builds in app/android/app/build.gradle. They intentionally allow fallback to debug signing for local development runs, relying on Google Play Console validation to catch any improperly signed releases during upload.

Applied to files:

  • app/android/app/build.gradle
⏰ Context from checks skipped due to timeout of 300000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (2)
app/android/app/build.gradle (1)

138-138: Version bump verified and consistent across platforms.

versionName updated to "2.9.6" is consistent across Android, iOS (MARKETING_VERSION), and package.json. Note that versionCode (121) remains unchanged—if this release targets Google Play Store, versionCode should increment for submission, but this pattern may be intentional for internal/beta builds.

The metadata change here cannot affect E2E test outcomes; test failures would stem from the underlying MRZ parsing fix, not the version string.

app/ios/PassportReader.swift (1)

121-130: Implementation correctly follows ICAO Doc 9303 for TD1 overflow MRZ key construction—no changes required.

The updated padding logic is standards-compliant. ICAO Doc 9303 explicitly requires that for TD1 documents with long (overflow) document numbers, the BAC/PACE MRZ key must be derived using the entire reconstructed document number (9-character principal part + overflow characters), not just the first 9 characters with their individual check digit. The current implementation correctly calculates the checksum on the full padded value, which aligns with the specification.

Likely an incorrect or invalid review comment.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@transphorm transphorm marked this pull request as ready for review November 27, 2025 03:05
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +224 to +228
print("[LiveMRZScannerView] TD1 document number check digit is INVALID")
return nil
}

// print("[LiveMRZScannerView] Belgium MRZ result: \(belgiumMRZResult)")
print("[LiveMRZScannerView] TD1 document number validated: \(documentNumber)")

Choose a reason for hiding this comment

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

P1 Badge Remove PII logging in TD1 overflow handler

The new TD1 overflow flow prints the MRZ line and validated document number with unconditional print calls, e.g. logging documentNumber at line 228, and the surrounding lines are not wrapped in #if DEBUG. The app/AGENTS.md checklist explicitly calls out “No sensitive data in logs (PII, credentials, tokens)”, but this code will emit passport/ID numbers whenever a TD1 document is scanned in production builds, leaking PII to device logs.

Useful? React with 👍 / 👎.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/mobile-sdk-alpha/src/processing/mrz.ts (1)

188-202: Good addition of TD1 overflow support for Android.

The commented-out length check allows document numbers exceeding 9 characters to pass through. Per the learnings, this TypeScript layer is only reached on Android (iOS uses native Swift parsing in LiveMRZScannerView.swift). The remaining date validations correctly ensure dateOfBirth and dateOfExpiry are exactly 6 characters.

Consider removing the commented code entirely rather than leaving it in place, since the explanatory comment adequately documents the intent.

 export function checkScannedInfo(passportNumber: string, dateOfBirth: string, dateOfExpiry: string): boolean {
   // TD1 overflow format allows document numbers > 9 characters per ICAO 9303:
   // When document numbers exceed 9 characters, the overflow digits are stored in the optional data
   // field and the full document number is reconstructed during MRZ parsing.
-  // if (passportNumber.length > 9) {
-  //   return false;
-  // }
   if (dateOfBirth.length !== 6) {
     return false;
   }
app/ios/LiveMRZScannerView.swift (1)

13-13: Minor: Redundant nil initialization.

Per SwiftLint, initializing an optional with nil is redundant since optionals default to nil.

-    @State private var overrideDocumentNumber: String? = nil  // for TD1 overflow format (ID cards)
+    @State private var overrideDocumentNumber: String?  // for TD1 overflow format (ID cards)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2485ab1 and 762de05.

📒 Files selected for processing (6)
  • app/android/app/build.gradle (1 hunks)
  • app/ios/LiveMRZScannerView.swift (5 hunks)
  • app/ios/OpenPassport/Info.plist (1 hunks)
  • app/ios/Self.xcodeproj/project.pbxproj (2 hunks)
  • app/package.json (1 hunks)
  • packages/mobile-sdk-alpha/src/processing/mrz.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (19)
app/**/*.{ts,tsx,js,jsx,json,yml,yaml}

📄 CodeRabbit inference engine (app/AGENTS.md)

Ensure yarn nice passes (fixes linting and formatting) before creating a PR

Files:

  • app/package.json
app/android/**

📄 CodeRabbit inference engine (app/AGENTS.md)

Android build must succeed via yarn android command in target environments

Files:

  • app/android/app/build.gradle
app/{ios,android}/**

📄 CodeRabbit inference engine (app/AGENTS.md)

Document complex native module changes and explain platform-specific code paths

Files:

  • app/android/app/build.gradle
  • app/ios/OpenPassport/Info.plist
  • app/ios/LiveMRZScannerView.swift
  • app/ios/Self.xcodeproj/project.pbxproj
app/android/**/*

⚙️ CodeRabbit configuration file

app/android/**/*: Review Android-specific code for:

  • Platform-specific implementations
  • Performance considerations
  • Security best practices for mobile

Files:

  • app/android/app/build.gradle
app/ios/**

📄 CodeRabbit inference engine (app/AGENTS.md)

iOS build must succeed via yarn ios command in target environments

Files:

  • app/ios/OpenPassport/Info.plist
  • app/ios/LiveMRZScannerView.swift
  • app/ios/Self.xcodeproj/project.pbxproj
app/ios/**/*

⚙️ CodeRabbit configuration file

app/ios/**/*: Review iOS-specific code for:

  • Platform-specific implementations
  • Performance considerations
  • Security best practices for mobile

Files:

  • app/ios/OpenPassport/Info.plist
  • app/ios/LiveMRZScannerView.swift
  • app/ios/Self.xcodeproj/project.pbxproj
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{js,jsx,ts,tsx}: NEVER log sensitive data including PII (names, DOB, passport numbers, addresses), credentials, tokens, API keys, private keys, or session identifiers.
ALWAYS redact/mask sensitive fields in logs using consistent patterns (e.g., ***-***-1234 for passport numbers, J*** D*** for names).

Files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Use React Navigation with createStaticNavigation for type-safe navigation in React Native applications.
Implement platform-specific handling with Platform.OS === 'ios' ? 'iOS' : 'Android' checks before platform-specific code in React Native.
Initialize native modules with initializeNativeModules() before any native operations in React Native.
Implement lazy loading for screens using React.lazy() in React Native applications.
Implement custom modal system with useModal hook and callback registry in React Native.
Integrate haptic feedback using useHapticNavigation hook in React Native navigation.
Use platform-specific initial routes: web uses 'Home', mobile uses 'Splash' in React Navigation.
Use Zustand for global state management in React Native applications.
Use custom hooks for complex state (useModal, useHapticNavigation) instead of inline logic.
Use AsyncStorage for simple data, SQLite for complex data, and Keychain for sensitive data in React Native.
Use @/ alias for src imports and @tests/ alias for test imports in TypeScript/JavaScript files.
Use conditional rendering with Platform.OS for platform-specific code in React Native.
Use Tamagui for UI components in React Native applications.
Do not log sensitive data in production, including identity verification and passport information.
Use Keychain for secure storage of sensitive data in React Native.
Implement proper cleanup of sensitive data after use.
Implement certificate validation for passport data verification.
Always use try-catch for async operations in React Native and TypeScript code.
Implement graceful degradation when native modules fail in React Native.
Provide user-friendly error messages in UI and error handlers.
Lazy load screens and components to optimize bundle size in React Native.
Prevent memory leaks in native modules in React Native.

Files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
**/*.{tsx,jsx,ts,js}

📄 CodeRabbit inference engine (.cursorrules)

Implement proper cleanup in useEffect and component unmount hooks in React.

Files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
packages/mobile-sdk-alpha/src/processing/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/mobile-sdk-migration.mdc)

Migrate MRZ processing helpers from app/src/utils/ to packages/mobile-sdk-alpha/src/processing/ with comprehensive tests for MRZ parsing and cross-platform compatibility

Files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/mobile-sdk-migration.mdc)

**/*.{ts,tsx,js}: Never log PII, credentials, or private keys in production code; use DEBUG_SECRETS_TOKEN flag for debug-level secrets
Use consistent redaction patterns for sensitive fields in logs and test data

Files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
packages/mobile-sdk-alpha/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/mobile-sdk-migration.mdc)

packages/mobile-sdk-alpha/src/**/*.{ts,tsx}: Test platform-specific code paths for React Native (iOS and Android) and web implementations
Ensure all exports in migrated modules support tree shaking for optimal bundle size
Use TypeScript adapter pattern for cross-platform implementations with consistent interfaces
Implement consistent SDKError class for error handling with error codes and details
Use lazy loading for modules to optimize runtime performance and memory usage
Use async/await patterns for non-blocking operations throughout SDK modules
Provide comprehensive TypeScript type definitions and maintain type safety across SDK exports
Create clear and actionable error messages with descriptive error codes for SDK consumers
Support flexible configuration options and partner branding customization in SDK integrations

Files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
packages/mobile-sdk-alpha/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/mobile-sdk-alpha/AGENTS.md)

Use TypeScript with strict type checking for this package

Files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
packages/mobile-sdk-alpha/**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (packages/mobile-sdk-alpha/AGENTS.md)

Configure ESLint with TypeScript-specific rules

Files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
packages/mobile-sdk-alpha/**/*.{js,ts,tsx,json,md,yml,yaml}

📄 CodeRabbit inference engine (packages/mobile-sdk-alpha/AGENTS.md)

Use Prettier for code formatting with root Prettier and EditorConfig settings

Files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
packages/mobile-sdk-alpha/**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

packages/mobile-sdk-alpha/**/*.{ts,tsx,js,jsx}: Review alpha mobile SDK code for:

  • API consistency with core SDK
  • Platform-neutral abstractions
  • Performance considerations
  • Clear experimental notes or TODOs

Files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
**/{mobile,client,app,time,verification}/**/*.{ts,tsx,js,swift,kt}

📄 CodeRabbit inference engine (.cursor/rules/compliance-verification.mdc)

Use server-signed time tokens or chain block timestamps for trusted time in mobile clients, do not trust device wall-clock alone

Files:

  • app/ios/LiveMRZScannerView.swift
**/{mobile,client,app,proof,zk}/**/*.{ts,tsx,js,swift,kt}

📄 CodeRabbit inference engine (.cursor/rules/compliance-verification.mdc)

**/{mobile,client,app,proof,zk}/**/*.{ts,tsx,js,swift,kt}: Include trusted time anchor in proof generation and verify time anchor authenticity before proof generation in mobile implementations
Achieve proof generation in <60 seconds on mid-tier mobile devices

Files:

  • app/ios/LiveMRZScannerView.swift
app/**/*.{ts,tsx,js,jsx,swift,kt,java}

📄 CodeRabbit inference engine (app/AGENTS.md)

app/**/*.{ts,tsx,js,jsx,swift,kt,java}: Flag security-sensitive operations and note performance implications in code comments
Ensure no sensitive data (PII, credentials, tokens) is present in logs

Files:

  • app/ios/LiveMRZScannerView.swift
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/mobile-sdk-migration.mdc:0-0
Timestamp: 2025-11-25T14:07:55.494Z
Learning: Applies to packages/mobile-sdk-alpha/src/processing/**/*.{ts,tsx} : Migrate MRZ processing helpers from app/src/utils/ to packages/mobile-sdk-alpha/src/processing/ with comprehensive tests for MRZ parsing and cross-platform compatibility
Learnt from: seshanthS
Repo: selfxyz/self PR: 1337
File: packages/mobile-sdk-alpha/src/processing/mrz.ts:189-194
Timestamp: 2025-11-18T12:17:14.819Z
Learning: In packages/mobile-sdk-alpha/src/processing/mrz.ts, the checkScannedInfo function and related TD1 extraction/validation logic are only reached on Android. iOS uses native Swift parsing (LiveMRZScannerView.swift) that bypasses this TypeScript layer.
Learnt from: CR
Repo: selfxyz/self PR: 0
File: packages/mobile-sdk-alpha/AGENTS.md:0-0
Timestamp: 2025-11-25T14:08:51.167Z
Learning: Applies to packages/mobile-sdk-alpha/**/*.test.{ts,tsx} : Verify `extractMRZInfo()` using published sample MRZ strings (e.g., ICAO examples) rather than fake data
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/mobile-sdk-migration.mdc:0-0
Timestamp: 2025-11-25T14:07:55.494Z
Learning: Applies to packages/mobile-sdk-alpha/samples/**/*.{ts,tsx,js,jsx} : Create sample React Native and web applications in packages/mobile-sdk-alpha/samples/ demonstrating MRZ parsing, proof flow, and iOS URL scheme integration
📚 Learning: 2025-11-25T14:08:51.167Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: packages/mobile-sdk-alpha/AGENTS.md:0-0
Timestamp: 2025-11-25T14:08:51.167Z
Learning: Applies to packages/mobile-sdk-alpha/**/package.json : Ensure exports are properly configured before publishing the package

Applied to files:

  • app/package.json
📚 Learning: 2025-11-25T14:08:51.167Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: packages/mobile-sdk-alpha/AGENTS.md:0-0
Timestamp: 2025-11-25T14:08:51.167Z
Learning: Applies to packages/mobile-sdk-alpha/**/package.json : Verify package conditions are valid before publishing

Applied to files:

  • app/package.json
📚 Learning: 2025-07-29T01:08:28.530Z
Learnt from: transphorm
Repo: selfxyz/self PR: 795
File: app/android/app/build.gradle:157-158
Timestamp: 2025-07-29T01:08:28.530Z
Learning: For this React Native project, the team prefers build flexibility over fail-fast behavior for release builds in app/android/app/build.gradle. They intentionally allow fallback to debug signing for local development runs, relying on Google Play Console validation to catch any improperly signed releases during upload.

Applied to files:

  • app/android/app/build.gradle
📚 Learning: 2025-11-25T14:07:55.494Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/mobile-sdk-migration.mdc:0-0
Timestamp: 2025-11-25T14:07:55.494Z
Learning: Applies to packages/mobile-sdk-alpha/src/processing/**/*.{ts,tsx} : Migrate MRZ processing helpers from app/src/utils/ to packages/mobile-sdk-alpha/src/processing/ with comprehensive tests for MRZ parsing and cross-platform compatibility

Applied to files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
  • app/ios/LiveMRZScannerView.swift
📚 Learning: 2025-11-18T12:17:14.819Z
Learnt from: seshanthS
Repo: selfxyz/self PR: 1337
File: packages/mobile-sdk-alpha/src/processing/mrz.ts:189-194
Timestamp: 2025-11-18T12:17:14.819Z
Learning: In packages/mobile-sdk-alpha/src/processing/mrz.ts, the checkScannedInfo function and related TD1 extraction/validation logic are only reached on Android. iOS uses native Swift parsing (LiveMRZScannerView.swift) that bypasses this TypeScript layer.

Applied to files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
  • app/ios/LiveMRZScannerView.swift
📚 Learning: 2025-11-25T14:08:51.167Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: packages/mobile-sdk-alpha/AGENTS.md:0-0
Timestamp: 2025-11-25T14:08:51.167Z
Learning: Applies to packages/mobile-sdk-alpha/**/*.test.{ts,tsx} : Verify `extractMRZInfo()` using published sample MRZ strings (e.g., ICAO examples) rather than fake data

Applied to files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
📚 Learning: 2025-11-25T14:07:28.166Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/compliance-verification.mdc:0-0
Timestamp: 2025-11-25T14:07:28.166Z
Learning: Applies to **/{compliance,ofac,verification,identity}/**/*.{ts,tsx,js,py} : Validate passport numbers by removing whitespace/punctuation and performing country-specific format validation

Applied to files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
📚 Learning: 2025-11-25T14:08:51.167Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: packages/mobile-sdk-alpha/AGENTS.md:0-0
Timestamp: 2025-11-25T14:08:51.167Z
Learning: Applies to packages/mobile-sdk-alpha/**/*.test.{ts,tsx} : Test `isPassportDataValid()` with realistic, synthetic passport data and never use real user PII

Applied to files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
📚 Learning: 2025-11-25T14:07:55.494Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/mobile-sdk-migration.mdc:0-0
Timestamp: 2025-11-25T14:07:55.494Z
Learning: Applies to packages/mobile-sdk-alpha/src/validation/**/*.{ts,tsx} : Migrate document validation logic from app/src/utils/ to packages/mobile-sdk-alpha/src/validation/ with unit tests for each validation rule and edge cases

Applied to files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
📚 Learning: 2025-10-23T12:08:55.529Z
Learnt from: shazarre
Repo: selfxyz/self PR: 1236
File: packages/mobile-sdk-alpha/src/flows/onboarding/document-nfc-screen.tsx:356-378
Timestamp: 2025-10-23T12:08:55.529Z
Learning: In packages/mobile-sdk-alpha/src/flows/onboarding/document-nfc-screen.tsx, the NFC native events emitted via NativeEventEmitter are generic status strings (e.g., "PACE succeeded", "BAC failed", "Reading DG1 succeeded") and do not contain sensitive MRZ data or passport numbers, so they do not require sanitization before logging.

Applied to files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
  • app/ios/LiveMRZScannerView.swift
📚 Learning: 2025-11-25T14:07:28.166Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/compliance-verification.mdc:0-0
Timestamp: 2025-11-25T14:07:28.166Z
Learning: Applies to **/{compliance,ofac,verification,identity}/**/*.{ts,tsx,js,py} : Implement three-tier OFAC verification system: Passport Number Check (direct passport validation), Name + DOB Check (full name with exact date of birth), and Name + Year Check (name with year of birth, defaulting to Jan-01)

Applied to files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
📚 Learning: 2025-11-25T14:07:55.494Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/mobile-sdk-migration.mdc:0-0
Timestamp: 2025-11-25T14:07:55.494Z
Learning: Applies to packages/mobile-sdk-alpha/src/index.ts : Re-export all new migrated modules via packages/mobile-sdk-alpha/src/index.ts and document them in packages/mobile-sdk-alpha/README.md

Applied to files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
📚 Learning: 2025-11-25T14:07:55.494Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/mobile-sdk-migration.mdc:0-0
Timestamp: 2025-11-25T14:07:55.494Z
Learning: Applies to packages/mobile-sdk-alpha/src/attestation/**/*.{ts,tsx} : Migrate attestation verification from app/src/utils/ to packages/mobile-sdk-alpha/src/attestation/ with tests for PCR0 validation and certificate chain validation

Applied to files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
📚 Learning: 2025-11-25T14:06:55.954Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-25T14:06:55.954Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Implement certificate validation for passport data verification.

Applied to files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
📚 Learning: 2025-11-25T14:07:28.166Z
Learnt from: CR
Repo: selfxyz/self PR: 0
File: .cursor/rules/compliance-verification.mdc:0-0
Timestamp: 2025-11-25T14:07:28.166Z
Learning: Applies to **/{age,verification,identity,compliance}/**/*.{ts,tsx,js,py} : Implement age verification with day-level precision for 'olderThan' checks accepting ISO 8601 date inputs

Applied to files:

  • packages/mobile-sdk-alpha/src/processing/mrz.ts
📚 Learning: 2025-11-22T23:16:26.696Z
Learnt from: transphorm
Repo: selfxyz/self PR: 1446
File: .github/workflows/mobile-bundle-analysis.yml:117-117
Timestamp: 2025-11-22T23:16:26.696Z
Learning: In the selfxyz/self repository, for mobile workflows (bundle analysis, deployment, CI/CD):
- Both iOS and Android builds now cache Ruby gems at the unified path `app/vendor/bundle`
- The previous separate paths (app/ios/vendor/bundle for iOS) have been deprecated in favor of this unified approach

Applied to files:

  • app/ios/Self.xcodeproj/project.pbxproj
🪛 SwiftLint (0.57.0)
app/ios/LiveMRZScannerView.swift

[Warning] 13-13: Initializing an optional variable with nil is redundant

(redundant_optional_initialization)

🔇 Additional comments (7)
app/android/app/build.gradle (1)

137-138: LGTM - Version bump to 2.9.3.

Version name updated consistently with the iOS and package.json changes.

app/ios/OpenPassport/Info.plist (1)

23-24: LGTM - iOS version string updated to 2.9.3.

Consistent with the Android and package.json version bumps.

app/ios/Self.xcodeproj/project.pbxproj (2)

549-549: LGTM - MARKETING_VERSION updated in Debug configuration.

Version 2.9.3 is consistent across all platform configurations.


689-689: LGTM - MARKETING_VERSION updated in Release configuration.

Both Debug and Release configurations are now aligned at 2.9.3.

app/package.json (1)

2-3: LGTM - Package version updated to 2.9.3.

Version is synchronized across Android, iOS, and package.json.

app/ios/LiveMRZScannerView.swift (2)

89-112: Well-implemented ICAO 9303 check digit calculation.

The weighting scheme [7, 3, 1] and character-to-value mappings correctly follow the ICAO 9303 standard for MRZ check digit calculation.


289-298: Good routing logic for TD1 overflow detection.

The detection heuristic (3+ lines with first line exactly 30 characters) correctly identifies TD1 format MRZ blocks and routes them to the specialized overflow handler before falling back to the existing correction logic.

Comment on lines +114 to 189
/// Extracts and validates the document number from TD1 MRZ line 1, handling both standard and overflow formats.
/// TD1 format uses an overflow mechanism when document numbers exceed 9 digits.
/// Example overflow format: IDBEL595392450<8039<<<<<<<<<< where positions 6-14 contain the principal part (595392450),
/// position 15 contains the overflow indicator (<), positions 16-18 contain overflow digits (803), and position 19 contains the check digit (9).
/// The full document number becomes: 595392450803.
/// This overflow format can occur for any country using TD1 MRZ (ID cards).
private func extractAndValidateTD1DocumentNumber(line1: String) -> (documentNumber: String, isValid: Bool)? {
guard line1.count == 30 else { return nil }

// extracting positions 6-14 (9 characters - principal part)
let startIndex6 = line1.index(line1.startIndex, offsetBy: 5)
let endIndex14 = line1.index(line1.startIndex, offsetBy: 14)
let principalPart = String(line1[startIndex6..<endIndex14])

// checking position 15 for overflow indicator
let pos15Index = line1.index(line1.startIndex, offsetBy: 14)
let pos15 = line1[pos15Index]

if pos15 != "<" {
// handling standard format where position 15 is the check digit
let checkDigit = Int(String(pos15)) ?? -1
let calculatedCheck = calculateMRZCheckDigit(principalPart)
let isValid = (checkDigit == calculatedCheck)
print("[extractAndValidateTD1DocumentNumber] Standard format: \(principalPart), check=\(checkDigit), calculated=\(calculatedCheck), valid=\(isValid)")
return (principalPart, isValid)
}
return nil
}

private func cleanBelgiumDocumentNumber(doc9: String, doc3: String, checkDigit: String) -> String? {
// For Belgium TD1 format: IDBEL000001115<7027
// doc9 = "000001115" (9 digits)
// doc3 = "702" (3 digits after <)
// checkDigit = "7" (single check digit)
// handling overflow format: scanning positions 16+ until we hit <
let pos16Index = line1.index(line1.startIndex, offsetBy: 15)
let remainingPart = String(line1[pos16Index...])

// finding the overflow digits and the check digit
var overflowDigits = ""
var checkDigitChar: Character?

var cleanDoc9 = doc9
// Strip first 3 characters
let startIndex = cleanDoc9.index(cleanDoc9.startIndex, offsetBy: 3)
cleanDoc9 = String(cleanDoc9[startIndex...])
for char in remainingPart {
if char == "<" {
break
}
overflowDigits.append(char)
}

guard overflowDigits.count > 0 else {
print("[extractAndValidateTD1DocumentNumber] ERROR: No overflow digits found")
return nil
}

let fullDocumentNumber = cleanDoc9 + doc3
// extracting check digit (last character of overflow)
checkDigitChar = overflowDigits.last
let overflowWithoutCheck = String(overflowDigits.dropLast())

// constructing full document number: principal + overflow (without check digit)
let fullDocumentNumber = principalPart + overflowWithoutCheck

return fullDocumentNumber
// validating check digit against full document number
guard let checkDigitChar = checkDigitChar,
let checkDigit = Int(String(checkDigitChar)) else {
print("[extractAndValidateTD1DocumentNumber] ERROR: Invalid check digit")
return nil
}
let calculatedCheck = calculateMRZCheckDigit(fullDocumentNumber)
let isValid = (checkDigit == calculatedCheck)

#if DEBUG
print("[extractAndValidateTD1DocumentNumber] Overflow format:")
print(" Principal part (6-14): \(principalPart)")
print(" Overflow with check: \(overflowDigits)")
print(" Overflow without check: \(overflowWithoutCheck)")
print(" Full document number: \(fullDocumentNumber)")
print(" Check digit: \(checkDigit)")
print(" Calculated check: \(calculatedCheck)")
print(" Valid: \(isValid)")
#endif

return (fullDocumentNumber, isValid)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Solid TD1 overflow extraction logic with one concern: document numbers logged in production.

The extraction and validation logic correctly handles both standard TD1 format (9-digit document number with check digit at position 15) and the overflow format per ICAO 9303. The check digit validation against the reconstructed full document number is properly implemented.

However, lines 137-138 log the document number in production builds (outside #if DEBUG). Per coding guidelines, passport/document numbers are PII and should never be logged in production.

-            print("[extractAndValidateTD1DocumentNumber] Standard format: \(principalPart), check=\(checkDigit), calculated=\(calculatedCheck), valid=\(isValid)")
+            #if DEBUG
+            print("[extractAndValidateTD1DocumentNumber] Standard format: [REDACTED], check=\(checkDigit), calculated=\(calculatedCheck), valid=\(isValid)")
+            #endif
             return (principalPart, isValid)
🤖 Prompt for AI Agents
In app/ios/LiveMRZScannerView.swift around lines 114 to 189, remove or guard the
production logs that print document numbers (lines ~137-138 and any other prints
that expose principalPart/fullDocumentNumber) so PII is never logged in
non-DEBUG builds; wrap all diagnostic print statements that include document
numbers in #if DEBUG ... #endif blocks or replace them with non-PII status logs
(e.g., "document validation succeeded/failed") and, if needed, log only a
redacted/hashed identifier instead of the raw document number.

Comment on lines +202 to 265
/// Processes TD1 documents (ID cards) by manually extracting and validating the document number using the overflow format handler,
/// then parses the remaining MRZ fields (name, dates, etc.) using QKMRZParser. This bypasses QKMRZParser's validation for the
/// document number field since it doesn't handle TD1 overflow format correctly.
private func processTD1DocumentWithOverflow(result: String, parser: QKMRZParser) -> QKMRZResult? {
print("[LiveMRZScannerView] Processing TD1 document with manual overflow validation")

guard let correctedBelgiumLine = correctBelgiumDocumentNumber(result: result) else {
print("[LiveMRZScannerView] Failed to correct Belgium document number")
return nil
}

// print("[LiveMRZScannerView] Belgium corrected line: \(correctedBelgiumLine)")

// Split MRZ into lines and replace the first line
let lines = result.components(separatedBy: "\n")
guard lines.count >= 3 else {
print("[LiveMRZScannerView] Invalid MRZ format - not enough lines")
return nil
}

let originalFirstLine = lines[0]
// print("[LiveMRZScannerView] Original first line: \(originalFirstLine)")

// Pad the corrected line to 30 characters (TD1 format)
let paddedCorrectedLine = correctedBelgiumLine.padding(toLength: 30, withPad: "<", startingAt: 0)
// print("[LiveMRZScannerView] Padded corrected line: \(paddedCorrectedLine)")
let line1 = lines[0]
print("[LiveMRZScannerView] Line 1: \(line1)")

// Reconstruct the MRZ with the corrected first line
var correctedLines = lines
correctedLines[0] = paddedCorrectedLine
let correctedMRZString = correctedLines.joined(separator: "\n")
// print("[LiveMRZScannerView] Corrected MRZ string: \(correctedMRZString)")
// extracting and validating document number manually using overflow format handler
guard let (documentNumber, isDocNumberValid) = extractAndValidateTD1DocumentNumber(line1: line1) else {
print("[LiveMRZScannerView] Failed to extract TD1 document number")
return nil
}

guard let belgiumMRZResult = parser.parse(mrzString: correctedMRZString) else {
print("[LiveMRZScannerView] Belgium MRZ result is not valid")
if !isDocNumberValid {
print("[LiveMRZScannerView] TD1 document number check digit is INVALID")
return nil
}

// print("[LiveMRZScannerView] Belgium MRZ result: \(belgiumMRZResult)")
print("[LiveMRZScannerView] TD1 document number validated: \(documentNumber)")

// Try the corrected MRZ first
if isValidMRZResult(belgiumMRZResult) {
return belgiumMRZResult
// parsing the original MRZ to get all other fields (name, birthdate, etc.)
// using QKMRZParser for non-documentNumber fields
guard let mrzResult = parser.parse(mrzString: result) else {
print("[LiveMRZScannerView] Failed to parse MRZ with QKMRZParser")
return nil
}

// If document number is still invalid, try single character correction
if !belgiumMRZResult.isDocumentNumberValid {
if let correctedResult = singleCorrectDocumentNumberInMRZ(result: correctedMRZString, docNumber: belgiumMRZResult.documentNumber, parser: parser) {
// print("[LiveMRZScannerView] Single correction successful: \(correctedResult)")
if isValidMRZResult(correctedResult) {
return correctedResult
}
}
// validating that other fields are also correct
if !mrzResult.isBirthdateValid || !mrzResult.isExpiryDateValid {
print("[LiveMRZScannerView] TD1 document has invalid birthdate or expiry date")
return nil
}

return nil
#if DEBUG
print("[LiveMRZScannerView] QKMRZParser extracted fields:")
print(" countryCode: \(mrzResult.countryCode)")
print(" surnames: \(mrzResult.surnames)")
print(" givenNames: \(mrzResult.givenNames)")
print(" birthdate: \(mrzResult.birthdate?.description ?? "nil")")
print(" sex: \(mrzResult.sex ?? "nil")")
print(" expiryDate: \(mrzResult.expiryDate?.description ?? "nil")")
print(" personalNumber: \(mrzResult.personalNumber)")
print(" Parser's documentNumber: \(mrzResult.documentNumber)")
print(" Our validated documentNumber: \(documentNumber)")
#endif

// storing the manually validated full document number
// this will be used for NFC chip authentication (BAC keys)
overrideDocumentNumber = documentNumber
#if DEBUG
print("[LiveMRZScannerView] Set overrideDocumentNumber to: \(documentNumber)")
#endif

// returning MRZ result, the document number will be overridden in mapVisionResultToDictionary
return mrzResult
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

TD1 processing flow is well-structured, but production logs expose PII.

The processTD1DocumentWithOverflow function correctly:

  1. Validates document number using the overflow format handler
  2. Parses remaining fields via QKMRZParser
  3. Validates birthdate and expiry dates
  4. Stores the validated document number for NFC chip authentication

However, lines 206, 215, 228 log document numbers and MRZ line data in production builds. These contain PII and should be guarded with #if DEBUG or redacted.

 private func processTD1DocumentWithOverflow(result: String, parser: QKMRZParser) -> QKMRZResult? {
-        print("[LiveMRZScannerView] Processing TD1 document with manual overflow validation")
+        #if DEBUG
+        print("[LiveMRZScannerView] Processing TD1 document with manual overflow validation")
+        #endif

         let lines = result.components(separatedBy: "\n")
         guard lines.count >= 3 else {
-            print("[LiveMRZScannerView] Invalid MRZ format - not enough lines")
+            #if DEBUG
+            print("[LiveMRZScannerView] Invalid MRZ format - not enough lines")
+            #endif
             return nil
         }

         let line1 = lines[0]
-        print("[LiveMRZScannerView] Line 1: \(line1)")
+        #if DEBUG
+        print("[LiveMRZScannerView] Line 1: [REDACTED]")
+        #endif

         // extracting and validating document number manually using overflow format handler
         guard let (documentNumber, isDocNumberValid) = extractAndValidateTD1DocumentNumber(line1: line1) else {
-            print("[LiveMRZScannerView] Failed to extract TD1 document number")
+            #if DEBUG
+            print("[LiveMRZScannerView] Failed to extract TD1 document number")
+            #endif
             return nil
         }

         if !isDocNumberValid {
-            print("[LiveMRZScannerView] TD1 document number check digit is INVALID")
+            #if DEBUG
+            print("[LiveMRZScannerView] TD1 document number check digit is INVALID")
+            #endif
             return nil
         }

-        print("[LiveMRZScannerView] TD1 document number validated: \(documentNumber) ✓")
+        #if DEBUG
+        print("[LiveMRZScannerView] TD1 document number validated: [REDACTED] ✓")
+        #endif

         // parsing the original MRZ to get all other fields (name, birthdate, etc.)
         // using QKMRZParser for non-documentNumber fields
         guard let mrzResult = parser.parse(mrzString: result) else {
-            print("[LiveMRZScannerView] Failed to parse MRZ with QKMRZParser")
+            #if DEBUG
+            print("[LiveMRZScannerView] Failed to parse MRZ with QKMRZParser")
+            #endif
             return nil
         }

         // validating that other fields are also correct
         if !mrzResult.isBirthdateValid || !mrzResult.isExpiryDateValid {
-            print("[LiveMRZScannerView] TD1 document has invalid birthdate or expiry date")
+            #if DEBUG
+            print("[LiveMRZScannerView] TD1 document has invalid birthdate or expiry date")
+            #endif
             return nil
         }
🤖 Prompt for AI Agents
In app/ios/LiveMRZScannerView.swift around lines 202–265, several print
statements (notably at ~206, ~215, ~228) emit MRZ line data and the document
number in production; wrap all prints that expose PII (line1, parser
documentNumber, our validated documentNumber, and any raw MRZ content) in #if
DEBUG / #endif blocks or redact the sensitive values before logging, and ensure
overrideDocumentNumber assignment remains but its logging is only inside DEBUG;
update all debug prints in this function accordingly so no PII is logged in
release builds.

@transphorm
Copy link
Member Author

not sure why the demo sdk e2e is failing...but tested the mobile build and it builds fine

@transphorm transphorm marked this pull request as draft December 3, 2025 05:43
@transphorm transphorm changed the title Arman/belgian id overflow format SELF-1381: (Arman) Belgian ID overflow format Dec 22, 2025
@transphorm transphorm force-pushed the arman/belgian-id-overflow-format branch from 0f3abc8 to 4a91ca7 Compare December 23, 2025 02:11
@transphorm transphorm changed the title SELF-1381: (Arman) Belgian ID overflow format SELF-1381 (feat): Belgian (Arman) ID overflow format Dec 25, 2025
@transphorm transphorm requested a review from seshanthS December 28, 2025 20:15
@transphorm transphorm marked this pull request as ready for review December 28, 2025 20:15
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 135 to +138
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 121
versionName "2.9.5"
versionName "2.9.6"

Choose a reason for hiding this comment

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

P1 Badge Bump Android versionCode for 2.9.6

The Android defaultConfig now advertises versionName 2.9.6 but still uses versionCode 121; Google Play enforces monotonically increasing versionCode values, so if 2.9.5 was already uploaded at 121 this build will be rejected as a duplicate and the 2.9.6 release cannot be shipped.

Useful? React with 👍 / 👎.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

This is the final PR Bugbot will review for you during this billing cycle

Your free Bugbot reviews will reset on January 17

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

if lines.count >= 3 && lines[0].count == 30 {
// trying overflow validation
if let td1Result = processTD1DocumentWithOverflow(result: result, parser: parser) {
handleValidMRZResult(td1Result)
Copy link

Choose a reason for hiding this comment

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

TD1 documents lose OCR correction fallback for document numbers

The old processBelgiumDocument function included a fallback to singleCorrectDocumentNumberInMRZ when the document number check digit was invalid (likely due to OCR errors). The new processTD1DocumentWithOverflow function lacks this fallback—when isDocNumberValid is false, it simply returns nil. Additionally, the main flow now returns early at line 297 for ALL TD1 documents (not just Belgium), preventing them from reaching the singleCorrectDocumentNumberInMRZ fallback at lines 301-308. This regression means TD1 documents with minor OCR errors in the document number that could have been corrected by single character replacement will now fail to scan.

Additional Locations (1)

Fix in Cursor Fix in Web

value = Int(String(char)) ?? 0
} else if char.isLetter {
// mapping letters to values: A=10, B=11, ..., Z=35
value = Int(char.asciiValue ?? 0) - Int(Character("A").asciiValue ?? 0) + 10
Copy link

Choose a reason for hiding this comment

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

Check digit calculation incorrect for lowercase letters

The calculateMRZCheckDigit function uses char.isLetter which matches both uppercase and lowercase letters, but the value calculation assumes uppercase ASCII values ('A' = 65). For lowercase letters like 'a' (ASCII 97), the calculation produces 97 - 65 + 10 = 42 instead of the correct value 10. This would cause incorrect check digit results if OCR produces lowercase characters, potentially rejecting valid documents. While MRZ data is defined as uppercase per ICAO 9303, the function would silently produce wrong results rather than handling this case explicitly.

Fix in Cursor Fix in Web

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.

3 participants