- JavaScript 94%
- CSS 2.7%
- HTML 2.3%
- Python 1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| demo | ||
| extension | ||
| tests | ||
| tools | ||
| package.json | ||
| README.md | ||
TNDetector
A first-pass Chrome/Edge extension that finds shipment tracking numbers in JSON responses and displays them on the page. Enable it on a return or shipping site before submitting the form. No Developer Tools window is needed during normal use.
This version uses general field-name and shipment-context rules. It contains no FedEx-specific endpoint or page selectors, and it has not been validated against a live FedEx return workflow.
Install locally
- Open
chrome://extensionsin Chrome oredge://extensionsin Edge. - Turn on Developer mode.
- Choose Load unpacked and select this project's
extensionfolder (the folder containingmanifest.json). - Pin TNDetector to the toolbar.
- Open the return/shipping site, click TNDetector, and choose Enable on this site. Accept the browser's site-access prompt if shown.
- Reload before starting the return workflow, then submit normally. TNDetector also starts listening immediately to new requests on the current page after activation.
When a response contains a candidate, a small panel appears with its tracking number, available carrier/reference, source JSON field, and Copy button. Multiple packages appear separately. Minimize collapses the panel; Clear removes the current tab's results. The toolbar badge shows the result count. Disable a site through the same toolbar popup.
The extension itself requires no build, Node installation, API key, or backend server. Manifest settings target Chrome/Edge 111 or later; use a current browser. Only Chrome for Testing is covered by the automated browser test; Edge needs a manual check.
To share it as a ZIP, run npm run package (Python 3 required). The output is dist/TNDetector-0.1.0.zip; recipients must extract it and load the extracted folder. This is an unpacked prototype, not a signed/store-published extension.
Local demo
With Node.js 20 or later:
cd TNDetector
npm run demo
Open http://127.0.0.1:8765, enable TNDetector there, and reload. The demo includes a nested return response with two packages, a different provider's response through XHR, unrelated IDs, and malformed JSON. Numbers and carrier associations are fictional test data. No shipping or return transaction occurs.
The confirmation-page link demonstrates retaining results through a same-origin navigation. /?early=1 makes a request from the first script in the document to exercise early capture. The demo server only binds to 127.0.0.1.
Detection
Supported examples include:
output.rma.shipments[0].label.trackingNumber
return.shipping_label.tracking_number
data.packages[0].trackingCode
shipment.tracking.number
tracking_numbers[0]
shipment.waybillNumber
- Field spelling is normalized across case, underscores, and hyphens.
- Explicit tracking/waybill fields produce Tracking number entries. Context-dependent fields such as
trackingIdandconsignmentNumberproduce Possible tracking number entries only when shipment context is present. - Bare numbers,
orderNumber,rmaNumber, and analytics tracking IDs do not independently produce results. - Values must contain digits and use a conservative alphanumeric/space/hyphen format, 6–40 characters long. String values preserve leading zeroes. Unsafe JavaScript numeric integers are rejected because their original digits may already have been rounded.
- Carrier, return/order reference, and direction are collected from the same object and its ancestors. Sibling shipments are not searched for context. Carrier identity is never guessed from number format.
- Repeated candidates with the same number, carrier, references, and direction are merged. Different package numbers or references remain separate.
Results are candidates extracted from website data, not carrier-verified shipment records. Unknown naming conventions can be missed, and a misleading field name can cause a false positive. The Source field disclosure helps inspect ambiguous results. Future provider-specific rules can be added to the shared detector after obtaining representative redacted response samples.
Architecture
| File | Responsibility |
|---|---|
extension/manifest.json |
Manifest V3 permissions, popup, and background worker |
extension/site.js |
Host match patterns and early script registration definitions |
extension/background.js |
Permission changes, script registration, per-tab session records, and badge |
extension/detector.js |
JSON traversal, context association, candidate validation, deduplication, and expiry |
extension/capture.js |
Page-world fetch/XHR observation and bounded candidate bridge |
extension/content.js |
Isolated-world bridge validation and shadow-root results panel |
extension/popup.* |
Per-site enable/disable controls |
demo/ |
Local example return site and synthetic responses |
tests/ |
Detector tests and real-extension browser checks |
tools/package.py |
Package only runtime extension files into a ZIP |
After a host permission is granted, two dynamic scripts run at document_start: capture in MAIN, and display in ISOLATED. A handshake queues early detections until the display script is ready. The fetch hook observes a cloned response on a side branch and returns the original fetch promise; the XHR hook observes completion. Neither sends or replays a network request. Removing permission unregisters future scripts, tells current scripts to stop, restores hooks when still owned by TNDetector, and clears that site's session records.
This follows Chrome's content script execution worlds, dynamic script registration, optional permissions, and session storage APIs.
Privacy and bounds
- Access is off by default. A grant covers only the selected hostname and scheme, including all ports on that hostname under Chrome's host-permission model. Other subdomains require separate grants.
- No telemetry, remote services, external code, tracking-link requests, or uploads are included.
- Complete response bodies are processed transiently in the page context. Only validated candidate fields cross into the extension. Request URLs, headers, cookies, full responses, and other fields are not stored.
- Results are scoped to one tab and origin in
chrome.storage.session, not disk-backed local/sync history. They survive same-origin reloads, are excluded after 30 minutes without another matching response, and are cleared on tab closure, an observed cross-origin navigation, or site disable. Expired storage is pruned on the next state check; checks run every 30 seconds in an active injected document. Browser shutdown/extension reload also clears session storage. - Up to 100 records are kept per tab. Each scan produces at most 50 candidates and traverses at most 10,000 objects, 1,000 entries per object, and 32 levels. Capture scans at most 200 parsed responses per minute per document.
- Fetch clone reading is limited to 1 MiB, 10 seconds, and four concurrent reads. XHR text inspection is limited to 1 MiB of characters. Parsed XHR JSON is skipped when its declared content length exceeds 1 MiB; otherwise traversal limits apply without serializing the entire response again.
- Messages from the page are untrusted. Candidate fields are validated again in the isolated script and worker, and displayed with text nodes. A page can nevertheless spoof tracking values or alter/remove the on-page panel; this is not an authenticity or security tool.
First-pass limitations
- Only the top-level document is instrumented. Requests originating independently inside iframes, dedicated/shared/service workers, WebSockets, or EventSource are not captured. A top-page fetch served by a service worker can still be observed if it exposes a normal readable response.
- Only successful responses are inspected. Fetch/text XHR needs a JSON content type; XHR explicitly using
responseType = "json"is also supported. Missing/misleading MIME types, binary/PDF/image labels, JSON embedded only in HTML, and data never sent to the browser are outside this version. - Earlier responses cannot be recovered retroactively. A page that caches or replaces the networking functions before/after installation can bypass observation.
- Capture may skip responses when size, traversal, rate, or concurrency limits are reached. JSON parsing and JavaScript hooks impose some overhead; this is a prototype, not a transparent network-level monitor.
- The panel waits for the first result. An empty page does not prove that no tracking number exists. A page navigation to a different origin starts a fresh result scope.
- Tracking names are currently English aliases. Long/all-letter/non-Latin identifiers and references stored only in sibling metadata objects may require additional rules.
- The Copy button uses the page's clipboard API. If unavailable (for example, on an insecure HTTP site), it selects the number for manual Ctrl/Cmd+C.
- Store upload, branding icons, automatic carrier tracking links, an options dashboard, and live provider compatibility validation are follow-up work.
Checks
Validated locally on 2026-09-18: all 10 detector tests and the real-extension browser scenario passed on Linux with Chrome for Testing 153.0.8010.52, with no page JavaScript errors. Desktop (1440px) and narrow (360px) screenshots were also inspected. Edge, the native permission-confirmation dialog, and live provider workflows have not been tested.
The dependency-free detector checks run with:
npm test
The browser check requires Puppeteer Core 24.43.1 and a recent full Chrome for Testing build supporting extension automation (not chrome-headless-shell):
npm install --no-save --package-lock=false puppeteer-core@24.43.1
CHROME_PATH=/absolute/path/to/chrome npm run test:browser
The test uses a temporary browser profile and localhost server, loads the actual extension, pre-authorizes localhost through Chrome's extension settings, and enables it through its toolbar popup. The native site-access confirmation dialog needs a manual browser check; headless automation cannot accept it. The test checks response integrity, no request replay, fetch and both XHR modes, multiple packages/providers, ignored responses, deduplication, same-origin navigation, responsive rendering, copying, aborted requests, first-script capture, tab isolation, clearing, and live permission removal. Screenshots go to ignored test-results/. PUPPETEER_MODULE can point to an existing Puppeteer Core ESM entry instead of installing a local copy. It never opens a real return/shipping website.