Skip to content

Add web app and Node.js service technology detection#4

Merged
mariotaku merged 6 commits into
mainfrom
claude/web-app-tech-detection-x71d8v
Jul 6, 2026
Merged

Add web app and Node.js service technology detection#4
mariotaku merged 6 commits into
mainfrom
claude/web-app-tech-detection-x71d8v

Conversation

@mariotaku

Copy link
Copy Markdown
Member

Summary

This PR adds comprehensive technology detection for non-native webOS components (web apps and Node.js services), enabling compatibility verification against firmware runtimes. Web apps are analyzed for frontend frameworks and JavaScript syntax levels; services are scanned for Node.js dependencies.

Key Changes

New Detection Library (common/webdetect)

  • Web app detection (web.rs): Parses HTML DOM (not regex) to extract <script type="module"> flags, Angular ng-version attributes, and remote resource URLs. Tokenizes JavaScript (via ress lexer) to detect ES syntax features (let/const, arrow functions, async/await, optional chaining, etc.) while ignoring false matches in strings/comments. Identifies frameworks via license banners in JS text (React, Vue, Angular, Enact, Enyo, jQuery).
  • Service runtime detection (service.rs): Extracts Node.js dependencies and entry point from package.json.
  • ES level mapping (eslevel.rs): Maps detected syntax features to ES levels (ES5 through ES2021+) and correlates them to minimum Chromium major versions for compatibility checking.

Firmware Runtime Resolution (common/fw/src/runtime.rs)

  • Resolves Node.js and web-engine versions from firmware packages.json
  • Supports multiple package name variants (e.g., webruntime, lib32-webruntime, versioned chromium<NN>)
  • Distinguishes between Chromium and WebKit engines with version parsing

Integration with Verification (common/verify/src/ipk)

  • New VerifyForFirmware trait extends package verification to include non-native compatibility
  • DetectionResult enum wraps web app and service detection with per-firmware verdicts
  • CompatVerdict enum (Ok/Fail/Unknown) represents compatibility outcomes
  • Web app ES level is checked against firmware's web engine Chromium version
  • Services report available Node.js version (informational only; no strict requirement check)

Path Traversal Protection (common/ipk/src/path.rs)

  • Guards untrusted package metadata (app/service IDs, entry points) against directory traversal
  • Lexically normalizes paths without filesystem access before opening files

Output Formatting (packages/ipk-verify)

  • Renders detection results in summary and detailed views
  • Shows per-firmware compatibility table with web engine versions and ES support verdicts
  • Displays framework, dependencies, and syntax features as evidence

