Skip to content

fix(coverage): reject package: and dart: URIs outside known roots in Resolver - #2640

Closed
j0xh-sec wants to merge 1 commit into
dart-lang:mainfrom
j0xh-sec:fix/coverage-resolver-containment-package-dart
Closed

j0xh-sec wants to merge 1 commit into
dart-lang:mainfrom
j0xh-sec:fix/coverage-resolver-containment-package-dart

Conversation

@j0xh-sec

@j0xh-sec j0xh-sec commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Completes the containment check added in #2534.

#2534 added _isWithinKnownRoots to the file: branch of Resolver.resolve(), on the basis — stated in its own _knownRoots doc comment — that coverage source entries are untrusted and their resolved contents are read into formatter output. The package: and dart: branches were left without the same check, so they can still resolve outside the known roots.

The gap

Plain .. does not traverse, because Uri.parse performs RFC 3986 dot-segment removal before the resolver sees the path. But Uri.pathSegments percent-decodes after that, so an encoded separator survives as a single segment:

package:foo/..%2f..%2foutside.txt  ->  pathSegments = [foo, ../../outside.txt]

packageName stays foo (the package lookup succeeds), and p.join(packagePath, ...) + resolveSymbolicLinks then walk out of the package root. The same applies to dart:. The resolved path's contents are read by format_coverage --pretty-print and filterIgnored, so wherever coverage JSON crosses a trust boundary this reads files outside the roots.

Change

  • Apply the existing _isWithinKnownRoots check to the package: and dart: branches, mirroring file:.
  • Canonicalize the known roots with resolveSymbolicLinks, since the paths they are compared against are canonicalized the same way. Without this, a root reached through a symlink (a symlinked pub cache, or /var -> /private/var on macOS) would never contain its own files and legitimate resolution would start returning null.

Tests

Three added: a package: encoded-separator escape returns null, a dart: escape returns null, and a package: URI through a symlinked root still resolves. dart analyze, dart format --set-exit-if-changed, and the full coverage suite pass.

Notes

  • Scoped to Resolver. BazelResolver returns relative paths (resolved by the caller against the cwd) rather than paths within these roots, so it is a different shape — happy to follow up on it separately if you'd like.
  • Severity is modest and already bounded by the trust model the fix(coverage): reject file: URIs outside known roots in Resolver #2534 comment describes; this just extends the same check to the remaining branches it logically covers.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates Resolver.resolve to reject package: and dart: URIs that fall outside known roots, and canonicalizes known roots to ensure paths reached through symlinks resolve correctly. The changes are accompanied by comprehensive unit tests. The review feedback suggests a robustness improvement in resolver.dart to fall back to a normalized absolute path when resolving symbolic links for roots, preventing potential mismatches when comparing absolute paths against relative roots.

Comment thread pkgs/coverage/lib/src/resolver.dart Resolved

@liamappelbe liamappelbe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with comments.

Comment thread pkgs/coverage/lib/src/resolver.dart Resolved
Comment thread pkgs/coverage/lib/src/resolver.dart Resolved
@j0xh-sec

j0xh-sec commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Done @liamappelbe — moved symlink resolution and root containment into _resolveLinksWithinKnownRoots() and updated all call sites. Thanks!

@j0xh-sec
j0xh-sec force-pushed the fix/coverage-resolver-containment-package-dart branch from 03c1d1d to 927b7ff Compare October 7, 2026 07:42
@j0xh-sec

j0xh-sec commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Also cleaned up the odd formatter indentation by extracting the known-root initialization into _createKnownRoots(). I squashed the PR down to a single commit as well.

@j0xh-sec
j0xh-sec force-pushed the fix/coverage-resolver-containment-package-dart branch from 927b7ff to 707e908 Compare October 9, 2026 20:12
@j0xh-sec

j0xh-sec commented Oct 9, 2026

Copy link
Copy Markdown
Contributor Author

@liamappelbe Formatting is fixed and the PR is squashed to one commit. Could you
please approve the pending workflows and merge it when ready? Thanks!

@j0xh-sec j0xh-sec closed this by deleting the head repository Oct 28, 2026
@liamappelbe

Copy link
Copy Markdown
Contributor

Hi @j0xh-sec. Sorry for the slow response. I've been on vacation. If you reopen this PR I'd be happy to merge it.

@j0xh-sec

Copy link
Copy Markdown
Contributor Author

The original fork was accidentally deleted, which automatically closed this PR and prevented reopening it.

I've restored the changes in #2764, including the original fix and regression tests. Further updates will be tracked there.

Thanks @liamappelbe for your review !

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants