Repository navigation
Conversation
The target scanner probes from the panel's server, so it cannot tell whether an ISP cuts a name as SNI, and scanning many neighbours from a VPS can be read as network scanning by the host. A new "From my internet" tab in the REALITY target scanner hands out a one-day command (like the clean IP scanner's) that runs a standard library Python script on the admin's phone or PC, with the VPN off: - it reads the certificates of the server's neighbours (its /24, /22, /20, announced prefix or AS), - checks each name the way Xray needs it: TLS 1.3, an X25519-only ClientHello, h2 and a certificate valid for the name, - sends the name as SNI to the inbound's port to see whether the ISP cuts it, and spots an SNI router in front of the port, - scores the targets (open here, same /24 or AS, not a CDN, DNS match, handshake time) and reports them back with the token alone. The panel server makes no outbound probes for this; it only stores the reports (24 h, 50 commands, 20 reports each, 100 targets per report, every field validated). "Use" puts the target and its SNI into the form, and the admin saves as usual.
Picking a target closes the scanner, and the report was lost with it: adding a second target meant a new command and a new scan. The command is kept per inbound in this browser tab until it expires, and the scanner no longer drops its state when it closes.
A panel behind Cloudflare answered the one-day command with 403 (error 1010): Cloudflare refuses Python's default urllib User-Agent. The command now names one; the script's own requests already did.
|
@MHSanaei blocking: the one-day command runs whatever Python the panel URL returns, and on a panel reached over plain HTTP (or through a TLS proxy that sends no Feature: Fits with gaps. This adds a "From my internet" tab to the REALITY target scanner. The panel makes a 24-hour command; the admin runs it with Python on a phone or PC on the users' ISP, with the VPN off. The script scans the server's neighbouring IPs for usable REALITY targets, tests each name as SNI through the inbound's port to see whether the ISP filters it, and posts a ranked list back for the admin to pick from. The panel already has a scanner, but it probes from the server and cannot see ISP SNI filtering, so this capability is new. It has three gaps. On a panel served over HTTP, the command fetches and runs code with no encryption or integrity check, over the exact network the feature treats as hostile. For an inbound deployed to a node, the default server is the master, so the scan and the filter test hit the wrong machine. Picking a target here also leaves the previous target's scan verdict on screen. Blocking — 1 HIGH / 2 MEDIUM / 1 LOW open (4 new), 0 pre-existing Reviewed head: 4472237 Evidence. No linked issue; the claim comes from the title, body and three commits. Base has no device-side scan: the only scanner posts to Design. The script body is fixed at build time and only five values change per command. The trust model could therefore rest on something the admin can check, not only on the transport the browser happened to use. Keep in mind that the person running it is on a network that injects into plaintext traffic. The filter test assumes REALITY passes an unlisted SNI on to its dest, and that the dest then completes a handshake for a name it does not host. I could not verify the first half: Findings HIGH — MEDIUM — MEDIUM — LOW — Coverage
|
…nd's own server The one-day command fetched the finder script and exec'd whatever came back. On a panel reached over plain HTTP that fetch crosses the users' ISP, the network the feature treats as hostile, so anyone on the path could swap in their own code. The script is now fixed when the command is made and the command carries its SHA-256: a script that does not match is not run. - For an inbound deployed to a node, an empty Server now means the node's address, not the master's public IP, so the scan and the SNI filter test hit the machine that serves the inbound. - Picking a target from the finder clears the previous target's scan result, which otherwise kept its verdict and cert chain size on screen for the new target. - The command's base URL honours X-Forwarded-Proto/-Host only from a trusted proxy, like resolveHost, and keeps the port the browser used.
|
Pushed 3875728 for all four findings.
|
# Conflicts: # frontend/src/test/inbound-form-modal.test.tsx
|
Merged current
Verified locally on Windows: Go tests for |
sni_through caught OSError, and ssl.SSLError is one, so a dest refusing an unknown name with a TLS alert was marked filtered. A dest that accepts only its own name then marked every target filtered and made sni_router_check report an SNI router that does not exist, dropping the whole filter result. An alert proves the name crossed the network, so it now counts as through. Reset, timeout, close and a non-TLS answer still count as cut.
|
Merged current Also pushed earlier and not yet mentioned here: cf9ca5b stops reading a dest's TLS alert as an ISP filter. A dest that refused unknown names used to mark every target filtered and made Local: frontend tests, typecheck, lint, build, |
Resolves the conflict with MHSanaei#7025: the uTLS field in the REALITY form keeps main's NativeSelect, and the form test keeps both sides' imports.
Summary
A "From my internet" tab in the REALITY target scanner. It finds targets next to the server and checks them from a device on the users' ISP, including whether the ISP cuts each name as SNI. The panel server sends no probes for it.
Why
The current scanner probes from the panel's server. It can tell whether a target speaks TLS 1.3 + h2 + X25519, but not whether the users' ISP filters the name as SNI, and in filtered networks that is what decides whether a target works. Scanning many neighbours from a VPS can also be read as network scanning by the host (the concern raised on #6950).
How it works
Like the clean IP scanner's full test (#6950), the panel hands out a one-day command. It runs a standard-library Python script (3.8+) on the admin's phone (Termux) or PC, with the VPN off:
The report appears in the dialog by itself. Use puts
targetandserverNamesinto the form, and the admin saves as usual. A report from a VPN or a datacenter network is flagged, because its filter result says nothing about the users.Abuse / safety
realityfinder/run/:token/{script.py,report}routes work only with an unexpired token and can only add a report to that token's job.Type of change
Areas affected
How was this tested?
internal/web/service/reality_finder_test.go: rejected servers (private, IPv6, injection attempt), bad port and scope, the values written into the script, the 24 h expiry, report sanitizing and ordering, the report and command caps, and the User-Agent in the command. I broke each guard once to confirm that a test goes red.frontend/src/test/reality-finder-panel.test.tsx: 443 is used for an inbound behind a local listener, the current target name is sent, a report renders and Use givesname:443+[name], the VPN warning, and the report coming back after the dialog is reopened (not after the command expires). Also mutation-checked.urllibUser-Agent with 403 (error 1010), so the command now sends one.go test ./...,golangci-lint run(0 issues),npm run lint,npm run typecheck, the vitest unit and components projects (1951 tests), andnpm run buildall pass.make genshows no drift, and the docs OpenAPI copy andserver.mdxare regenerated.Breaking changes
None. Existing scanner behavior is unchanged, and no DB or config change is needed.
Checklist
go build ./...and the test suite pass locally.npm run lint,npm run typecheck, andnpm run buildpass.