Skip to content

fix(network): Scala/Rust brace selectors, Kotlin declarations, Dart package:, shell variable paths - #3607

Merged
squid-protocol merged 1 commit into
mainfrom
claude/festive-fermat-kojuy7
Sep 25, 2026
Merged

squid-protocol merged 1 commit into
mainfrom
claude/festive-fermat-kojuy7

Conversation

@squid-protocol

Copy link
Copy Markdown
Owner

Summary

Fixes #3595. Fixes #3596. Fixes #3597. Fixes #3598.

These four import-graph bugs were found by the 20-language accuracy check (#3594).

#3595: brace selectors (Scala, Rust). galaxyscope.extract_raw_imports deleted the braces and then split on commas. So import io.circe.{ Decoder, Json } became io.circe. Decoder plus a bare Json: one never resolved, and the other linked across languages by name. For example, syn's use crate::ty::{ReturnType, Type}; linked Rust to Zig's Type.zig. New _expand_import_selectors distributes the prefix to every selector:

  • nested groups, => / as renames, _ wildcards;
  • Rust's self selector names the prefix itself.

Only a { right after ., :: or \ counts as a group. ${VAR} (shell) and {{ x }} (HTML templates) keep the old comma split exactly.

Scala also gets:

  • resolution by declared name (circe's Error.scala declares DecodingFailure), plus object members;
  • package objects: io.circe.syntax._ resolves to syntax/package.scala;
  • the path-mirror check.

Rust crate:: / self:: / super:: paths name this crate, so they never reach the typosquat radar. Without that, the full paths produced a new false alert: bevy's crate::component::Components "mimics" Component.

#3596: Kotlin.

  • a.b.Util.X (an object member or nested class) is Util's file. The member retry now only lands on a capitalized owner, so java.util.Collections never becomes a lone util.kt.
  • a.b.asName (a top-level function or property) is the one file under …/a/b/ that declares it, found from the functions and classes the scan already extracts.
  • A test's helper of the same name is not what production code imports.

#3597: Dart. package:name/path.dart is the path tail name/lib/path.dart; dart: is the SDK; relative URIs resolve beside the importer.

#3598: shell. source "$VAR/x/y.sh" resolves only by its literal tail after the last expansion. A bare name resolves beside the importer. The stem alone never decides: $rvm_path/scripts/completion is no longer lib/completion.bash, and $XDG_CONFIG_HOME:-$HOME/.config is no longer a config.yml. ~/... is never the repository.

Measured: import_graph_accuracy.py (precision / recall)

language before after
kotlin (kotlinpoet) 100 / 62.1 99.1 / 95.2
dart (dart-lang/http) 99.1 / 80.8 99.1 / 95.3
scala (circe) 99.4 / 33.0 98.2 / 54.5
shell (bash-it) 87.5 / 75.0 100 / 100
rust (serde-rs/json) 94.7 / 100 100 / 100
other 15 unchanged

Baseline regenerated. The small precision dips were checked against source:

  • Kotlin (2 edges): imports written inside a test's multi-line string, kotlin("Example.kt", """ import … """).
  • Scala (9 edges): a Scaladoc example and a build.sbt string (import io.circe._).

Both are the capture-reads-comments-and-strings class (#3600). The engine resolves the captured token correctly: io.circe._ really is the circe package object.

Remaining Scala misses: 381 of 458 import something with no single file.

Remaining Kotlin misses: Kotlin function extraction drops extension functions with a generic receiver (filed separately).

Type of change

  • Bug fix
  • New feature / language support
  • Parsing or engine logic
  • Docs, tooling, or CI only
  • Other

CI checklist

  • ruff-audit and dead-key are clean. mypy's local flags are only the yaml-stub noise, which is the same on main.
  • python -m pytest tests/: 11,150 passed.
  • crucible_check.py --update --yes: both fixtures reblessed. Scope is below.
  • import_graph_accuracy.py --ci: OK, 20 languages.

Golden-master scope

Zero-dependency fixture: 155 differences, 39 of them coordinate or composition-fit ripple.

  • rust (57), scala (11), shell (7): Extracted Dependencies becomes full selector paths, with the edges and exposures that follow.
  • zig (14), go (3), lua (2), nix (2), tcl (1), yaml (1): only incoming-edge fields drop (popularity, direct and total downstream). These were targets of the cross-language bare-selector edges. Verified: zig/zig/Type.zig lost syn's use crate::ty::{ReturnType, Type}.
  • summary: firewall.imports_unknown 11179 → 11162. typosquat_hits is unchanged at 27, and no Alert changes.

The PHP and HTML dependency lists are untouched. An earlier iteration re-split PHP's existing multi-line capture garbage and cut an HTML {{ placeholder }}/path. The selector guard above prevents both, and the PHP capture bug itself is filed separately.

Verification

$ pytest tests/security_auditing/test_import_resolution_by_language_rule.py tests/core_engine/test_extract_raw_imports.py \
         tests/core_engine/test_galaxyscope.py -q        -- 97 passed
$ python tests/tools/import_graph_accuracy.py --ci      -- OK, 20 languages
$ python -m pytest tests/ -n auto                       -- 11150 passed

🤖 Generated with Claude Code

https://claude.ai/code/session_01YAtpBfGGyBGGovitSVaM5Q


Generated by Claude Code

…ackage:, shell variable paths (#3595 #3596 #3597 #3598)

- #3595: `a.b.{C, D => E}` / `a::{b, c::{d}}` distribute their prefix to
  every selector (galaxyscope._expand_import_selectors). The old reading
  deleted the braces and split on commas: `a.b. C` and a bare `D`, which
  never resolved -- or, bare, linked across languages (syn's
  `use crate::ty::{ReturnType, Type}` -> zig's Type.zig). Only a brace
  right after `.`/`::`/`\` is a group; `${VAR}` and `{{ x }}` keep the old
  split. Scala also resolves by declared name, object members, and
  package objects (`io.circe.syntax._` -> syntax/package.scala). Rust
  `crate::`/`self::`/`super::` paths never reach the typosquat radar.
- #3596: Kotlin `a.b.Util.X` is Util's file (member retry, now only onto
  a capitalized owner -- `java.util.X` is never a lone util.kt), and
  `a.b.asName` is the one file under .../a/b/ declaring it (a test's
  helper of the same name is not what production code imports).
- #3597: Dart `package:name/path.dart` is the path tail name/lib/path.dart;
  `dart:` is the SDK; relative URIs resolve beside the importer.
- #3598: shell `source "$VAR/x/y.sh"` resolves only by its literal tail
  (a bare name beside the importer); `~/...` is never the repository.

import_graph_accuracy (precision / recall):
  kotlin 100/62.1 -> 99.1/95.2, dart 99.1/80.8 -> 99.1/95.3,
  scala 99.4/33.0 -> 98.2/54.5, shell 87.5/75.0 -> 100/100,
  rust 94.7/100 -> 100/100; the other 15 unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YAtpBfGGyBGGovitSVaM5Q
@github-actions

Copy link
Copy Markdown
Contributor

🐦‍⬛ Muninn Security Scan

✅ No security issues found.

🐦‍⬛ Powered by Muninn · Skald Lab

@squid-protocol
squid-protocol merged commit 577805e into main Sep 25, 2026
36 checks passed
@squid-protocol
squid-protocol deleted the claude/festive-fermat-kojuy7 branch September 25, 2026 13:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment