Skip to content

Fix location detection to only return valid ISO-3166 country codes#231

Merged
yosriady merged 3 commits into
mainfrom
claude/fix-country-location-timezone-Qa8Uw
Apr 23, 2026
Merged

Fix location detection to only return valid ISO-3166 country codes#231
yosriady merged 3 commits into
mainfrom
claude/fix-country-location-timezone-Qa8Uw

Conversation

@yosriady
Copy link
Copy Markdown
Contributor

@yosriady yosriady commented Apr 23, 2026

Summary

This PR fixes the location detection logic in EventFactory to only return valid ISO-3166 alpha-2 country codes, preventing invalid or ambiguous timezone mappings from being exposed.

Key Changes

  • EventFactory.getLocation(): Updated to validate that mapped timezone values are exactly 2 uppercase letters (ISO-3166 alpha-2 format) before returning them. Returns empty string for unmapped timezones, generic timezone abbreviations, or invalid formats.
  • COUNTRY_LIST configuration:
    • Removed all generic timezone abbreviations (UTC, GMT, EST, CST, etc.) and their variants that don't map to specific countries
    • Removed comma-separated country mappings that were ambiguous:
      • America/Phoenix: Changed from "US,CA" to "US"
      • Pacific/Guam: Changed from "GU,MP" to "GU"
    • Added missing mapping: Asia/Kostanay → "KZ"
  • New test suite: Added comprehensive test coverage in EventFactoryLocation.spec.ts validating:
    • UTC and Etc/GMT zones return empty string
    • Known timezones map to correct country codes
    • Unknown timezones return empty string
    • No comma-separated leaks in the output

Implementation Details

The location detection now enforces strict validation using a regex pattern /^[A-Z]{2}$/ to ensure only valid ISO-3166 alpha-2 codes are returned. This prevents ambiguous or non-standard timezone identifiers from being included in event context data.

https://claude.ai/code/session_01Tz1XFApBYhb1GSppB1PHds


Open in Devin Review

… unknown

Previously getLocation() fell through to returning the raw timezone when the
zone wasn't mapped to a country, leaking values like "UTC" or "Etc/GMT+7"
into the location field. Clean the COUNTRY_LIST of non-ISO self-mapped
entries (POSIX abbreviations, UTC/GMT aliases, all Etc/*, Factory), collapse
the stale multi-country rows ("US,CA" -> "US", "GU,MP" -> "GU"), add the
legacy Asia/Kostanay alias, and guard getLocation() so only ISO-3166 alpha-2
codes ever escape.
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request refines the COUNTRY_LIST configuration by removing non-ISO-3166 alpha-2 entries and correcting multi-country mappings. The EventFactory.getLocation method now strictly validates that timezone lookups return a valid two-letter country code, and comprehensive unit tests have been added to ensure correct behavior. Feedback suggests improving type safety during the lookup and optimizing performance by defining the validation regex as a constant.

Comment thread src/event/EventFactory.ts Outdated
Comment on lines +83 to +88
const mapped = COUNTRY_LIST[timezone as keyof typeof COUNTRY_LIST];
// Only emit ISO-3166 alpha-2. Anything else (including the raw
// timezone string) is treated as unknown.
return typeof mapped === "string" && /^[A-Z]{2}$/.test(mapped)
? mapped
: "";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The current implementation has two minor areas for improvement:

  1. Type Safety: The type assertion timezone as keyof typeof COUNTRY_LIST is technically incorrect because timezone is a dynamic string that might not be a key in the constant object. This also makes the typeof mapped === "string" check appear redundant to the TypeScript compiler (which assumes it must be a string based on the assertion). A safer approach is to treat the lookup as a generic record access.
  2. Performance: The regular expression /^[A-Z]{2}$/ is recreated on every call to getLocation(). While modern engines optimize literal regexes, it is better practice to define it as a constant outside the method to avoid redundant instantiation.

Consider refining the lookup and the regex handling as shown in the suggestion.

      const mapped = (COUNTRY_LIST as Record<string, string>)[timezone];
      // Only emit ISO-3166 alpha-2. Anything else (including the raw
      // timezone string) is treated as unknown.
      const ISO_3166_ALPHA_2_REGEX = /^[A-Z]{2}$/;
      return typeof mapped === "string" && ISO_3166_ALPHA_2_REGEX.test(mapped)
        ? mapped
        : "";

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

@yosriady
Copy link
Copy Markdown
Contributor Author

@codex review

chatgpt-codex-connector[bot]

This comment was marked as resolved.

…in getLocation

Address review feedback: the `timezone as keyof typeof COUNTRY_LIST` cast
narrows to a literal union the runtime string may not belong to, which also
made the `typeof mapped === "string"` guard look redundant to the compiler.
Cast through `Record<string, string>` instead, and hoist the validator regex
to module scope so it isn't re-parsed on every call.
devin-ai-integration[bot]

This comment was marked as resolved.

…RY_LIST

Moved from the stray position between Asia/Qatar and Asia/Qostanay up to
between Asia/Kolkata and Asia/Krasnoyarsk, matching the alphabetical
convention used throughout the table.
@yosriady yosriady merged commit 67e3a0e into main Apr 23, 2026
11 checks passed
@yosriady yosriady deleted the claude/fix-country-location-timezone-Qa8Uw branch April 23, 2026 10:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants