Add Rhino test suite for String email/URL patterns #160

Open
tobi wants to merge 6 commits from test-string-module into main
Owner

Adds a minimal Rhino test suite for String.EMAILPATTERN and String.URLPATTERN — the same patterns that caused the ReDoS in #229.

Approach

A single tests/String.test.js file runs in the plain Rhino shell (no Helma server needed). A one-liner mock for app.addRepository is enough to load the module; the tests then call String.EMAILPATTERN and String.URLPATTERN directly — the same objects isEmail() and isUrl() delegate to.

A new testJs Gradle task runs the suite and is wired into check, so it runs as part of every build:

./gradlew testJs

No new dependencies. The rhino-all JAR already in the build is the only runtime.

Coverage (95 cases)

  • Valid email and URL inputs — Unicode/IDN, IPv4 literals, bare IPv4, punycode, credentials, hyphens, short addresses
  • RFC 5322 atext chars that must still pass in local part: !#$%&'*+/=?^_~
  • Structural failures — no TLD, double @, double dot, trailing dot, no host, no scheme
  • RFC 5321 non-atext chars blocked from local part: [](),;:
  • Null/control bytes — \x00, CR, LF, tab, NBSP in domain and local part
  • Trailing junk — null byte, newline, space after a valid address
  • Scheme confusion attacks — javascript:, data:, vbscript:, file:, uppercase/mixed-case variants
  • Host confusion — backslash-at (evil.com\@good.com), backslash in hostname
  • Invisible and bidi Unicode — U+00AD soft hyphen, U+200B–U+200F zero-width/directional marks, U+202A–U+202E bidi controls, U+2060 word joiner, U+2066–U+2069 bidi isolates
  • ReDoS timing guard — 16-label attack string must complete in under 500 ms
Adds a minimal Rhino test suite for `String.EMAILPATTERN` and `String.URLPATTERN` — the same patterns that caused the ReDoS in #229. ## Approach A single `tests/String.test.js` file runs in the plain Rhino shell (no Helma server needed). A one-liner mock for `app.addRepository` is enough to load the module; the tests then call `String.EMAILPATTERN` and `String.URLPATTERN` directly — the same objects `isEmail()` and `isUrl()` delegate to. A new `testJs` Gradle task runs the suite and is wired into `check`, so it runs as part of every build: ``` ./gradlew testJs ``` No new dependencies. The `rhino-all` JAR already in the build is the only runtime. ## Coverage (95 cases) - Valid email and URL inputs — Unicode/IDN, IPv4 literals, bare IPv4, punycode, credentials, hyphens, short addresses - RFC 5322 atext chars that must still pass in local part: `!#$%&'*+/=?^_~` - Structural failures — no TLD, double `@`, double dot, trailing dot, no host, no scheme - RFC 5321 non-atext chars blocked from local part: `[](),;:` - Null/control bytes — `\x00`, CR, LF, tab, NBSP in domain and local part - Trailing junk — null byte, newline, space after a valid address - Scheme confusion attacks — `javascript:`, `data:`, `vbscript:`, `file:`, uppercase/mixed-case variants - Host confusion — backslash-at (`evil.com\@good.com`), backslash in hostname - Invisible and bidi Unicode — U+00AD soft hyphen, U+200B–U+200F zero-width/directional marks, U+202A–U+202E bidi controls, U+2060 word joiner, U+2066–U+2069 bidi isolates - ReDoS timing guard — 16-label attack string must complete in under 500 ms
Add Rhino test suite for String email/URL patterns
All checks were successful
Build / build (push) Successful in 10s
cd1bf212b0
A minimal test runner in plain Rhino JavaScript — no test framework,
no extra dependencies beyond the rhino-all JAR already in the build.
A thin app.addRepository mock lets the module load outside a running
Helma server; the tests then call String.EMAILPATTERN / String.URLPATTERN
directly (the same objects isEmail() and isUrl() use).

Adds a testJs Gradle task wired into check so it runs as part of
every build. Covers 95 cases across:
- valid email and URL inputs (including Unicode/IDN, IPv4, punycode)
- structural failures (no TLD, double @, trailing dot, …)
- RFC 5321 non-atext chars blocked from local part ([](),;:)
- RFC 5322 atext chars that must still pass (!#$%&'*+/=?^_~)
- null/control bytes (\x00, CR, LF, tab, NBSP)
- invisible and bidi Unicode (U+00AD, U+200B–U+200F, U+202A–U+202E, U+2060, U+2066–U+2069)
- scheme confusion attacks (javascript:, data:, vbscript:, file:)
- host confusion (backslash-at, backslash in host)
- ReDoS timing guard (16-label attack must complete in under 500ms)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mock Object.prototype.dontEnum (a Helma runtime extension) so the module
loads outside a running server. Resolve the modules path relative to cwd
when run via Gradle, or fall back to /home/helma for direct java invocation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tobi force-pushed test-string-module from 07af6d598c to 98510eb147 2026-07-03 22:05:14 +00:00 Compare
This pull request can be merged automatically.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin test-string-module:test-string-module
git switch test-string-module

Merge

Merge the changes and update on Forgejo.
git switch main
git merge --no-ff test-string-module
git switch test-string-module
git rebase main
git switch main
git merge --ff-only test-string-module
git switch test-string-module
git rebase main
git switch main
git merge --no-ff test-string-module
git switch main
git merge --squash test-string-module
git switch main
git merge --ff-only test-string-module
git switch main
git merge test-string-module
git push origin main
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
antville/helma!160
No description provided.