Documentation
¶
Overview ¶
Package t3finger fingerprints a TYPO3 CMS website without authentication: it enumerates the installed extensions ("plugins") by abusing the deterministic Composer-mode asset path, and detects the core version by hashing the static assets TYPO3 ships and matching them against a database built from official releases.
Everything here is reachable pre-auth over plain HTTP and is intended for authorized security testing and asset inventory only.
Index ¶
- Variables
- func AssetURL(base, pkg string) string
- func BranchOf(version string) string
- func CompareVersions(a, b string) int
- func DefaultExtensionKeys() []string
- func DefaultExtensionList() []string
- func FetchAdvisoriesFor(ctx context.Context, packages []string) (map[string]*AdvisoryDB, error)
- func FetchExtensionKeys(ctx context.Context) ([]string, error)
- func FetchExtensionList(ctx context.Context) ([]string, error)
- func LoadExtensionKeys(path string) ([]string, error)
- func LoadExtensionList(path string) ([]string, error)
- func SeedExtensionKeys() []string
- func SortBySeverity(advs []Advisory)
- type Advisory
- type AdvisoryDB
- type Baseline
- type Builder
- type CatMeta
- type DB
- func (d *DB) AllPaths() []string
- func (d *DB) DiscriminatingPaths() []string
- func (d *DB) DiscriminatingPathsRanked() []string
- func (d *DB) DiscriminatingUnderRanked(prefix string) []string
- func (d *DB) Empty() bool
- func (d *DB) Has(v string) bool
- func (d *DB) Merge(src *DB)
- func (d *DB) Newest() string
- func (d *DB) PresenceDiscriminatingPaths() []string
- func (d *DB) VersionsForAnyHash(md5hex string) []string
- func (d *DB) VersionsForHash(servedPath, md5hex string) []string
- func (d *DB) VersionsHavingFile(servedPath string) []string
- type ExtBuilder
- func (b *ExtBuilder) BuildExtProbeDB(ctx context.Context, keys []string, stamp string) (*ExtProbeDB, error)
- func (b *ExtBuilder) CatalogueAuthors(ctx context.Context) (map[string]CatMeta, error)
- func (b *ExtBuilder) UpdateExtDB(ctx context.Context, existing *ExtProbeDB, keys []string, stamp string) (*ExtProbeDB, UpdateStats, error)
- type ExtEntry
- type ExtProbe
- type ExtProbeDB
- func (d *ExtProbeDB) ByComposer(name string) *ExtEntry
- func (d *ExtProbeDB) CandidatesForComposer(name string) []string
- func (d *ExtProbeDB) ComposerForAssetHash(md5hex string) string
- func (d *ExtProbeDB) Empty() bool
- func (d *ExtProbeDB) IdentifyAssetHash(md5hex string) (key, composer string, ok bool)
- func (d *ExtProbeDB) KeyForComposer(name string) string
- func (d *ExtProbeDB) Keys() []string
- type ExtResult
- type Extension
- type FileProbe
- type Finding
- type Fingerprinter
- func (f *Fingerprinter) AnnotateExtensionCVEs(ctx context.Context, res *ExtResult) error
- func (f *Fingerprinter) Detect(ctx context.Context, rawURL string) (*VersionResult, error)
- func (f *Fingerprinter) DetectMode(ctx context.Context, target string) (Mode, error)
- func (f *Fingerprinter) EnumerateExtensions(ctx context.Context, target string, packages []string, ...) (*ExtResult, error)
- func (f *Fingerprinter) EnumerateExtensionsLegacy(ctx context.Context, target string, keys []string, ...) (*ExtResult, error)
- func (f *Fingerprinter) PassiveExtensions(ctx context.Context, target string) (*ExtResult, error)
- func (f *Fingerprinter) Reachability(ctx context.Context, target string) *Reachability
- func (f *Fingerprinter) RefreshExtensionLatestLive(ctx context.Context, res *ExtResult)
- type Mode
- type Option
- type Reachability
- type Release
- type Releases
- type UpdateStats
- type VersionResult
Constants ¶
This section is empty.
Variables ¶
var DefaultProbes = []string{
"typo3/sysext/backend/Resources/Public/Css/backend.css",
"typo3/sysext/core/Resources/Public/Icons/T3Icons/icons.json",
"typo3/sysext/rte_ckeditor/Resources/Public/Contrib/@ckeditor/ckeditor5-core.js",
"typo3/sysext/rte_ckeditor/Resources/Public/Contrib/ckeditor5-bundle.js",
"typo3/sysext/backend/Resources/Public/JavaScript/date-time-picker.js",
"typo3/sysext/backend/Resources/Public/JavaScript/modal.js",
"typo3/sysext/backend/Resources/Public/JavaScript/module-menu.js",
"typo3/sysext/backend/Resources/Public/JavaScript/Modal.js",
"typo3/sysext/backend/Resources/Public/JavaScript/ModuleMenu.js",
"typo3/sysext/backend/Resources/Public/JavaScript/FormEngine.js",
"typo3/sysext/backend/Resources/Public/JavaScript/LoginRefresh.js",
"typo3/sysext/core/Resources/Public/JavaScript/Contrib/es-module-shims.js",
"typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js",
"typo3/sysext/install/Resources/Public/JavaScript/Install.js",
"typo3/sysext/t3skin/stylesheets/visual/element_message.css",
"typo3/cli_dispatch.phpsh",
}
DefaultProbes is the curated set of publicly-served static core files we hash for version detection in LEGACY (non-composer) installs. Paths are relative to the TYPO3 web root (i.e. what a browser requests). They were chosen for two properties: they exist across many releases, and their content changes between releases (often per-patch), so their md5 pins the version tightly.
Location naming changed across majors (CamelCase JS in ≤11, kebab-case ES modules in 12+), so both spellings are listed; a 404 on the wrong one is harmless. The engine also auto-probes any additional discriminating path the embedded DB knows about, so a fuller DB extends coverage without code changes.
var HashFull = false
HashFull, when set on a build, hashes the entire Resources/Public tree (deep). Off by default: only depth-1 public files are hashed, which keeps the embedded DB compact while retaining the high-signal, per-patch-changing files (backend.css, top-level backend JS, icons.json, the ckeditor bundle).
Functions ¶
func AssetURL ¶
AssetURL returns the deterministic /_assets/<md5>/ URL for a package. A bare Packagist name ("vendor/pkg") is wrapped as "/vendor/<name>/"; a value that already starts with "/" is hashed verbatim (with an enforced trailing slash).
func CompareVersions ¶
CompareVersions compares "a.b.c" strings numerically. Missing parts = 0.
func DefaultExtensionKeys ¶
func DefaultExtensionKeys() []string
DefaultExtensionKeys returns the bundled TER extension keys (folder names), used for legacy-mode /typo3conf/ext/<key>/ enumeration. Refresh it with `t3scan buildwordlist -keys`.
func DefaultExtensionList ¶
func DefaultExtensionList() []string
DefaultExtensionList returns the bundled Packagist TYPO3-extension names (vendor/package), used for Composer-mode /_assets/ enumeration. Refresh it with `t3scan buildwordlist`.
func FetchAdvisoriesFor ¶
FetchAdvisoriesFor queries the Packagist advisory feed for the given packages and returns advisoryDBs keyed by package name. Used to map enumerated extensions (with a composer name + version) to their known CVEs.
func FetchExtensionKeys ¶
FetchExtensionKeys downloads the TER catalogue and returns every unique extension key. Used by `t3scan buildwordlist -keys` and for legacy-mode enumeration, where extensions live at /typo3conf/ext/<key>/ rather than the Composer-mode /_assets/<md5>/ path.
func FetchExtensionList ¶
FetchExtensionList downloads the complete, current list of TYPO3 extension package names from Packagist. Used by `t3scan buildwordlist` to refresh the bundled default.
func LoadExtensionKeys ¶
LoadExtensionKeys reads extension keys from a file, or returns the embedded default when path is empty.
func LoadExtensionList ¶
LoadExtensionList reads candidate packages from a file (one per line, '#' comments allowed). If path is empty, the embedded default list is returned.
func SeedExtensionKeys ¶
func SeedExtensionKeys() []string
SeedExtensionKeys returns the curated seed list of common extension keys used by `t3scan buildextdb` to build the default extension-probe DB.
func SortBySeverity ¶
func SortBySeverity(advs []Advisory)
SortBySeverity orders advisories most-severe first, newest ID breaking ties.
Types ¶
type Advisory ¶
type Advisory struct {
ID string `json:"id"` // TYPO3-CORE-SA-YYYY-NNN
CVE string `json:"cve"` // CVE-YYYY-NNNNN (may be empty)
Title string `json:"title"` //
Affected string `json:"affected"` // composer constraint, e.g. ">=11.0.0,<11.5.51|>=12.0.0,<12.4.46"
Link string `json:"link"` //
Severity string `json:"severity"` // may be empty
}
Advisory is one published TYPO3 core security advisory.
type AdvisoryDB ¶
type AdvisoryDB struct {
Package string `json:"package"`
Advisories []Advisory `json:"advisories"`
}
AdvisoryDB is the embedded set of core advisories.
func FetchAdvisories ¶
func FetchAdvisories(ctx context.Context) (*AdvisoryDB, error)
FetchAdvisories downloads the current TYPO3 core advisories from Packagist. Used by `t3scan buildadvisories` to refresh the embedded set.
func LoadAdvisories ¶
func LoadAdvisories() *AdvisoryDB
LoadAdvisories returns the advisories compiled into the binary.
func (*AdvisoryDB) For ¶
func (db *AdvisoryDB) For(v string) []Advisory
For returns every advisory that affects version v, most severe first.
type Baseline ¶
Baseline records what "not installed" looks like, calibrated per target instead of assuming 404 (some setups answer differently).
type Builder ¶
Builder constructs a version-fingerprint DB by downloading official TYPO3 release tarballs and hashing the static files they serve.
func (*Builder) Build ¶
Build downloads each version, hashes its public static files, and assembles a DB. Files whose content is identical across every version are pruned (they carry no signal); everything else is kept for path-based (legacy) and content-based (composer) matching.
func (*Builder) ListReleases ¶
ListReleases returns every public (non-ELTS) release version, newest first is not guaranteed — callers should sort. ELTS releases are gated behind a paywall and cannot be downloaded, so they are skipped.
type CatMeta ¶
type CatMeta struct {
Author string // authorname (fall back to authorcompany)
Owner string // ownerusername — the TER account that owns the key
}
CatMeta is the catalogue metadata for one extension key (identity that lives in the TER index itself, so it is fetched without any per-version download).
type DB ¶
type DB struct {
// Files: servedPath -> md5 -> []version
Files map[string]map[string][]string `json:"files"`
// Versions is the full ordered list of versions covered by the DB.
Versions []string `json:"versions"`
// BuiltAt is a free-form build stamp (set by `t3scan builddb`).
BuiltAt string `json:"builtAt,omitempty"`
// contains filtered or unexported fields
}
DB is the version-fingerprint database. For each publicly-served asset path, it maps the file's md5 hex digest to the list of TYPO3 core versions that ship that exact byte content. Files whose content varies across releases are the discriminators; identical-everywhere files still confirm a stock install.
func LoadEmbedded ¶
LoadEmbedded returns the database compiled into the binary.
func (*DB) DiscriminatingPaths ¶
DiscriminatingPaths returns served paths whose content varies across versions (more than one hash bucket) — the files worth probing because they narrow the version. Sorted for determinism.
func (*DB) DiscriminatingPathsRanked ¶
DiscriminatingPathsRanked returns discriminating paths ordered by how much they narrow the version (most hash-buckets first), so a bounded probe budget spends on the files that actually change most per patch — not an alphabetical slice. Ties broken by shortest path (stable top-level assets).
func (*DB) DiscriminatingUnderRanked ¶
DiscriminatingUnderRanked returns the discriminating paths that start with prefix (a sysext path like "typo3/sysext/backend/"), ranked by how much each narrows the version — for active probing of one package's files.
func (*DB) Merge ¶
Merge folds src into d: every (path, md5) → version mapping from src is added, version lists are de-duplicated and sorted, and the covered-version set is unioned. Used for incremental DB builds.
func (*DB) PresenceDiscriminatingPaths ¶
PresenceDiscriminatingPaths returns paths that exist in only SOME covered versions (added or removed across releases) — their mere presence/absence bounds the version even when their content never varies. These complement the content-discriminating paths for add/remove-boundary narrowing.
func (*DB) VersionsForAnyHash ¶
VersionsForAnyHash returns versions that ship a file — at ANY path — whose content md5 equals md5hex. Used when the request path can't be mapped to a DB path (composer mode). Unexported cache is built on first use.
func (*DB) VersionsForHash ¶
VersionsForHash returns the versions matching a (servedPath, md5) pair.
func (*DB) VersionsHavingFile ¶
VersionsHavingFile returns the union of all versions in which servedPath exists (any content). Used for presence-based narrowing.
type ExtBuilder ¶
type ExtBuilder struct {
Concurrency int
MaxVersions int // most-recent versions to hash per extension (0 = all)
Progress func(string)
HTTP *http.Client
}
ExtBuilder builds the extension DB by downloading real extension packages from the TYPO3 Extension Repository (TER) and recording, per extension: its dependencies, presence-probe files, and the content hash of every web-servable static file across versions (the per-version diff used to fingerprint the installed version of a plugin/theme).
func NewExtBuilder ¶
func NewExtBuilder() *ExtBuilder
NewExtBuilder returns an ExtBuilder with sane defaults. The HTTP client uses a large idle-connection pool so high -c concurrency reuses keep-alive connections instead of churning a new TCP+TLS handshake per download.
func (*ExtBuilder) BuildExtProbeDB ¶
func (b *ExtBuilder) BuildExtProbeDB(ctx context.Context, keys []string, stamp string) (*ExtProbeDB, error)
BuildExtProbeDB builds full records for the given extension keys.
func (*ExtBuilder) CatalogueAuthors ¶
CatalogueAuthors downloads the TER index and returns key -> author/owner meta. Used to enrich an existing DB without re-downloading any package.
func (*ExtBuilder) UpdateExtDB ¶
func (b *ExtBuilder) UpdateExtDB(ctx context.Context, existing *ExtProbeDB, keys []string, stamp string) (*ExtProbeDB, UpdateStats, error)
UpdateExtDB brings an extension DB up to date with the live TER catalogue, downloading ONLY the (extension, version) pairs it does not already have:
- a plugin with a new release -> just its new versions are fetched & merged
- a brand-new plugin -> built from scratch
- an unchanged plugin -> skipped entirely (zero downloads)
Pass existing == nil (or an empty DB) for a full build. The returned DB is the UNPRUNED working "raw" DB (every hashed public file, so the next update stays correct); call PruneForEmbed on it to get the compact DB embedded in the binary. New CVEs are orthogonal — they live in the advisory DB and are refreshed with buildadvisories, not here.
Downloads run through a SINGLE global worker pool over every (extension, version) pair, so Concurrency requests are always in flight regardless of how the versions are distributed across extensions. This matters because each TER zip is tiny but costs a full request round-trip: the build is latency-bound, so flat global concurrency — not per-extension goroutines — is what makes it fast and removes the long tail of version-heavy plugins.
type ExtEntry ¶
type ExtEntry struct {
Composer string `json:"composer,omitempty"`
Author string `json:"author,omitempty"` // authorname from the TER index
Owner string `json:"owner,omitempty"` // ownerusername (TER account owning the key)
Latest string `json:"latest,omitempty"`
Versions []string `json:"versions,omitempty"`
Requires map[string]string `json:"requires,omitempty"`
Probes []string `json:"probes"`
Files map[string]map[string][]string `json:"files,omitempty"`
// contains filtered or unexported fields
}
ExtEntry is the full record for one extension (a plugin, a theme/sitepackage, or any TER package — themes are just extensions):
- Composer/Latest/Versions: identity and coverage.
- Requires: its declared dependencies (from composer.json).
- Probes: web-servable files that prove it is installed (presence).
- Files: for each web-servable path, md5 -> versions that ship that exact content — the per-version static-file diff used to pin the INSTALLED version of the extension by hashing what the target serves.
func (*ExtEntry) AnyHash ¶
AnyHash returns versions of this extension shipping a file (any path) with the given content md5 — for when the served path can't be mapped exactly.
func (*ExtEntry) DiscriminatingFiles ¶
DiscriminatingFiles returns the paths whose content varies across versions — the files worth hashing on a live target to pin the extension's version.
func (*ExtEntry) DiscriminatingFilesRanked ¶
DiscriminatingFilesRanked returns discriminating paths ordered by how much they narrow the version — most hash-buckets (changes most often) first, so probing a few pins the version tightly. Ties broken by shortest path (stable, usually top-level assets that are reliably served).
func (*ExtEntry) VersionForHash ¶
VersionForHash returns the versions of extension `key` whose file `path` has content md5 == md5hex (exact per-version static-file match).
type ExtProbe ¶
type ExtProbe = ExtEntry
ExtProbe is the legacy alias kept for the builder's simpler output; the rich entry supersedes it. (Files/Requires may be empty for breadth-only builds.)
type ExtProbeDB ¶
type ExtProbeDB struct {
BuiltAt string `json:"builtAt,omitempty"`
Extensions map[string]ExtEntry `json:"extensions"`
// contains filtered or unexported fields
}
ExtProbeDB maps extension key -> its full record.
func LoadExtProbeDB ¶
func LoadExtProbeDB() *ExtProbeDB
LoadExtProbeDB returns the embedded extension DB (decompressing it).
func LoadExtProbeDBFile ¶
func LoadExtProbeDBFile(path string) (*ExtProbeDB, error)
LoadExtProbeDBFile loads a DB from a file path (json or json.gz).
func ParseExtProbeDB ¶
func ParseExtProbeDB(b []byte) (*ExtProbeDB, error)
ParseExtProbeDB reads a DB from JSON or gzipped-JSON bytes.
func PruneForEmbed ¶
func PruneForEmbed(raw *ExtProbeDB) *ExtProbeDB
PruneForEmbed returns a compact copy of a raw DB with version-invariant files dropped — the form embedded in the binary. The input is left untouched.
func (*ExtProbeDB) ByComposer ¶
func (d *ExtProbeDB) ByComposer(name string) *ExtEntry
ByComposer returns the entry for a Packagist name (vendor/pkg), or nil. Used to version-fingerprint a composer-mode extension whose key we don't have.
func (*ExtProbeDB) CandidatesForComposer ¶
func (d *ExtProbeDB) CandidatesForComposer(name string) []string
CandidatesForComposer returns every extension key that declares the given composer name — more than one when forks/dummies squat the same name. Ordered canonical-first (most versions). Empty if the name is unknown.
func (*ExtProbeDB) ComposerForAssetHash ¶
func (d *ExtProbeDB) ComposerForAssetHash(md5hex string) string
ComposerForAssetHash returns the composer name behind a /_assets/<md5>/ hash.
func (*ExtProbeDB) Empty ¶
func (d *ExtProbeDB) Empty() bool
Empty reports whether the DB has no entries.
func (*ExtProbeDB) IdentifyAssetHash ¶
func (d *ExtProbeDB) IdentifyAssetHash(md5hex string) (key, composer string, ok bool)
IdentifyAssetHash reverses a composer-mode /_assets/<md5>/ directory hash back to the installed extension it belongs to, using a precomputed table of md5("/vendor/<composer>/") over the whole catalogue. This turns a hash already present in the target's HTML into a certain, zero-extra-request identification (the site is literally serving that package's asset). ok=false if unknown.
func (*ExtProbeDB) KeyForComposer ¶
func (d *ExtProbeDB) KeyForComposer(name string) string
KeyForComposer returns the TER extension key for a Packagist name, or "".
func (*ExtProbeDB) Keys ¶
func (d *ExtProbeDB) Keys() []string
Keys returns the extension keys in the DB, sorted.
type ExtResult ¶
type ExtResult struct {
Target string `json:"target"`
Probed int `json:"probed"`
Errors int `json:"errors"`
Baseline Baseline `json:"baseline"`
Extensions []Extension `json:"extensions"`
NotEnumerable bool `json:"notEnumerable"` // control probe itself looked like a hit
Blocked bool `json:"blocked,omitempty"` // IP ban / WAF / rate-limit detected
BlockReason string `json:"blockReason,omitempty"` // what was observed
Notes []string `json:"notes,omitempty"`
}
ExtResult is the full extension-enumeration report.
type Extension ¶
type Extension struct {
Package string `json:"package"` // vendor/name (composer) or ext key (legacy)
Confirmed bool `json:"confirmed"` // a subpath under it also responded
Evidence string `json:"evidence,omitempty"` // the subpath/URL that confirmed it
AssetURL string `json:"assetUrl,omitempty"` // the /_assets/<md5>/ URL that hit (composer)
Location string `json:"location,omitempty"` // where it was found (legacy path)
Status int `json:"status"` // HTTP status of the hit
// Version and VersionSource are populated by legacy enumeration when a
// version-bearing file (composer.json, ext_emconf.php, …) is web-readable.
Version string `json:"version,omitempty"`
VersionSource string `json:"versionSource,omitempty"`
// ComposerName is the Packagist name (vendor/pkg) read from composer.json,
// used to look the extension up in the advisory feed.
ComposerName string `json:"composerName,omitempty"`
// VersionSources lists how the version was determined ("ext_emconf.php",
// "static-file hash", …) when more than one method agreed.
VersionExact bool `json:"versionExact,omitempty"` // pinned by static-file hash
// VersionCandidates is the full candidate set when Version is a range (from
// static-file hashing) — used for accurate per-candidate CVE assessment.
VersionCandidates []string `json:"versionCandidates,omitempty"`
// Key is the TER extension key (differs from the composer name; unique even
// when several keys share a composer name). Drives the canonical Link.
Key string `json:"key,omitempty"`
// Author / Owner identify who ships this extension — the disambiguator when
// several extensions declare the same composer name.
Author string `json:"author,omitempty"`
Owner string `json:"owner,omitempty"`
// Link is the canonical TER extension page (by key).
Link string `json:"link,omitempty"`
// Ambiguous is set when this is one of several extensions that share the
// asset path (same composer name) and the served files couldn't single one
// out — only one of the Ambiguous siblings is actually installed.
Ambiguous bool `json:"ambiguous,omitempty"`
// Latest is the newest published version of this extension: from the DB
// snapshot (built at DB-build time) or refreshed live with --live-versions.
Latest string `json:"latest,omitempty"`
// LatestSource is "db" (embedded snapshot) or "live" (queried at scan time).
LatestSource string `json:"latestSource,omitempty"`
// Outdated is true when the detected version is behind Latest.
Outdated bool `json:"outdated,omitempty"`
// Requires is the extension's declared dependencies (from the probe DB).
Requires map[string]string `json:"requires,omitempty"`
// Vulns lists known advisories affecting this extension version (with -cve).
Vulns []Advisory `json:"vulnerabilities,omitempty"`
// VulnsPossible lists advisories for the package when the version is unknown
// (composer-mode enumeration) — the extension has known CVEs, but whether
// this install is affected depends on its (unreadable) version.
VulnsPossible []Advisory `json:"vulnerabilitiesPossible,omitempty"`
}
Extension is one enumerated package result.
type FileProbe ¶
type FileProbe struct {
Path string `json:"path"`
Status int `json:"status"`
MD5 string `json:"md5,omitempty"`
Size int `json:"size,omitempty"`
Matched bool `json:"matched"`
Versions []string `json:"versions,omitempty"`
ByPath bool `json:"byPath,omitempty"` // matched on exact path (legacy) vs content-only
}
FileProbe is the outcome of hashing one served file.
type Finding ¶
type Finding struct {
Kind string `json:"kind"` // install-tool | debug-mode | sitemap | host-header | error-page
Severity string `json:"severity"` // info | low | medium | high
Title string `json:"title"`
Detail string `json:"detail,omitempty"`
URL string `json:"url,omitempty"`
}
Finding is a security-relevant pre-auth observation.
type Fingerprinter ¶
type Fingerprinter struct {
HTTP *http.Client
DB *DB
Advisories *AdvisoryDB
ExtProbes *ExtProbeDB
Releases *Releases
UserAgent string
// Insecure skips TLS certificate verification when true.
Insecure bool
// Proxy, when set, routes all HTTP(S) through this proxy URL. Supports
// http://, https:// and socks5:// schemes (e.g. Burp on
// http://127.0.0.1:8080, or socks5://127.0.0.1:9050 for Tor).
Proxy string
// ProbeConcurrency bounds concurrent HTTP requests (default 16).
ProbeConcurrency int
// Rate caps requests per second across all workers (0 = unlimited).
Rate float64
// contains filtered or unexported fields
}
Fingerprinter is the shared client for both extension enumeration and version detection against a TYPO3 target.
func New ¶
func New(opts ...Option) (*Fingerprinter, error)
New builds a Fingerprinter. By default it loads the embedded version DB; a missing/empty DB is tolerated (extension enumeration needs no DB).
func (*Fingerprinter) AnnotateExtensionCVEs ¶
func (f *Fingerprinter) AnnotateExtensionCVEs(ctx context.Context, res *ExtResult) error
AnnotateExtensionCVEs looks up known advisories for every enumerated extension that has both a Packagist name and a version, and fills in Extension.Vulns. It performs a single batched request to the advisory feed.
func (*Fingerprinter) Detect ¶
func (f *Fingerprinter) Detect(ctx context.Context, rawURL string) (*VersionResult, error)
Detect fingerprints the TYPO3 version of rawURL.
func (*Fingerprinter) DetectMode ¶
DetectMode does a light identify pass and reports the install layout, so the extension enumerator can pick the right technique (Composer /_assets/ vs legacy /typo3conf/ext/). Returns ModeUnknown if it can't tell.
func (*Fingerprinter) EnumerateExtensions ¶
func (f *Fingerprinter) EnumerateExtensions(ctx context.Context, target string, packages []string, progress func(done, total int)) (*ExtResult, error)
EnumerateExtensions probes each candidate package against the target and returns the ones whose asset directory responds differently from a known-absent control. Progress, if non-nil, is called with (done, total).
func (*Fingerprinter) EnumerateExtensionsLegacy ¶
func (f *Fingerprinter) EnumerateExtensionsLegacy(ctx context.Context, target string, keys []string, progress func(done, total int)) (*ExtResult, error)
EnumerateExtensionsLegacy detects each extension by requesting a file it actually ships (from the probe DB, or generic markers) under the legacy roots. A 200 ⇒ installed. Versions come from the probe DB and/or ext_emconf.php.
func (*Fingerprinter) PassiveExtensions ¶
PassiveExtensions finds installed extensions WITHOUT brute force, from signals the target already exposes:
- composer-mode /_assets/<md5>/ URLs in the HTML, reversed against the catalogue — certain, since the site is serving that package's own asset;
- legacy /typo3conf/ext/<key>/ paths — the key is written in the URL;
- an exposed /composer.lock — the full package list with EXACT versions;
- legacy typo3conf/PackageStates.php — the active-extension list.
Each hit is confirmed and records how it was found. Versions are filled in from composer.lock when available, and otherwise pinned by hashing the package's public files (composer hits, reusing the /_assets/<md5>/ prefix). This is the cheap first pass; brute force is only needed afterwards for backend-only extensions that expose no public asset.
func (*Fingerprinter) Reachability ¶
func (f *Fingerprinter) Reachability(ctx context.Context, target string) *Reachability
Reachability probes the target root and classifies whether we are being blocked. A network error is NOT a block (host down / DNS) — only a live HTTP response that denies us counts.
func (*Fingerprinter) RefreshExtensionLatestLive ¶
func (f *Fingerprinter) RefreshExtensionLatestLive(ctx context.Context, res *ExtResult)
RefreshExtensionLatestLive queries Packagist for the current newest stable version of every enumerated extension that has a Packagist name, overriding the DB snapshot's Latest and recomputing Outdated. Used by --live-versions so a stale embedded DB never hides that a plugin has a newer release.
type Option ¶
type Option func(*Fingerprinter)
Option configures a Fingerprinter.
func WithConcurrency ¶
WithConcurrency sets the maximum number of concurrent requests.
func WithHTTPClient ¶
WithHTTPClient sets a custom HTTP client.
func WithInsecure ¶
WithInsecure disables TLS certificate verification.
type Reachability ¶
type Reachability struct {
Status int `json:"status"` // HTTP status at the root
Reachable bool `json:"reachable"` // got any HTTP response at all
Blocked bool `json:"blocked"` // root looks banned / WAF-blocked
Reason string `json:"reason,omitempty"` // human explanation
WAF string `json:"waf,omitempty"` // detected WAF vendor, if any
}
Reachability is the outcome of a lightweight "are we actually allowed to talk to this host?" check — used to tell a real "not TYPO3 / no plugins" result apart from an IP ban / WAF block, where every path answers a blanket 4xx/5xx and any scan result would be meaningless.
type Release ¶
type Release struct {
Version string `json:"version"`
Type string `json:"type"`
ELTS bool `json:"elts"`
}
Release is one entry from the get.typo3.org release API.
type Releases ¶
type Releases struct {
Latest string `json:"latest"` // newest stable version overall
Branches map[string]string `json:"branches"` // "13.4" -> "13.4.33"
}
Releases tracks the latest stable release per TYPO3 branch (major.minor), so a detected version can be flagged as up-to-date or behind. Sourced from the official get.typo3.org release feed.
func FetchReleases ¶
FetchReleases downloads the current release feed and computes the latest stable (non-development) release per branch. Used by `t3scan buildreleases`.
func LoadReleases ¶
func LoadReleases() *Releases
LoadReleases returns the embedded release snapshot.
func (*Releases) LatestForBranch ¶
LatestForBranch returns the latest known patch of a version's branch, or "".
type UpdateStats ¶
type UpdateStats struct {
Extensions int `json:"extensions"` // total in the resulting DB
NewExtensions int `json:"newExtensions"` // keys built for the first time
Updated int `json:"updated"` // existing keys that gained versions
Unchanged int `json:"unchanged"` // existing keys already up to date
Downloads int `json:"downloads"` // version zips fetched this run
}
UpdateStats summarizes an incremental extension-DB update.
type VersionResult ¶
type VersionResult struct {
Target string `json:"target"`
IsTypo3 bool `json:"isTypo3"`
Mode Mode `json:"mode"`
BasePath string `json:"basePath,omitempty"`
Version string `json:"version,omitempty"` // best single answer, when confident
Range string `json:"versionRange,omitempty"` // human summary
Candidates []string `json:"candidates,omitempty"`
Confidence string `json:"confidence"` // high | medium | low
Method string `json:"method,omitempty"`
Markers []string `json:"markers,omitempty"` // is-TYPO3 evidence
Files []FileProbe `json:"files,omitempty"`
Notes []string `json:"notes,omitempty"`
// Blocked is set when the target looks like it is denying us (IP ban / WAF /
// rate-limit) rather than genuinely not being TYPO3 — any result is then
// unreliable. BlockReason/WAF explain what was seen.
Blocked bool `json:"blocked,omitempty"`
BlockReason string `json:"blockReason,omitempty"`
WAF string `json:"waf,omitempty"`
// LatestInBranch is the newest stable release of the detected version's
// branch (e.g. 13.4.35 for a 13.4.x target); NewestOverall is the newest
// stable TYPO3 release overall. Outdated is set when the target is behind
// its branch's latest patch. Sourced from the get.typo3.org release feed.
LatestInBranch string `json:"latestInBranch,omitempty"`
NewestOverall string `json:"newestOverall,omitempty"`
Outdated bool `json:"outdated,omitempty"`
// ExtensionsHint lists extension keys passively discovered in the served
// HTML (from typo3conf/ext/<key>/ asset URLs) — free, no enumeration needed.
ExtensionsHint []string `json:"extensionsHint,omitempty"`
// Findings holds security-relevant pre-auth observations (exposed install
// tool, debug mode, XML sitemap, host-header disclosure, …).
Findings []Finding `json:"findings,omitempty"`
// Vulnerabilities lists published core advisories affecting the detected
// version. Maybe holds advisories affecting only some candidates when the
// version is a range (uncertain until the version is pinned).
Vulnerabilities []Advisory `json:"vulnerabilities,omitempty"`
Maybe []Advisory `json:"maybeVulnerable,omitempty"`
// contains filtered or unexported fields
}
VersionResult is the full version-detection report for one target.