CVE-2025-10954
Description
Versions of the package github.com/nyaruka/phonenumbers before 1.2.2 are vulnerable to Improper Validation of Syntactic Correctness of Input in the phonenumbers.Parse() function. An attacker can cause a panic by providing crafted input causing a "runtime error: slice bounds out of range".
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Crafted input to phonenumbers.Parse() in github.com/nyaruka/phonenumbers before 1.2.2 causes a panic from slice bounds out of range, enabling denial of service.
Versions of the Go package github.com/nyaruka/phonenumbers before 1.2.2 contain a vulnerability in the phonenumbers.Parse() function that fails to properly validate the syntactic correctness of input. The official description notes that an attacker can trigger a panic with a crafted input, specifically causing a "runtime error: slice bounds out of range" [1]. The Snyk advisory provides a proof-of-concept using the input "0;phone-context=+00000;", which crashes the process [3]. The root cause is the lack of bounds-checking when parsing malformed phone-context parameters or similar URI-like components, leading to the index-out-of-range panic.
Exploitation requires no authentication—an attacker simply submits a crafted string to any server endpoint that calls Parse(). The attack surface includes any application that uses this library to process user-supplied phone numbers, such as web forms, API endpoints, or data ingestion pipelines. The vulnerability is triggered solely by crafting a specific string; no special network position is needed beyond the ability to send inputs to the vulnerable function [2][3].
The impact is a denial-of-service condition (DoS) where a single crafted request can crash the application process. Since the panic is unrecoverable in Go unless the application has a recovery mechanism, repeated attacks can lead to sustained service unavailability. No other impacts such as code execution or data leakage are documented in the advisories [1][3].
The vulnerability is fixed in version 1.2.2 of the package, released after the disclosure date of November 23, 2023 [2][3]. The fix is included in a commit that updates parsing support for phone-context headers and likely validates slice boundaries more strictly [2]. Users should upgrade to the latest version; no workarounds are mentioned in the sources.
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/nyaruka/phonenumbersGo | < 1.2.2 | 1.2.2 |
Affected products
2- nyaruka/phonenumbersdescription
- Range: <1.2.2
Patches
10479e35488e8Update support for phone-context
2 files changed · +121 −25
phonenumbers.go+79 −25 modified@@ -397,6 +397,21 @@ var ( FIRST_GROUP_ONLY_PREFIX_PATTERN = regexp.MustCompile(`\(?\$1\)?`) REGION_CODE_FOR_NON_GEO_ENTITY = "001" + + // Regular expression of valid global-number-digits for the phone-context parameter, following the + // syntax defined in RFC3966. + RFC3966_VISUAL_SEPARATOR = "[\\-\\.\\(\\)]?" + RFC3966_PHONE_DIGIT = "(" + DIGITS + "|" + RFC3966_VISUAL_SEPARATOR + ")" + RFC3966_GLOBAL_NUMBER_DIGITS = "^\\" + string(PLUS_SIGN) + RFC3966_PHONE_DIGIT + "*" + DIGITS + RFC3966_PHONE_DIGIT + "*$" + RFC3966_GLOBAL_NUMBER_DIGITS_PATTERN = regexp.MustCompile(RFC3966_GLOBAL_NUMBER_DIGITS) + + // Regular expression of valid domainname for the phone-context parameter, following the syntax + // defined in RFC3966. + ALPHANUM = VALID_ALPHA + DIGITS + RFC3966_DOMAINLABEL = "[" + ALPHANUM + "]+((\\-)*[" + ALPHANUM + "])*" + RFC3966_TOPLABEL = "[" + VALID_ALPHA + "]+((\\-)*[" + ALPHANUM + "])*" + RFC3966_DOMAINNAME = "^(" + RFC3966_DOMAINLABEL + "\\.)*" + RFC3966_TOPLABEL + "\\.?$" + RFC3966_DOMAINNAME_PATTERN = regexp.MustCompile(RFC3966_DOMAINNAME) ) // INTERNATIONAL and NATIONAL formats are consistent with the definition @@ -2898,7 +2913,10 @@ func parseHelper( } nationalNumber := NewBuilder(nil) - buildNationalNumberForParsing(numberToParse, nationalNumber) + err := buildNationalNumberForParsing(numberToParse, nationalNumber) + if err != nil { + return err + } if !isViablePhoneNumber(nationalNumber.String()) { return ErrNotANumber @@ -3008,43 +3026,78 @@ func parseHelper( var ErrNumTooLong = errors.New("the string supplied is too long to be a phone number") +// Extracts the value of the phone-context parameter of numberToExtractFrom where the index of +// ";phone-context=" is the parameter indexOfPhoneContext, following the syntax defined in +// RFC3966. +func extractPhoneContext(numberToExtractFrom string, indexOfPhoneContext int) string { + // If no phone-context parameter is present + if indexOfPhoneContext == -1 { + return "" + } + + phoneContextStart := indexOfPhoneContext + len(RFC3966_PHONE_CONTEXT) + // If phone-context parameter is empty + if phoneContextStart >= len(numberToExtractFrom) { + return "" + } + + // find end of this phone-context (go doesn't have a indexOf(s, after)) + phoneContextEnd := strings.IndexRune(numberToExtractFrom[phoneContextStart:], ';') + if phoneContextEnd != -1 { + phoneContextEnd += phoneContextStart + } + + // If phone-context is not the last parameter + if phoneContextEnd != -1 { + return numberToExtractFrom[phoneContextStart:phoneContextEnd] + } else { + return numberToExtractFrom[phoneContextStart:] + } +} + +// Returns whether the value of phoneContext follows the syntax defined in RFC3966. +func isPhoneContextValid(phoneContext string) bool { + if len(phoneContext) == 0 { + return false + } + + // Does phone-context value match pattern of global-number-digits or domainname + return RFC3966_GLOBAL_NUMBER_DIGITS_PATTERN.MatchString(phoneContext) || RFC3966_DOMAINNAME_PATTERN.MatchString(phoneContext) +} + // Converts numberToParse to a form that we can parse and write it to // nationalNumber if it is written in RFC3966; otherwise extract a possible // number out of it and write to nationalNumber. func buildNationalNumberForParsing( numberToParse string, - nationalNumber *Builder) { + nationalNumber *Builder) error { indexOfPhoneContext := strings.Index(numberToParse, RFC3966_PHONE_CONTEXT) + + phoneContext := extractPhoneContext(numberToParse, indexOfPhoneContext) + if indexOfPhoneContext >= 0 && !isPhoneContextValid(phoneContext) { + return ErrNotANumber + } if indexOfPhoneContext > 0 { - phoneContextStart := indexOfPhoneContext + len(RFC3966_PHONE_CONTEXT) - // If the phone context contains a phone number prefix, we need - // to capture it, whereas domains will be ignored. - if numberToParse[phoneContextStart] == PLUS_SIGN { - // Additional parameters might follow the phone context. If so, - // we will remove them here because the parameters after phone - // context are not important for parsing the phone number. - phoneContextEnd := strings.Index(numberToParse[phoneContextStart:], ";") - if phoneContextEnd > 0 { - nationalNumber.WriteString( - numberToParse[phoneContextStart:phoneContextEnd]) - } else { - nationalNumber.WriteString(numberToParse[phoneContextStart:]) - } - } - // Now append everything between the "tel:" prefix and the - // phone-context. This should include the national number, an - // optional extension or isdn-subaddress component. Note we also - // handle the case when "tel:" is missing, as we have seen in some - // of the phone number inputs. In that case, we append everything - // from the beginning. + // If the phone context contains a phone number prefix, we need to capture it, whereas domains + // will be ignored. + if phoneContext[0] == PLUS_SIGN { + // Additional parameters might follow the phone context. If so, we will remove them here + // because the parameters after phone context are not important for parsing the phone + // number. + nationalNumber.WriteString(phoneContext) + } + + // Now append everything between the "tel:" prefix and the phone-context. This should include + // the national number, an optional extension or isdn-subaddress component. Note we also + // handle the case when "tel:" is missing, as we have seen in some of the phone number inputs. + // In that case, we append everything from the beginning. indexOfRfc3966Prefix := strings.Index(numberToParse, RFC3966_PREFIX) indexOfNationalNumber := 0 if indexOfRfc3966Prefix >= 0 { indexOfNationalNumber = indexOfRfc3966Prefix + len(RFC3966_PREFIX) } - nationalNumber.WriteString( - numberToParse[indexOfNationalNumber:indexOfPhoneContext]) + nationalNumber.WriteString(numberToParse[indexOfNationalNumber:indexOfPhoneContext]) } else { // Extract a possible number from the string passed in (this // strips leading characters that could not be the start of a @@ -3065,6 +3118,7 @@ func buildNationalNumberForParsing( // This is because we are concerned about deleting content from a // potential number string when there is no strong evidence that the // number is actually written in RFC3966. + return nil } // Takes two phone numbers and compares them for equality.
phonenumbers_test.go+42 −0 modified@@ -42,6 +42,48 @@ func TestParse(t *testing.T) { } } +func TestParseNationalNumber(t *testing.T) { + var tests = []struct { + input string + region string + err error + expectedNum *PhoneNumber + }{ + {input: "033316005", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "33316005", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "03-331 6005", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "03 331 6005", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "tel:03-331-6005;phone-context=+64", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "tel:331-6005;phone-context=+64-3", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "tel:331-6005;phone-context=+64-3", region: "US", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "tel:03-331-6005;phone-context=+64;a=%A1", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "tel:03-331-6005;isub=12345;phone-context=+64", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "tel:+64-3-331-6005;isub=12345", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "03-331-6005;phone-context=+64", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "0064 3 331 6005", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "01164 3 331 6005", region: "US", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "+64 3 331 6005", region: "US", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "+01164 3 331 6005", region: "US", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "+0064 3 331 6005", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + {input: "+ 00 64 3 331 6005", region: "NZ", err: nil, expectedNum: testPhoneNumbers["NZ_NUMBER"]}, + + {input: "tel:253-0000;phone-context=www.google.com", region: "US", err: nil, expectedNum: testPhoneNumbers["US_LOCAL_NUMBER"]}, + {input: "tel:253-0000;isub=12345;phone-context=www.google.com", region: "US", err: nil, expectedNum: testPhoneNumbers["US_LOCAL_NUMBER"]}, + {input: "tel:2530000;isub=12345;phone-context=1234.com", region: "US", err: nil, expectedNum: testPhoneNumbers["US_LOCAL_NUMBER"]}, + } + + for _, tc := range tests { + num, err := Parse(tc.input, tc.region) + + if tc.err != nil { + assert.EqualError(t, err, tc.err.Error(), "error mismatch for input %s", tc.input) + } else { + assert.NoError(t, err, "unexpected error for input %s", tc.input) + assert.Equal(t, tc.expectedNum, num, "number mismatch for input=%s region=%s", tc.input, tc.region) + } + } +} + func TestConvertAlphaCharactersInNumber(t *testing.T) { var tests = []struct { input, expected string
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-fmjh-f678-cv3xghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-10954ghsaADVISORY
- github.com/nyaruka/phonenumbers/commit/0479e35488e8a002a261cdb515ef8a7f80ca37feghsaWEB
- github.com/nyaruka/phonenumbers/issues/148ghsaWEB
- security.snyk.io/vuln/SNYK-GOLANG-GITHUBCOMNYARUKAPHONENUMBERS-6084070ghsaWEB
News mentions
0No linked articles in our index yet.