Skip to content

RN consumer tooling retains an unpatched sprintf-js advisory #328

Closed

Description

@mdspinali

Severity: Medium, from the reviewed Moderate advisory.

The retained RN 0.82.1 and RN 0.83.10 Candidate consumer locks contain Sprintf-js 1.0.3.
Both complete npm audit --json runs returned 1 on clean source 0c0279863507183c4e72386db9a4adc4f66933a5.
Each reports one concrete Moderate advisory and four inherited Moderate package records.

GHSA-hp3w-g68c-fv3c lists Sprintf-js <=1.1.3 and no patched version.
The official npm registry lists 1.1.3 as latest.
An update to that version does not clear the advisory.

The installed path is react-native -> babel-jest -> babel-plugin-istanbul -> @istanbuljs/load-nyc-config -> js-yaml -> argparse -> sprintf-js.
Jest also reaches the same leaf through a development dependency path.
The parent range is ~1.0.2.
The lock classification does not establish bundled mobile-runtime reachability.

The advisory requires attacker control of the format string.
The retained implementation passes precision values without an upper-bound check at sprintf-js/src/sprintf.js:75.
The nested Argparse formatter calls Sprintf-js at argparse/lib/help/formatter.js:16, :325, :559, and :744.
The observed NYC loader uses the YAML loader, while the separate YAML CLI imports Argparse.
Complete supported-input non-applicability remains unproved.

The Candidate audits report 52 package records from two unique advisories.
The other concrete finding is Braces, tracked in #312.
The generated lifecycle logs have matching totals, but their deleted locks prevent exact historical advisory comparison.

Required outcome

Use a supported compatible correction, or establish authoritative non-applicability for the complete supported input boundary.
Do not force a transitive major, add an override, or suppress the advisory.
No currently identified compatible leaf update satisfies this outcome.

Acceptance criteria

  • Preserve the complete audit, exact lock identities, and all measured dependency paths.
  • Identify the supported input boundary and exact advisory applicability.
  • Apply a supported correction, or document complete non-applicability with independent review.
  • Run applicable Make gates on the final clean source for any dependency correction.
  • Require positive structured results, zero failures, and zero skips.
  • Keep the original audit exit 1 and the lifecycle lock-retention limit visible.

Evidence: diagnose-rn-consumer-npm-advisories-0c02-report.md and its JSON index in the local release dispatch directory.
Applicable cells are both RN versions on iOS and Android.
This finding does not establish mobile-runtime exploitability or a protocol defect.

