Skip to content

OWASP dependency-check (Retire.js) flags every fork of 1.7.3 for CVE-2020-27511, even patched ones — request a tagged 1.7.4 / version bump #368

@danshome

Description

@danshome

Summary

Downstream projects that vendor Prototype.js 1.7.3 are being flagged by
OWASP dependency-check-maven for CVE-2020-27511 even after they
manually patch out the vulnerable String#stripTags and
String#unescapeHTML functions, because OWASP dependency-check identifies
the library by the Version literal in prototype.js (via the bundled
Retire.js signature database) and
the upstream literal still reads Version: '1.7.3'.

This makes the CI / security-scan story very awkward for any project that
ships a fork of prototype.js: the only path to a green dependency-check
build is to either (a) drop Prototype.js entirely, (b) suppress the finding
project-wide (which masks future Prototype-related CVEs as well), or
(c) rewrite the Version literal at build time to deliberately defeat the
Retire.js extractor. We have taken option (c) in
JavaMelody — see the writeup at
the bottom of this issue — but it would be far better to fix this at the
source.

This issue continues the conversation from #355 (CVE-2020-27511) but is
specifically about the scanner-flag problem for downstream forks, not
the underlying ReDoS itself.

How OWASP dependency-check flags this file

OWASP dependency-check-maven ships with Retire.js's signature database. The
entry for prototypejs
declares a vulnerability range of { "below": "1.7.4" } for CVE-2020-27511
and identifies any prototype.js file via these filecontent extractors:

Prototype JavaScript framework, version (§§version§§)
Prototype[ ]?=[ ]?\{[ \r\n\t]*Version:[ ]?(?:'|")(§§version§§)(?:'|")

where §§version§§ expands to the capture group ([0-9][0-9a-z_\.\-]+).

Since 1.7.3 is the final tagged Prototype.js release and has been since
2015-11-18, every fork in existence still ships these literals and is
therefore flagged as vulnerable — even forks that have removed the
vulnerable methods entirely
(so the ReDoS exploit path is unreachable).

Reproducer

  1. Create a Java project with dependency-check-maven configured and a
    single embedded prototype.js resource taken verbatim from the
    master branch of prototypejs/prototype.
  2. Run mvn dependency-check:check.
  3. Observe a HIGH-severity CVE-2020-27511 finding.
  4. Manually delete String.prototype.stripTags and
    String.prototype.unescapeHTML from the file (both the function
    definitions and the entries in the Object.extend(String.prototype, ...)
    hash near the end of that section).
  5. Re-run mvn dependency-check:check.
  6. Expected: no finding (the vulnerable code paths are gone).
    Actual: the HIGH-severity CVE-2020-27511 finding still appears,
    because Retire.js detects the Version: '1.7.3' literal and there is
    no entry above 1.7.4 in the vulnerability range.

What would fix this at the source

Any one of the following would unblock downstream forks:

  1. Tag a 1.7.4 release (no functional changes required) that simply
    bumps the literal Version: '1.7.3' and the header comment to
    1.7.4. The Retire.js comparator would then place patched files
    outside the below: 1.7.4 range. This is the lowest-friction option.
  2. Apply the CVE-2020-27511 patch on the master branch (e.g. remove
    or rewrite stripTags / unescapeHTML) and bump the version
    literal in the same commit, then tag 1.7.4. This also fixes the
    underlying ReDoS for anyone re-vendoring from master.
  3. Officially mark the project as archived / unmaintained with a
    pointer in the README to the recommended migration path. SCA vendors
    typically respect explicit upstream EOL status when triaging.

Of these, (1) is the most pragmatic given the project's release cadence.

What we did in our fork (JavaMelody)

For projects that need a green build today, here is the workaround we
landed in JavaMelody:

  • The vulnerable stripTags and unescapeHTML methods were removed in
    Dec 2023 (commit f5720f99). Neither method is called anywhere in
    JavaMelody, so the CVE's exploit path is not reachable.
  • We rewrite the Version literal at Maven resource-filter time to
    'jm-${project.version}-fork-of-prototypejs-1.7.3'. The leading j
    fails Retire.js's [0-9]-anchored version-extractor regex, so the file
    is no longer identified as a Prototype.js release of any version, while
    the trailing fork-of-prototypejs-1.7.3 substring keeps the upstream
    lineage discoverable for human auditors.
  • We rewrite the file header so it no longer contains the literal
    substring , version (the first Retire.js filecontent extractor).
  • We added a JUnit regression test that fails the build if the Version
    literal ever reverts to 1.7.3, and a prototype.js.CHANGES.md
    documenting the full local diff from upstream.

This works but it is fragile (it relies on the exact shape of Retire.js's
regexes never tightening) and it is not a solution we can recommend to
other downstream projects without effectively asking them to lie to their
SCA tooling. A real fix needs to land upstream.

Happy to send a PR doing option (1) or (2) if that would help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions