VYPR
Moderate severityNVD Advisory· Published Sep 27, 2025· Updated Sep 29, 2025

CVE-2025-10954

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.

PackageAffected versionsPatched versions
github.com/nyaruka/phonenumbersGo
< 1.2.21.2.2

Affected products

2

Patches

1
0479e35488e8

Update support for phone-context

https://github.com/nyaruka/phonenumbersRowan SeymourNov 24, 2023via ghsa
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

News mentions

0

No linked articles in our index yet.