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 |
Use GitHub's private vulnerability reporting on this repository (Security → Report 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.
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
.dartfile 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.
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.
- 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-oldnext toapp— does not pass. A path that escapes isWriteStatus.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.devand the Flutter SDK.packages/pinpoint/lib/src/write/write.dart(WriteBoundary), asserted inpackages/pinpoint/test/write_boundary_test.dartagainst real symlinks. - A second, weaker check decides whether a handle is drawn at all.
ProjectScopein 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_agentdoes not importdart:ioat 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 thepinpoint.daemonCommandsetting 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.jsonis build-time tooling.
Nothing below is a control you have today.
- Extension unregistration on detach.
dart:developerhas nounregisterExtension, so this needs a different mechanism than the sentence originally implied; the agent currently registers oninitStateand never withdraws. - Inert in release AOT builds. There is no
kReleaseModeguard 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 reachext.pinpoint.*can set it. Moving the confirmation surface to the host is design, not code, today. createdByLocalProjectas 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.
- 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.