netresearch / nr-bug-reporter
TYPO3 backend bug reporter: attribute an error to its originating Composer package and open a prefilled issue on that package's upstream GitHub tracker. Adds an error-page report action and a proactive backend toolbar item.
Package info
github.com/netresearch/t3x-nr-bug-reporter
Type:typo3-cms-extension
pkg:composer/netresearch/nr-bug-reporter
Requires
- php: ^8.2
- typo3/cms-backend: ^13.4 || ^14.3
- typo3/cms-core: ^13.4 || ^14.3
Requires (Dev)
- phpunit/phpunit: ^10.5 || ^11.0 || ^13.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-01 08:44:55 UTC
README
EXT:nr_bug_reporter
A TYPO3 backend extension that turns an error into a prefilled bug report on the originating package's own upstream GitHub tracker — plus a proactive "Report an issue" item in the backend toolbar that captures the current context.
Status: beta / MVP. The attribution engine is unit-tested and both backend features have been verified live in booted TYPO3 14.3.2 (PHP 8.5) and 13.4.30 (PHP 8.3) DDEV instances (see Verification). Not yet hardened for TER release (no functional/E2E test suite, no PHPStan or Rector configuration, and no RST docs yet).
What it does
Two entry points, one engine:
- Error-triggered (developer-facing). In Development application context, an uncaught exception renders the normal TYPO3 debug page with a "Report this bug" banner: the error is attributed to the third-party Composer package that most likely caused it, and the banner links to a prefilled issue on that package's GitHub tracker. If attribution is uncertain or the package is not on GitHub, the banner says so instead of filing a wrong report.
- Proactive (editor/developer-facing). A toolbar item (next to clear-cache/user) opens a dropdown showing the current module/URL, the last captured error (with a one-click GitHub link when one was resolved), and a short trail of recent backend actions. It offers a prefilled report to an admin-configured repository, or copy-to-clipboard text when none is configured.
Both share the attribution engine: map a stack frame to its owning package, resolve that package's upstream GitHub tracker, and gate whether a one-click report is actually appropriate.
Install
composer require netresearch/nr-bug-reporter
- Proactive toolbar appears for any authenticated backend user immediately (registered via DI) — no configuration needed.
- Configure a target repo (optional) for the proactive report under Settings → Extension
Configuration → nr_bug_reporter →
defaultReportRepository(a GitHub repo URL). Without it the toolbar offers copy-to-clipboard. - Error-page "Report this bug" feature — opt-in via
config/system/additional.php. It cannot be enabled from the extension: TYPO3 reads the exception-handler class during early bootstrap, beforeext_localconf.phpruns. Add ONE of:// Development error page: $GLOBALS['TYPO3_CONF_VARS']['SYS']['debugExceptionHandler'] = \Netresearch\NrBugReporter\Error\ReportingExceptionHandler::class; // Production capture (changes production error rendering — opt in deliberately): $GLOBALS['TYPO3_CONF_VARS']['SYS']['productionExceptionHandler'] = \Netresearch\NrBugReporter\Error\ReportingExceptionHandler::class;
Targets TYPO3 13.4 LTS + 14.3 LTS, PHP 8.2–8.5, Composer-mode installs.
How attribution works
Input is the exception's [getFile(), …getTrace()] assembled as {file, class} frames, innermost→
outermost. The engine:
- Resolves each frame to its owning package — class FQCN first (PSR-4 namespace → package), file path second. (Class-first is what makes trait methods resolve to the consuming package.)
- Walks innermost→outermost; the first extension / non-infra-library frame wins (extension = high, library = medium), skipping TYPO3 core, infrastructure libs (symfony/doctrine/psr/…), and the reporter itself. A candidate reached through a PSR-14/PSR-15 dispatcher is demoted to low confidence (a passive listener/middleware is rarely the real culprit).
- Falls back to root-cause frames when the exception was re-wrapped (
$previouschain).
Tracker resolution is a 4-tier chain (composer.json support.issues → support.source/homepage →
composer.lock/installed.json VCS source url → curated map), GitHub-host gated, handling
both https:// and SSH (git@github.com:owner/repo.git) forms.
A ReportPolicy then gates whether to offer an actionable button: it withholds for low/core/none
confidence, traces shorter than 3 frames, and config/author-error exceptions — so robust resolution
cannot amplify an attribution mistake into a harmful public issue.
Validation: the attribution engine
The engine was built spike-first and adversarially tested. The dev harness (CLI, runnable without a TYPO3 boot — see below) reports:
-
bin/run.php— designed corpus + 4-tier resolver chain: 19/19. -
bin/adversarial.php— 20 red-team trace shapes engineered to break attribution (the "before" baseline): 20 findings (the heuristic behaved exactly as an independent red-team predicted, 20/20). -
bin/hardened.php— the same 20 through the hardened heuristic + gating:Outcome n Meaning FIXED 5 right party + right action (the trait cases) SAFE 7 correctly withheld; no upstream report wanted MISSED 4 over-withheld — harm-free gating false-negatives HARMFUL 4 still a one-click report to the wrong party Harmful one-click reports: 20 → 4. The 4 residual map to known-hard cases out of current scope: abstract-base inheritance (PHP reports the defining class — unrecoverable from the trace), blame-the-tool for a non-infra library called directly, the infra-allowlist gap (
firebase/php-jwt), and a trait consumed within one package where the real culprit is an outer caller.
The CLI harness (
bin/,fixtures/) is a local dev tool: it reads a sibling TYPO3 core checkout for a real package index, so it is not portable CI. The portable, self-contained tests live inTests/Unit/(PHPUnit, no TYPO3 runtime required).
Layout
Classes/
Attribution/ PackageIndex, PackageAttributionService, AttributionResult (the engine)
Resolver/ GitHubTrackerResolver, TrackerEndpoint (4-tier tracker chain)
Decision/ ReportPolicy (confidence/config gating)
Service/ PackageIndexProvider (runtime index from Composer)
Capture/ CapturedError, SessionStore (last error + action trail)
Context/ BackendContext, BackendContextCollector (module/route/url)
Report/ IssueUrlComposer (prefilled URL + redaction)
Error/ ReportingExceptionHandler (error-page integration)
Backend/ ReportToolbarItem (proactive toolbar)
Middleware/ ActionTrailMiddleware (records recent actions)
EventListener/ BackendAssetLoader (loads the toolbar JS)
Configuration/ Services.yaml, JavaScriptModules.php, RequestMiddlewares.php, Icons.php
Resources/ Public/JavaScript/report-toolbar.js, Public/Icons/Extension.svg
Tests/Unit/ attribution + ReportPolicy + GitHubTrackerResolver tests (portable, no TYPO3 boot)
bin/, fixtures/ CLI dev/regression harness (local; needs a sibling TYPO3 core checkout)
.github/ CI: PHP lint + PHPUnit on PHP 8.2-8.5 × TYPO3 13.4/14.3, plus security checks
Verification
Verified live in booted DDEV instances — TYPO3 14.3.2 / PHP 8.5 (primary) and 13.4.30 / PHP 8.3, Development context:
- ✅ Installs via Composer and activates cleanly; the DI container compiles (Services.yaml, toolbar autoconfigure, PSR-14 listener, PSR-15 middleware, exception-handler opt-out).
- ✅ Backend loads; the proactive toolbar item renders with its icon, and the dropdown shows the live context (module, URL), the recent-actions trail (the middleware → session works), and the copy-report / GitHub-link action — no console errors.
- ✅ The error-page banner is injected into the debug exception page through the handler, correctly gated (a core-only error shows "no one-click report", not a wrong report).
- ✅ Unit tests pass for the safety-critical pure classes (attribution,
ReportPolicygating,GitHubTrackerResolver4-tier chain); CI workflow runs PHP lint + PHPUnit on PHP 8.2–8.5. - ✅ Every referenced TYPO3 FQCN/signature was verified against TYPO3 13.4/14.3 core source; the toolbar renders on both v13 (Bootstrap dropdown) and v14 (native popover API).
Found and fixed during the live install: the exception handler must be registered in
config/system/additional.php — ext_localconf.php runs after TYPO3 reads the handler class, so a
registration there is silently ignored. (The proactive toolbar is unaffected — it uses DI.)
Known limitations
- Attribution is a heuristic; the 4 residual adversarial cases above can still mis-route. The gate and the (planned) human-confirm step keep the blast radius small.
- The infra skip-list and config-error message patterns are hand-maintained; tune against real traces.
- Abstract-base inheritance attribution is not recoverable from an exception trace alone.
Governance and policies
This extension follows the organisation-wide Netresearch policies:
- Governance: ownership, roles and their responsibilities, how decisions are made and how disagreements are resolved.
- Roadmap: planned and excluded work for the
next twelve months. It applies here because this repository has no
ROADMAP.mdof its own. - Handling of dependency and code analysis findings: which vulnerability, licence and static-analysis findings must be fixed, by when, and how exceptions are recorded.
- Secret management: where CI and release credentials are stored, who may use them, and when they are rotated.
- Access roster: the accounts with admin or write access to this repository.
Checks that run on every pull request in this repository:
.github/workflows/checks.yml: Composer Audit (fails on any advisory for an installed package) and Opengrep SAST, both throughsecurity.ymlofnetresearch/typo3-ci-workflows(which Opengrep findings block a pull request is set by the organisation's static analysis rule); Dependency Review (fails on added dependencies with a vulnerability of severity high or higher); PHP licence check (license-check.yml, fails when a Composer dependency declaresSSPL,BSLorBUSL, or anSSPL-orBUSL-identifier of any version, such asSSPL-1.0orBUSL-1.1;BSL-1.0passes); CodeQL for the workflow files andResources/Public/JavaScript/(it has no PHP analyser); Betterleaks secret scanning; zizmor for the workflow files. Thefuzzjob is called but runs nothing here, as the repository has no fuzz or mutation tests..github/workflows/ci.yml: PHP lint of every PHP file and the unit tests, for PHP 8.2 to 8.5 and TYPO3 13.4 and 14.3. Code style, PHPStan, Rector and functional tests are switched off, as the repository has no configuration for them..github/workflows/harness-verify.yml:scripts/verify-harness.shchecks thatAGENTS.mdanddocs/match the repository.
No exception is recorded: composer.json has no config.audit.ignore entry.
Security
What data the extension collects, where it goes, which credentials it uses (none), who can create a report, and what users can and cannot expect in terms of security is in docs/SECURITY-ASSURANCE.md. Report vulnerabilities privately as described in the organisation's SECURITY.md, not in a public issue. A change that adds or removes a security control updates that document.
License
GPL-2.0-or-later