Notable Implementation Details

  • Token-stream ES detection: Uses ress lexer to avoid false positives from keywords/operators in strings and comments (e.g., /** as **).
  • Conservative ES level: Represents the minimum engine requirement; unknown levels don't fail the build.
  • Framework precedence: Enact > Enyo > Angular > React > Vue > AngularJs > jQuery when multiple frameworks detected.
  • Semver parsing: Handles Debian upstream version strings (e.g., 120.0.6099.270-137.paparoa.1) by extracting leading three numeric components.
  • Inline script handling: Concatenates inline <script> bodies for ES detection when apps inline all JS into index.html.

https://claude.ai/code/session_01WNiK747P4W8C2M9RRhRFS3

claude added 6 commits July 6, 2026 11:44
Non-native app and service components were previously skipped with
"Skip because this component is not native". Instead, detect and report
their technology and check per-firmware compatibility, mirroring the
native library check.

For web/hosted apps, scan the bundled HTML/JS to identify the frontend
framework (Enact, React, Vue, Angular, jQuery, webOSTV.js) and its
version, and determine the minimum ECMAScript level the shipped bundle
requires. For JS/Node services, read the bundled package.json for the
declared engines.node requirement and dependencies.

Compatibility is checked against each firmware's actual runtime, read
from the packages.json already committed under common/data: the Node.js
version and the web engine. The web engine resolves across generations
and both families -- Chromium (lib32-webruntime / webruntime /
chromiumNN / chromium-webos) and the legacy LG WebKit port
(webkit-starfish). A definitive incompatibility fails the component and
the process exit code; unknown firmware data yields UNKNOWN and never
fails.

New webdetect-lib crate holds the pure text/JSON detectors; fw-lib gains
node_version()/web_engine() accessors; verify-lib gains
Package::verify_for_firmware and DetectionResult/CompatVerdict; the
ipk-verify report replaces the skip line with a per-firmware summary in
all three output formats.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WNiK747P4W8C2M9RRhRFS3
The JS scan misses two compat-relevant signals that live only in
index.html.

Detect <script type="module">: ES modules require Chromium 61, so a
bundle that reads as ES5 but is loaded as a module still needs a modern
engine. Modeled as a new EsFeature::EsModule (ES2018 bucket, which is
verdict-identical to Chromium 61 for the real firmware set), it flows
through the existing ES-level compatibility path with no verify-layer
change.

Detect remote/hosted resources: src/href references to http(s):// or
protocol-relative URLs in index.html are collected as an informational
note ("loads N remote resources", listed under --details). This does not
affect the compatibility verdict or exit code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WNiK747P4W8C2M9RRhRFS3
A .ipk is attacker-controlled. Several path fragments from its metadata
were joined onto the extraction directory and then opened without a
containment check: the app id and service ids from packageinfo.json, and
the main/executable entry from appinfo.json/services.json. A value like
"../../../../etc/passwd" or "/dev/zero" would make the verifier open a
file outside the package (a device read is an easy DoS; tar's unpack_in
only guards the extraction writes, not these later reads).

Add a lexical path-containment guard (ensure_within) in ipk-lib and apply
it at every join site. Traversal is now rejected before the file is
opened, with a clear error, so the malicious path is never read. Also cap
the index.html read in webdetect to MAX_FILE_BYTES so a pathological
entry can't exhaust memory.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WNiK747P4W8C2M9RRhRFS3
Regex scanning could not tell code from strings/comments, forcing
conservative patterns and still misfiring (e.g. a JSDoc /** banner read
as the ** operator). Replace it with lightweight parsing for the two
places accuracy matters:

- JS ES-feature detection now runs over a ress token stream, so keywords
  and operators inside strings, comments and regex literals are ignored.
  ?. and ?? (not single tokens in this lexer) are reconstructed from
  adjacent ?/./? with span adjacency; async is confirmed as a contextual
  keyword (identifier followed by function/(/ident) rather than a
  variable named "async".
- HTML signals (script type=module, remote src/href, Angular ng-version)
  now come from a tl DOM instead of attribute regexes, so body text or
  comments can't false-match.

Framework identity stays on banner/marker scanning (license comments are
unambiguous as substrings). Both crates are light: tl has zero
transitive deps and ress adds only log + unicode-xid. The ES-level
result remains a conservative engine floor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WNiK747P4W8C2M9RRhRFS3
Enact was detected but Enyo -- the legacy LG/webOS framework Enact
descends from, still used by many older webOS TV apps -- was not. Add
FrameworkKind::Enyo, matched from the enyo.* API and its bundle/file
markers (enyo.kind/Control/version/..., @enyo/, enyo.js), with a
best-effort version from `enyo.version = { core: "x.y.z" }`. Enyo ranks
just below Enact in precedence so an Enact bundle (which shares lineage)
is still reported as Enact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WNiK747P4W8C2M9RRhRFS3
Two fixes surfaced by testing against real webosbrew packages (the
Homebrew Channel and its updater):

- Many webOS apps (Enyo/Enact single-file builds) inline all JS into
  index.html instead of shipping separate .js files, so ES-level
  detection returned UNKNOWN. Collect inline <script> bodies from the
  parsed DOM (skipping application/json / importmap) and feed them to the
  token scanner. The Homebrew Channel (an Enyo app) now correctly reads
  as ES5 instead of UNKNOWN.

- Drop all trust in package.json engines.node. webOS services don't
  declare it reliably, so a Node compatibility verdict derived from it is
  meaningless. Services no longer carry a compat verdict or affect the
  exit code; the report shows the service's dependencies plus each
  firmware's bundled Node.js version as information only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WNiK747P4W8C2M9RRhRFS3
@mariotaku mariotaku merged commit d61c1cf into main Jul 6, 2026
1 check passed
@mariotaku mariotaku deleted the claude/web-app-tech-detection-x71d8v branch July 6, 2026 13:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants