Skip to content

Security: Parag0506/pinpoint

Security

SECURITY.md

Security

Status

Pinpoint is an early prototype. There is no release, no published package and no supported version, so there is nothing yet that can carry a security patch. Treat everything below as a statement of intent plus an honest description of the current attack surface.

version supported
main best effort, no guarantees
any release none exist yet

Reporting a vulnerability

Use GitHub's private vulnerability reporting on this repository (SecurityReport a vulnerability). That keeps the report private until there is a fix.

Please do not open a public issue for anything that could be used against someone running the tool. Include what you did, what happened, and the commit SHA you were on.

Expect a first reply within about a week. This is a one-person side project; it is not staffed like a product, and pretending otherwise would be the wrong thing to tell you.

What this tool actually does to your machine

Read this part even if you never report a bug. Pinpoint is not a passive viewer.

  • It writes to your source files. A mouse gesture on the running app turns into an edit applied to a .dart file on disk. There is an undo journal, and the round trip is asserted to be byte-identical, but the write is real and it is not sandboxed.
  • It runs code inside your app process. The in-app agent registers ext.pinpoint.* service extensions and mutates the live render tree to preview a change before it is committed.
  • It drives the VM service. The VM service is authenticated only by a random path token in its URI, and that URI routinely ends up in logs, CI output and bug reports. Anything that can reach the VM service can call the agent's extensions. This is already true of every Flutter debug session; Pinpoint widens what is reachable there.
  • It owns flutter run. In the default configuration Pinpoint launches and owns the Flutter tool process for the target device.

Pinpoint does not send anything off your machine and does not require an account. There is no telemetry — not disabled, not opt-in, not present: no analytics library is depended on and no code anywhere in this repository opens a network connection. The only sockets in the system are the VM service one that every Flutter debug session already has, and the stdio pipes between the editor extension and the daemon.

Boundaries

Split below into what the code does today and what the design commits to but has not built yet, because "some of these are implemented and some are not" is not something a reader can act on. Even the implemented half is a vertical prototype and should not be relied on as a security control.

Implemented, with an assertion behind it

  • The project root is a write boundary. Immediately before any write, the host resolves the target through every symlink on its way (the OS's own resolveSymbolicLinksSync, not a textual normalisation) and requires the resolved path to be contained in the resolved project root by path segment. Containment by segment and not by string prefix, so a sibling copy — app-old next to app — does not pass. A path that escapes is WriteStatus.outsideProject, a hard abort with no confirmation surface anywhere that turns it into a write. The bytes are then written to the resolved path, so the write lands on the file that was checked. Also read-only regardless of the root: .pub-cache, hosted/pub.dev and the Flutter SDK. packages/pinpoint/lib/src/write/write.dart (WriteBoundary), asserted in packages/pinpoint/test/write_boundary_test.dart against real symlinks.
  • A second, weaker check decides whether a handle is drawn at all. ProjectScope in the in-app agent applies the same roots and the same read-only list before offering a handle. It runs in the target app's process, which is exactly why it is not the one above.
  • No ext.pinpoint.* method writes to disk. package:pinpoint_agent does not import dart:io at all. Preview and read only; every write goes through the host-side daemon, which is not exposed on the VM service.
  • The daemon speaks stdio, and nothing else. It binds no port and listens on no socket: the only thing that can call it is the process that spawned it.
  • The extension spawns the daemon without a shell (shell: false), so the pinpoint.daemonCommand setting is a program name and not an arbitrary-command surface.
  • The extension ships no runtime dependencies. Its bundle is its own source plus Node's standard library; everything in extension/package-lock.json is build-time tooling.

Committed to, not built

Nothing below is a control you have today.

  • Extension unregistration on detach. dart:developer has no unregisterExtension, so this needs a different mechanism than the sentence originally implied; the agent currently registers on initState and never withdraws.
  • Inert in release AOT builds. There is no kReleaseMode guard on registration. In a release build there is no VM service to reach the extensions through, but that is the platform's doing and not this code's.
  • Content-addressed injected agent code. The eval-injection route (RULING B Tier 1) does not exist yet; the agent ships as a dev_dependency, which is visible in your lockfile and needs no hash to be auditable.
  • Guardrail confirmations are host-side. They are not. A guardrail is refused by the host (Writer.commit), but the confirmation that clears it arrives from the agent as a flag on a VM-service event, so anything that can reach ext.pinpoint.* can set it. Moving the confirmation surface to the host is design, not code, today.
  • createdByLocalProject as a third filter. Referenced in the design; not consulted anywhere in shipped code.

Loopback is an assumption, not a boundary. Pinpoint does nothing to keep the VM service local — that is the Flutter tool's business. A forwarded or otherwise exposed VM service port is equivalent to handing over the app process, with or without Pinpoint.

The reasoning behind each of these, including the parts that are still open, is in docs/DESIGN.md §17 (threat model), docs/FAILURE-MODES.md and docs/KNOWN-ISSUES.md §26.

Out of scope

  • An attacker who already has code execution as your user. They can edit your source directly.
  • Exposing a VM service port to a network you do not control. That is game over for any Flutter debug session, with or without Pinpoint.
  • Bugs in Flutter, Dart or the VM service itself — report those upstream, though a note here is welcome if Pinpoint makes them worse or easier to hit.

There aren't any published security advisories