Activity

  1. added
    bugSomething isn't working
    dependenciesPull requests that update a dependency file
    javascriptPull requests that update javascript code
    on Oct 7, 2026
  2. added this to the v0.4.0 milestone on Oct 10, 2026
  3. mdspinali commented on Oct 11, 2026

    @mdspinali
    ContributorAuthor

    Bounded current consumer review

    Reviewed source: clean 538e1bf7d302d098d33659cb507a2489de761159.
    The complete audit retains exit status 1.
    The exact Yarn lock SHA-256 is a45d7ee796fa5fbc7bb942c4b0778d1d3092d5b87ac9c2e82989d9f0175d95e0.

    The current consumer resolves NYC 1.1.0, js-yaml 3.15.2, argparse 1.0.10, and sprintf-js 1.0.3.
    The review used createRequire from each actual parent.
    It did not substitute the separate root js-yaml 4.3.2 installation.

    All 16 NYC configuration cases recorded zero sprintf loads and calls.
    YAML input values, file names, extends values, and error data did not become numeric format strings.
    The actual YAML CLI uses fixed Argparse declarations.
    Benign and precision-bearing data probes confirmed that boundary.

    Direct %.1000e, %.1000f, and %.1000g controls each caused RangeError in the installed sprintf implementation.
    Caller-defined Argparse usage, description, epilog, and help declarations also reached the vulnerable API.
    That caller boundary remains separate from ordinary NYC and YAML data inputs.
    Executable JavaScript configurations can execute arbitrary code and do not establish safe untrusted-data handling.

    The primary inspected and reran the independent diagnostic probe.
    It completed 61 isolated cases and 171 checks, with zero unexpected failures or skips.
    The results include intentional vulnerability demonstrations and original error exits.
    Both benign and malicious explicit --compact arguments retain the CLI's separate fixed-%r SyntaxError.
    No dependency correction or Make gate result is claimed by this diagnostic review.

    Complete issue acceptance remains pending for the supported consumer scope.
    Historical deleted lifecycle locks remain unavailable for exact comparison.
    The report, input cases, outputs, source identities, and complete audit remain retained in the closeout records.
    No advisory suppression or security exception was applied.

  4. mdspinali commented on Oct 11, 2026

    @mdspinali
    ContributorAuthor

    Resolved on 538e1bf7d302d098d33659cb507a2489de761159 through independently reviewed supported-input non-applicability.

    Change

    The complete supported tooling boundary does not pass attacker-selected numeric precision formats to sprintf-js.
    The actual consumers are the NYC YAML library loader and the shipped YAML CLI.
    This disposition changes no dependency, support policy, or scanner exclusion.
    The vulnerable leaf remains installed. It is not represented as patched.

    Validation

    Reviewed and reproduced source: clean 538e1bf7d302d098d33659cb507a2489de761159.

    • Actual parent resolution selects NYC 1.1.0, js-yaml 3.15.2, Argparse 1.0.10, and sprintf-js 1.0.3.
    • Node createRequire resolves from each actual parent. It does not substitute root YAML 4 or Argparse 2.
    • The complete installed import search identifies the actual formatter consumers in both workspace trees.
    • NYC loads YAML as data. It does not import the YAML CLI or construct Argparse declarations.
    • YAML CLI declarations are fixed. Input contents, filenames, options, errors, and substituted values cannot become numeric format declarations.
    • All 16 NYC cases recorded zero sprintf loads and calls.
    • All 22 actual YAML CLI cases kept ordinary and precision-bearing inputs outside the vulnerable format boundary.
    • Direct numeric precision controls and caller-defined Argparse declarations reproduced the real leaf defect.
    • Those controls prevent a false conclusion based on an unaffected replacement package.
    • The complete diagnostic probe executed 61 isolated cases and 171 checks, with zero unexpected failures or skips.
    • The primary inspected and independently reran that probe with the same results.
    • The four relevant Yarn lock blocks are identical between recorded consumer source 0c0279863507183c4e72386db9a4adc4f66933a5 and current dev.
    • Selectors, versions, ranges, resolutions, checksums, and link metadata match.
    • Repository manifests, CI inputs, and packaged consumer scripts cover RN 0.82.1 and 0.83.10 on both platforms.
    • They expose no configuration-to-Argparse declaration interface.
    • Independent security review accepted complete supported-input non-applicability after checking those actual definitions.

    Source boundaries include NYC index.js:78-82,99-134, nested js-yaml bin/js-yaml.js:24-61,84-131, and nested Argparse formatter :325,559,744.
    The supported consumer definitions remain in verification/consumers/react-native/test-consumer.sh:19-49,134-198.
    The complete reports, package identities, exact lock blocks, authored inputs, outputs, and negative controls remain in the closeout records.

    The first review imposed arbitrary third-party callers and reconstruction of deleted historical locks as extra closure conditions.
    Its follow-up corrected that scope against the literal acceptance criteria.
    It preserved the original vulnerability demonstrations and historical limits.

    NYC executable JavaScript configurations already execute caller code.
    This closure does not classify that code as safe untrusted data or declare sprintf-js safe for arbitrary callers.
    The benign and malicious explicit-option cases retain the separate fixed-%r SyntaxError.
    That error does not demonstrate attacker-selected numeric precision.

    The original complete audits still exit 1.
    Deleted generated lifecycle locks still prevent exact historical advisory comparison.
    No identical deleted-lock claim or audit suppression follows from this closure.
    No dependency correction occurred, so correction-specific Make gates are not claimed.

    This resolves #328's accepted applicability route. Braces and the Assembler finding remain open in #312 and #330.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdependenciesPull requests that update a dependency filejavascriptPull requests that update javascript code

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions