WordPress evidence review
The optional wordpress-review feature adds bounded WordPress-specific
interpretation to the existing web-review assessment. It does not start a
WordPress-specific scanner. It interprets signals from the selected application
entry response that the assessment already obtained, plus explicitly supplied
local context and advisory data. Selecting --wordpress-review alone adds no target or
provider requests. The separate --wordpress-discovery switch described below
explicitly authorizes a small bounded set of additional anonymous metadata
GETs through the same assessment broker.
An additional local --wordpress-fingerprints catalogue can authorize exact-byte
comparison for a few JS/CSS resources that discovery already observed; it does
not authorize guessing or crawling asset paths.
This feature remains absent from the ordinary default build. The current
untagged 0.10.0-alpha.3 development release-bundle compiles it as a Preview;
the published alpha.2 archives predate all WordPress producer and audit support.
A binary must be compiled with wordpress-review before the flags are available.
Compile-time inclusion does not enable the review; the operator must also select
it explicitly:
termivar scan https://authorized.example/ \
--profile web-review \
--wordpress-review \
--wordpress-context wordpress-context.json \
--wordpress-advisories wordpress-advisories.json \
--report-dir assessment-wordpress
--wordpress-context and --wordpress-advisories each require
--wordpress-review, and the review requires an explicit --profile
web-review. Termivar validates and bounds both local files before loading
secrets or constructing the scanner runtime. The CLI owns the file access; the
runtime receives validated values and no filesystem authority. The hardened
reader opens the final path component without following links and validates the
opened handle as a regular file; parent directories remain a trusted boundary.
Special files, malformed documents, duplicate keys, conflicting identities,
and limit violations are rejected.
What can be observed
V1 recognizes only supported structured data in the already captured selected application entry HTML:
- WordPress generator metadata in the expected
metaelement and attributes; - canonical same-origin asset-path hints for WordPress core, plugin, and theme identities.
The WordPress function reference documents that its HTML generator output is a
meta element whose content contains WordPress and the public version. The
same reference also documents a filter over generator output. Termivar
therefore treats this as a public declaration, not authenticated proof of the
installed source or patch state. See
https://developer.wordpress.org/reference/functions/get_the_generator/.
An eligible asset path can suggest a component identity. It does not establish that the component is installed, active, or executing on the server. Query parameters on an asset are not accepted as installed component versions. Article prose, comments, script strings, malformed elements, and foreign-origin assets do not establish WordPress identity. Missing hints do not establish absence, including when sites use custom content paths or content delivery networks.
Every result keeps the identity source, version source, confidence, and any contradiction separate. Repetition is not treated as independent evidence, and no declaration wins merely because it was seen last. V1 retains its numeric conflict rules; V2 decides semantic equivalence or conflict separately under each record's explicit comparison profile.
Explicit public-metadata discovery
Current development builds compiled with wordpress-review expose a separate
Preview option:
termivar scan http://127.0.0.1:8088/ \
--profile web-review \
--wordpress-review \
--wordpress-discovery \
--progress \
--report-dir assessment-wordpress-discovery
The numeric loopback address is illustrative and assumes an already running,
operator-authorized fixture. Termivar does not start WordPress. The discovery
option requires the explicit review and web-review profile; merely compiling
the feature or selecting --wordpress-review does not enable network work.
Reuse eligible pages already observed by the assessment
An explicit observed-page scope can broaden component interpretation without turning discovery into a page crawler:
termivar scan https://authorized.example/ \
--profile web-review \
--wordpress-review \
--wordpress-discovery \
--wordpress-page-scope observed \
--report-dir assessment-wordpress-observed-pages
The entry document contributes only ordinary <a href> first-hop candidates.
Termivar retains a deterministic bounded top set, selects at most three
non-entry pages, and reuses a page only when the same assessment already
completed and committed an anonymous, bodyless GET representation for the
exact query-free, fragment-free URL. The option never dispatches a missing page,
promotes a HEAD to GET, follows a redirect, retries, or recursively follows
links found on a secondary page. A selected page without such a representation
is reported as not_observed.
Observed mode can still add metadata traffic: a component asset on an eligible reused page may nominate its one exact stylesheet or readme under the layout already frozen from the entry. Repeated page sightings share one metadata request and preserve distinct opaque source-page references. The same 12-attempt WordPress allowance and metadata category ceilings apply. Reused page bytes were charged by the parent assessment and are reported as interpreted, not newly transferred WordPress bytes.
Only links inside the selected application directory and exact effective origin are eligible. Queries, fragments, downloads, login/admin/API/feed/action routes, static resources, encoded traversal or separators, userinfo, foreign ports, and sibling applications are excluded. Secondary content cannot replace the entry's application or role bases, nominate another REST index, or contribute a core generator version. A page whose content conflicts with the frozen application is retained as a rejected association and supplies no component or metadata candidate. These checks are bounded collection policy, not proof that every admitted server route is side-effect-free; the operator remains responsible for authorizing the selected application.
Page assets remain identity hints. A theme Version in the exact fetched
stylesheet is a served-artifact declaration, while a plugin readme Stable tag
is still distribution metadata and never becomes an installed plugin version.
Missing pages or signals do not establish component absence, and extra page
sightings do not establish activation, execution, authenticity, exploitability,
or remediation.
Reuse one supplied-session context without forwarding it
An executable compiled with both wordpress-review and
supplied-session-review exposes a separate integration for explicitly selected
V1/V2/V3 supplied-session resources. This V2 example uses an owned fixture:
termivar scan http://127.0.0.1:8088/ \
--profile web-review \
--wordpress-review \
--wordpress-discovery \
--wordpress-supplied-session \
--session-policy session-policy.toml \
--session-cookie-file session-cookies.tsv \
--report-dir assessment-wordpress-session
The loopback exception is for an owned fixture; real credentials require HTTPS,
and Secure cookies still require HTTPS. Authorization-header policies use the
existing env/file/stdin source instead of the cookie file. All selected resource
GETs remain operator-authorized and application-defined GET handlers may have
server-side effects.
For an owned V3-conforming fixture, prepare a strict V3 policy and a private
regular TSV with exactly the username and password rows, then use the
existing login-file input instead of the cookie file:
termivar scan http://127.0.0.1:8088/ \
--profile web-review \
--wordpress-review \
--wordpress-discovery \
--wordpress-supplied-session \
--session-policy ./owned-wp-form-policy.toml \
--session-login-file ./owned-wp-form.secret.tsv \
--report-dir ./assessment-wordpress-form-login
This command requires that exact owned fixture and its matching inputs; it is
not a command for a real site or native wp-login.php. The task-owned fixture
route is /termivar-session-login/ under the selected home path, with configured
user, pass and hidden csrf fields, one cookie-free form GET and at most one
bounded POST. The policy declares its exact logged-in cookie metadata, health
path and protected resource; no form or cookie-name discovery is performed.
WordPress password authentication must match the exact low-privilege user before
the route releases a genuine logged-in cookie. Its later health predicate
independently checks the actual logged-in user and expected principal. The lab
preallocates the session token as a private redaction oracle; V3 receives only
username/password, and this does not establish fresh session creation or
session-fixation prevention. No execution of this example is implied.
This option is valid only with the explicit review, discovery, supplied-session
policy/credential and web-review profile. The runtime first commits the
anonymous entry evidence and freezes its application/layout. It then reduces a
complete selected session-resource HTML response while the response is owned,
but publishes WordPress facts only if the immediately following health checkpoint
and the exact S01 resource evidence both commit. Policy, application, principal,
credential mechanism, epoch and opaque resource/page references stay linked in
a separate audit. Login-shaped, incompatible, unsupported, truncated,
health-unqualified and uncommitted responses cannot nominate metadata.
The schema remains security.wordpress-discovery-audit/v4. V1/V2 preserve
termivar.wordpress-supplied-session-metadata-discovery/v1, mode
committed_supplied_session_resources and epoch one. V3 uses collection policy
termivar.wordpress-supplied-session-metadata-discovery/v2 and mode
form_login_supplied_session_resources, without changing the field shape. Its
nested session_epoch records the actual final established-session epoch, zero
or one. Failed acquisition or unhealthy startup retains epoch zero, even if the
POST acquired a cookie. Epoch zero allows only empty planned pages marked
not_established / not_evaluated, zero dispatched resources, zero
committed/interpreted activity and no supporting component evidence. S01's
undispatched planned resource rows retain epoch one. Wrong-principal startup
failure has final epoch zero; health loss after successful startup retains epoch
one. Neither invents committed pages. V4–V7 remain refused
before their secret inputs are acquired.
Accepted component hints can nominate only the existing public metadata work. Those GETs are anonymous, exact-origin and broker-owned inside the same twelve WordPress-attempt allowance; neither Cookie nor Authorization is forwarded. The session traffic retains its separate S01 request/health accounting inside the same assessment parent budget. Authenticated-page JS/CSS does not authorize fingerprint acquisition in this initial slice, and the audit records that work as not selected. Existing anonymous entry, observed-page and fingerprint paths are unchanged when the option is absent. Private page URLs are not promoted into the ordinary crawl, and no flag, feature or request budget is added. A selected fingerprint catalogue can therefore have a valid empty audit with zero resource acquisitions and zero evaluated components for this private-page-only context.
A component seen only in this context remains an observed identity. Plugin
Stable tag, URL ver, catalogue candidates and any separately supplied
version keep their existing meanings; none becomes an authenticated installed
version. Different principals or epochs are explicit context/methodology or
coverage changes in Compare, not plugin removal, vulnerability introduction or
remediation.
Finite observed-asset fingerprint catalogues
An explicitly supplied development catalogue can compare complete observed JS/CSS bytes with a finite set of listed releases:
termivar scan http://127.0.0.1:8088/ \
--profile web-review \
--wordpress-review \
--wordpress-discovery \
--wordpress-page-scope observed \
--wordpress-fingerprints \
docs/examples/wordpress-review/asset-fingerprints/catalogue.synthetic.json \
--report-dir assessment-fingerprints
The loopback service must already be running and authorized. The option is the
only opt-in and is validated before runtime construction. It accepts strict
security.wordpress-asset-fingerprint-catalog/v1 JSON read once through the
same hardened local-file boundary. Each entry binds a plugin or theme slug, an
opaque release/build identity, a safe component-relative .js or .css path,
an exact byte length, a full SHA-256 digest, and
identity-content-bytes/v1. The input is limited to 4 MiB, 16 components, 128
releases per component, 32 files per release, 16,384 files overall, and a
16 MiB accounted prepared index. The checked-in synthetic matrix and its
independent preparation declaration are documented in
examples/wordpress-review/asset-fingerprints/README.md.
The test-only scripts/prepare_wordpress_fingerprint_catalog.py hashes only
explicit local files; it performs no discovery or network access.
Catalogue paths never create request authority. Termivar admits only structured
script src and stylesheet link href URLs already observed under the frozen
application and component roots. V1 accepts no query or one literal bounded
ver= value; that value stays a cache/distribution hint and never selects a
release. The exact admitted URL is retained for acquisition, while the safe
component-relative path is used for lookup. linked remains unsupported; an
observed page may contribute candidates only through the existing observed
reuse mode.
Eligible committed anonymous GET bytes are reused first. Otherwise Termivar
may issue one anonymous, redirect-free, retry-free identity request for an
actually observed resource through the existing broker. A request asks for
identity encoding. Comparison requires a complete status-200 body, an eligible
JS/CSS media type, no Content-Encoding, consistent byte length, and no obvious
HTML/login body. SHA-256 covers the exact content bytes before UTF-8 decoding,
newline/BOM handling, whitespace changes, or minification. A truncated, partial,
encoded, oversized, failed, or uncommitted response never becomes a complete
fingerprint. Raw code, query values, and local paths are not saved in the audit.
Fingerprint work shares the 12-attempt and 2 MiB WordPress envelopes with metadata discovery. It is further limited to four GETs, two resources per component, two components, one request in flight, and 512 KiB per asset. Reused bytes are interpreted but not charged to the wire twice. Exhausted budgets, ambiguous URL variants, missing reference rows, unsupported representations, and unavailable responses remain explicit coverage limitations.
Matching is three-valued for each listed release: exact length plus full digest
is a match, a different listed length/digest is a mismatch, and a missing or
unusable reference is unknown. The A/B shared-JS and B/C shared-CSS intersection
in the synthetic matrix leaves B as the sole compatible listed release. A
strong single_catalogue_candidate additionally needs at least two informative
paths, a complete listed matrix, and no hidden acquisition or variant conflict.
One file produces only provisional candidates; mixed A/C bytes produce no
consistent listed release. No majority vote, closest version, fuzzy hash, or
newest-version fallback is used.
These results never enter installed-version evidence or the vulnerability
evaluator. They mean only that observed files are compatible with named releases
inside the supplied finite catalogue under the same-release-artifact assumption.
Unlisted/custom builds, copied or cached assets, and mixed deployments remain
possible. SHA-256 is not a signature, catalogue provenance is not publisher
authentication, and even a single listed candidate is not whole-package
verification, patch state, exploitation, or remediation evidence. The audit
keeps separately supplied generator/theme/operator versions and conflicts intact;
plugin readme Stable tag and URL ver remain non-installed-version hints.
The selected application may be an unambiguous directory URL. A conventional
WordPress application under /blog/ needs no layout declaration:
termivar scan https://authorized.example/blog/ \
--profile web-review \
--wordpress-review \
--wordpress-discovery \
--report-dir assessment-wordpress-blog
The final slash is significant. Termivar does not turn /blog into /blog/,
follow a redirect to guess the application, or fetch the origin root as a
layout probe. The original target spelling must also contain no surrounding
whitespace, control characters, backslashes, encoded traversal, or encoded
separators; parser normalization is not allowed to silently change the selected
application. The selected application is one directory for this invocation; it
is not every WordPress installation sharing the origin.
When selected, discovery freezes candidates from structured links in the committed selected-entry response and sends only anonymous, bodyless GETs through the existing exact-origin broker. It does not inherit Authorization, Cookie, or proxy credentials, and the discovery-only client does not use ambient proxy configuration. Redirects and retries remain disabled. The WordPress narrowing policy admits at most 12 wire attempts: one advertised REST index, three theme stylesheets including at most one parent edge, and eight plugin readmes. It is sequential, retains at most 32 candidates, records when a later parent-theme declaration could not fit that cap, and has fixed per-response and aggregate byte ceilings underneath the existing assessment request, response, deadline, and cancellation limits. An attempt and a completed, parsed, committed source are reported separately. As with the parent broker, a delivered transport chunk can cross a retained-byte threshold before cancellation; the ceilings are not described as a perfect wire cutoff.
Candidates are evidence-derived, not a wordlist. Conventional component paths
retain the observed prefix before the exact wp-content segment:
- a
Linkheader or HTML link whose exact relation ishttps://api.w.org/can nominate the same-origin REST root; - a structured same-origin asset under
<prefix>/wp-content/themes/<slug>/...can nominate only that theme's rootstyle.css; - a structured same-origin asset under
<prefix>/wp-content/plugins/<slug>/...can nominate only that plugin's rootreadme.txt; - a valid child-theme
Templateheader can nominate one same-root parent stylesheet at depth one.
For example, an eligible resource under
/cms/wp-content/themes/example/assets/site.css nominates only
/cms/wp-content/themes/example/style.css; it does not nominate the root
/wp-content/ path. A /blog/ selection does not absorb a conventional
/shop/wp-content/ candidate. Multiple incompatible conventional prefixes are
reported as ambiguous instead of choosing the first, shortest, or most common
one.
The REST query form is closed: the current WordPress source emits
<application>/index.php?rest_route=/ when pretty permalinks are disabled. The
historical handbook illustration /?rest_route=/ remains useful context, but the current
core form is exercised by the pinned real-CMS lab. Extra or duplicate query
parameters, other routes, userinfo, fragments, encoded separators/traversal,
foreign origins, and a different loopback port are rejected. Termivar fetches
the REST index only; it does not traverse users, posts, settings, authentication
links, registered routes, or an API namespace.
The REST index retains only a bounded list of namespaces. wp/v2 means core
REST endpoint support, not WordPress 2.x. A namespace such as
termivar-lab/v1 is a protocol namespace and is neither a reliable plugin
directory identity nor an installed plugin version.
A theme stylesheet is interpreted from its bounded initial header section.
The identity remains the validated source path. Version is the served
artifact's declaration; Template is a parent-directory declaration; and
Requires at least, Tested up to, and Requires PHP are compatibility
statements, not installed core/PHP versions. A child Template declaration may
nominate one parent stylesheet under the same selected theme collection base;
it never falls back to root /wp-content/themes/ or searches another observed
root. A served file can be copied, cached, or stale, so even a valid declaration
is source-qualified observation, not authenticated installation or patch state.
A plugin readme contributes its display header, requirements, and Stable tag.
The Stable tag identifies a distribution/repository release pointer. It is
never inserted into the installed-version evidence collection, even if its
spelling is a supported version. Termivar does not fetch plugin PHP entrypoints
or inspect server source to fill that gap. trunk and unsupported tags remain
nonversion hints.
Entry-only discovery continues to use the additive top-level
security.wordpress-discovery-audit/v2. Explicit observed-page scope uses the
strict additive security.wordpress-discovery-audit/v3, which adds the selected
mode, entry and secondary opaque page references, candidate/selection/omission
counts, reuse and association outcomes, interpreted versus newly transferred
bytes, page evidence references, and metadata-to-source-page edges. It does not
contain page bodies or raw URLs. Both versions record
the deployment-aware policy, an opaque selected-application reference, each
role's exact/ambiguous/unresolved state and evidence basis, bounded skipped or
conflicting association counts, anonymous GET method,
seed/candidate/attempt/completion/commit counts, accounted response bytes,
outcomes, typed metadata, and evidence reference counts. Location references
are framed hashes for stable comparison and are not authentication or a promise
of anonymity. Historical discovery audit v1 remains readable with its original
root-only meaning. A discovery-influenced review without asset fingerprints
uses the additive strict security.wordpress-review-audit/v7 wrapper. A run
with an explicit fingerprint catalogue uses the additive strict
security.wordpress-review-audit/v8 wrapper and the separate top-level
security.wordpress-asset-fingerprint-audit/v1 document. Its required
review_basis_schema preserves which v1–v6 evaluation contract supplied the
review facts, while additional_request_count records the actual discovery and
fingerprint attempt count. With discovery absent, historical v1–v6 output stays
unchanged and retains additional_request_count=0. Current Compare/Verify
readers accept the coordinated v7/v8 review and top-level discovery/fingerprint
audits without compiling the producer feature. Compare applies the recorded
review basis to component and advisory semantics while treating collection
policy and request coverage as separate dimensions. A review-only versus
discovery comparison is therefore not a newly introduced vulnerability or
remediation.
The strict v3 document vocabulary also reserves linked page mode and
fetched acquisition rows for the coordinated bounded linked-page slice. The
current CLI exposes only observed and rejects linked; accepting a saved
linked row in the strict display-only reader does not activate page retrieval.
No eligible source, a 404, unsupported content, throttling, truncation, or a
budget/deadline stop remains visible as limited collection. It is never a
secure/clean result. Generator absence after an operator or plugin suppresses
it likewise remains unknown. Discovery can supply a source-qualified theme
version to the existing advisory evaluator, but a range match still does not
establish authenticated installation, exploitability, impact, or remediation.
exploit_execution and impact_validation remain not_performed.
The disposable real-WordPress acceptance design, independent WP-CLI ground
truth, exact four-request delta, pinned image identities, and licensing notes
are in the
discovery-lab example.
Explicit same-origin layout declaration
Truly custom component roots can be selected with one strict local declaration. The declaration supplies role bases, not component names or arbitrary request URLs:
{
"schema": "security.wordpress-layout/v1",
"application_url": "https://authorized.example/blog/",
"core_base_url": "https://authorized.example/cms/",
"themes_base_url": "https://authorized.example/site-content/themes/",
"plugins_base_url": "https://authorized.example/modules/"
}
termivar scan https://authorized.example/blog/ \
--profile web-review \
--wordpress-review \
--wordpress-discovery \
--wordpress-layout wordpress-layout.json \
--report-dir assessment-wordpress-custom-layout
--wordpress-layout requires the explicit review, discovery, and web-review
profile. The file is at most 16 KiB, is read once through the hardened local
regular-file boundary before scanner construction, and is never reopened by
the runtime. Its application_url must exactly match the selected canonical
application. Every supplied base must be a same-effective-origin directory URL
with a final slash and no credentials, query, or fragment. Theme and plugin
bases cannot be the origin root or overlap ambiguously. Unknown/duplicate
fields, percent-encoded path octets, malformed directory paths, alternate ports,
links, and special files fail preflight. Rejecting encoded octets keeps role
overlap and opaque location identity byte-unambiguous in this strict V1 input.
The declaration is an operator assertion of association, not authenticated
server configuration and not additional network authority. A declared theme or
plugin base still requires an eligible asset observed in the selected entry
response before Termivar derives that component's style.css or readme.txt.
An empty page therefore creates no wordlist or directory enumeration, and a
declared core base creates no config, PHP-source, or version request. Roles not
declared may use only an unambiguous coherent conventional observation.
Reports retain only the declaration schema, exact byte length, SHA-256, and opaque location identities—not its local path or raw URLs. The digest identifies the bytes read but does not authenticate the operator declaration or establish a simultaneous filesystem snapshot. Parent directories and a quiescent input remain caller trust boundaries.
Operator context
The context schema is security.wordpress-context/v1. A document names one
exact root and up to 256 declared components. The root must match the scan's
exact origin root and may not contain credentials, a query, or a fragment.
Components have a closed core, plugin, or theme kind; core uses the slug
wordpress, while plugin and theme slugs use bounded canonical lowercase
identifiers.
An optional version remains an operator-supplied string. Optional activation
states are active, inactive, network_active, or unknown. Optional facts
are limited to a closed hosting-OS classification, multisite state, and up to 32
bounded patch declarations per component. Omitted components and facts are
unknown, not absent. A supplied status or patch declaration is not live
authentication of the installation.
The context can be assembled from trusted operator inventory. The official
WP-CLI plugin list documentation describes fields including name, status,
and version, and activation states including active, network-active, and
inactive: https://developer.wordpress.org/cli/commands/plugin/list/. The V1
Termivar context normalizes WP-CLI's active-network spelling to
network_active. Termivar also accepts the bounded saved-inventory projection
described below. It never invokes WP-CLI or PHP.
The complete context file is limited to 1 MiB. See
docs/examples/wordpress-review/context.synthetic.json
for a deliberately fictional declaration.
Saved WP-CLI inventory
Instead of authoring security.wordpress-context/v1, an operator can save
three narrow WP-CLI projections before the assessment:
wp plugin list --fields=name,status,version --format=json > wordpress-plugins.json
wp theme list --fields=name,status,version --format=json > wordpress-themes.json
wp core version > wordpress-core-version.txt
Termivar reads only files already produced by the operator. It does not start WP-CLI, PHP, WordPress, or another subprocess. Select any nonempty combination of the three inputs with their exact CLI options:
termivar scan https://authorized.example/ \
--profile web-review \
--wordpress-review \
--wordpress-plugins-json wordpress-plugins.json \
--wordpress-themes-json wordpress-themes.json \
--wordpress-core-version-file wordpress-core-version.txt \
--wordpress-advisories wordpress-advisories.json \
--report-dir assessment-wordpress
The saved-inventory options conflict with --wordpress-context; they are an
alternate source for the same bounded operator-context role. Advisory input is
independent and may be supplied with either form. All selected inventory files
share one 1 MiB ceiling and one 256-entry ceiling. Plugin and theme inputs must
be JSON arrays whose rows contain bounded name, status, and version
strings. The core input is one version line of at most 64 bytes, with at most
one final LF or CRLF. Duplicate object keys, duplicate component identities,
unknown fields or statuses, malformed JSON, and limit violations fail closed.
Supported plugin statuses are active, inactive, active-network,
must-use, and dropin. Supported theme statuses are active, inactive,
and parent. active-network becomes the typed network_active activation.
That row describes only the supplied WP-CLI inventory entry; it does not prove
the complete activation topology of a multisite network.
must-use and theme parent are retained as inventory classifications without
inventing a normal activation state. A drop-in name such as object-cache.php
is a filename, not a canonical plugin catalogue slug; it is therefore reported
as an explicit inventory limitation and is not evaluated as an advisory
component. Update-related WP-CLI fields, when present in the bounded accepted
shape, are informational only and never become patch or remediation evidence.
These files are operator-supplied, unauthenticated declarations. Termivar binds
them to the exact origin selected by the invocation, but parsing does not prove
that WP-CLI produced them, that they are complete, or that they describe that
origin. A category that was not supplied is not_supplied, which means unknown,
not empty. Even a supplied empty array does not authenticate absence. Input
paths are not retained in the assessment. The report records fixed input class,
exact byte length, and SHA-256 for byte identification; a digest is not a
signature or source authentication.
The hardened reader opens the final path component without following links and validates the same opened handle as a regular file. Parent directories remain a trusted boundary; this is not whole-path containment or a concurrent filesystem snapshot. Saved inventory is parsed before credentials or scanner construction, adds no target/provider requests, and remains operator context in the existing single assessment runtime.
Saved inventory produces the additive
security.wordpress-review-audit/v3 shape so coverage, entry classifications,
limitations, and input-byte provenance remain explicit. It does not alter the
stable context or advisory schemas. Raw version strings remain evidence. A V1
catalogue still uses numeric-dotted/v1; a V2 catalogue evaluates them under
each record's explicit comparison profile. An unsupported version stays
indeterminate rather than selecting a profile or falling back automatically.
See the clearly fictional files and commands in the
saved-inventory example README.
Advisory snapshots
The advisory schema is security.wordpress-advisory-catalog/v1. A catalogue
contains a bounded identity, revision, retrieval date, and no more than 4,096
records. Each record names exactly one component and carries its own source
reference, source revision, retrieval date, and usage-basis note. A record can
contain at most 16 explicit version intervals, 16 fixed-version declarations,
and eight typed prerequisites.
Advisory files are data, never executable policy. They cannot contain regular
expressions, scripts, request templates, payloads, or commands. References are
inert bibliography; they are not fetched during a scan. Termivar does not
query vulnerability databases or claim that a supplied snapshot is current or
comprehensive. Without a file the report says catalogue_not_supplied. With a
file, no matched record means only that no match occurred in that supplied
snapshot.
V1 compares numeric-dotted release tuples of one to eight components in at
most 64 UTF-8 bytes. Each component contains ASCII digits only, has no
multi-digit leading zero, and is parsed with checked u32 arithmetic. Trailing
zero components normalize away (for example, 1.2 and 1.2.0 compare
equally). Prefixes, suffixes, empty components, and overflowing components are
unsupported and therefore indeterminate. This profile is intentionally
narrower than PHP's version_compare(): the PHP manual documents normalization
and ordering for separators and labels such as dev, alpha, beta, and
RC. Termivar neither implements that full behavior nor runs PHP. See
https://www.php.net/manual/en/function.version-compare.php.
Explicit comparison profiles
security.wordpress-advisory-catalog/v2 requires every record to select one
closed comparison_profile. numeric-dotted/v1 is exactly the V1 comparator
described above; its parsing and trailing-zero equality are unchanged.
php-release-subset/v1 is an additive, deliberately bounded interpretation of
common PHP-style release forms. The profile belongs to advisory data, not to
the observed version string: Termivar does not infer it from a suffix, vendor,
host, or whichever comparator might produce a match. Missing and unknown
profiles make the catalogue invalid before runtime construction.
The PHP release subset accepts nonempty ASCII strings of at most 64 bytes with
one to eight numeric release components. Numeric components are in
0..=2147483647, contain no more than ten digits, and may contain leading
zeroes. Single ., -, _, or + separators are accepted between numeric
components. One optional recognized suffix may follow the core, adjoining it
or following one such separator: dev, alpha/a, beta/b, RC/rc, or
pl/p. One optional numeric suffix counter may likewise adjoin the suffix or
follow one separator. + is a separator here, not SemVer build metadata.
Unknown or vendor labels, other case variants, v prefixes, whitespace,
controls, doubled/leading/trailing separators, multiple suffixes, arbitrary
build text, overflow, and excessive tokens remain unsupported. Termivar does
not strip unknown text into a guessed order. The accepted token sequence uses
the ordering of the documented PHP release forms, including PHP's treatment of
numeric, prerelease, final, and patch-level parts. It intentionally does not
inherit numeric-dotted trailing-zero normalization: 1.0 equals 1.0.0 under
numeric-dotted/v1, while 1.0 sorts before 1.0.0 under
php-release-subset/v1. Aliases such as 1.0-alpha1 and 1.0a1 compare
equally within the PHP subset. This is not full PHP compatibility, SemVer,
Composer constraint support, or automatic interpretation of vendor schemes.
The V2 evaluator resolves all version evidence separately for each record's
selected profile. Its saved security.wordpress-review-audit/v2 record shows
the comparison_profile, a bounded version_resolution, and the corresponding
reason alongside the existing relation, prerequisites, source basis, and
uncertainty. Unsupported evidence remains evidence; it is not discarded or
retried with another profile. A profile change is a methodology difference,
not proof that an installation changed or was remediated. V1 catalogues retain
the existing security.wordpress-review-audit/v1 shape.
The V2 component-level evidence class describes only provenance (observed hint, operator-supplied, or unknown). Semantic version equivalence, conflict, and unsupported input are reported per advisory record under its selected profile; they are not collapsed into one global numeric interpretation.
The subset is based on the public PHP version_compare() description and the
pinned PHP 8.3.0 implementation source at commit
d26068059e83fe40de3430a512471d194119bee0. The source is a comparison
reference, not a runtime dependency:
https://github.com/php/php-src/blob/d26068059e83fe40de3430a512471d194119bee0/ext/standard/versioning.c.
The evaluator checks component identity before version intervals and reports version relation and each prerequisite separately. It does not infer an open lower bound merely because a bulletin names a fixed version. Disjoint maintained branches remain separate. Unsupported prerequisites remain unevaluated rather than being discarded. Prerequisites are conjunctive: an outside declared range or a supported prerequisite contradicted by supplied facts makes the summary contradicted even when another prerequisite is unsupported; absent a contradiction, unsupported evidence remains indeterminate rather than becoming a match.
The example directory contains three catalogue examples:
advisories.curated.jsonis one manually curated factual record from an official WordPress project advisory. It is a small example, not a vulnerability feed.advisories.synthetic.jsonuses unmistakably fictional component and record identities to exercise matched, contradicted, and unsupported-version decisions. These are not alleged vulnerabilities.advisories.profiles.synthetic.jsonand its pairedcontext.profiles.synthetic.jsonexercise explicit V2 profile selection, prerelease interpretation, profile-dependent trailing-zero behavior, and rejection of a fictional vendor label. They are comparison fixtures, not alleged vulnerabilities.
Local Wordfence V3 Production-format exports (development source)
The feature-enabled development CLI accepts an explicitly selected, already saved Wordfence V3 Production-format JSON file through the existing assessment:
termivar scan <AUTHORIZED_EXACT_ROOT> \
--profile web-review \
--wordpress-review \
--wordpress-advisories wordfence-production.json \
--wordpress-advisories-format wordfence-v3-production \
--report-dir <NEW_REPORT_DIRECTORY>
Omitting --wordpress-advisories-format retains the existing strict Termivar
catalogue parser. The choice is never inferred from a filename or document
contents, and there is no fallback between the two formats.
By default, Termivar preserves the external source-comparison limitation: it
selects relevant associations but does not guess how the source intended every
version spelling to be ordered. Within the historical identity and resource
envelope, that unchanged path emits security.wordpress-review-audit/v4 with
wordfence-v3/source-semantics-unresolved/v1 and indeterminate relations.
When the expanded source-identity or resource metadata described below is
needed, the same unresolved policy is carried by audit v6 instead.
An operator can instead request one reproducible Termivar interpretation for the supplied external snapshot:
termivar scan <AUTHORIZED_EXACT_ROOT> \
--profile web-review \
--wordpress-review \
--wordpress-advisories wordfence-production.json \
--wordpress-advisories-format wordfence-v3-production \
--wordpress-external-version-profile php-release-subset/v1 \
--report-dir <NEW_REPORT_DIRECTORY>
The accepted values are numeric-dotted/v1 and
php-release-subset/v1. This option is valid only with an explicitly supplied
wordfence-v3-production file, --wordpress-review, and --profile
web-review; it does not override native Termivar catalogue records. Termivar
never selects a profile from the filename, source URL, component kind, or the
outcome it would produce, and it never retries unsupported input under the
other profile.
Within the historical identity and resource envelope, explicit selection emits
security.wordpress-review-audit/v5; audit v6 carries the same fields when its
expanded metadata is needed. Both record:
comparison_policy=termivar.wordfence-v3-explicit-interpretation/v1;- the selected
comparison_profile; policy_selection=explicit_operator;source_semantics_assurance=not_established.
These fields mean that the operator asked Termivar to apply a named local rule. They do not claim that Wordfence selected or endorses the rule, that the rule matches every component vendor, or that the local inventory is authentic.
The adapter does not obtain an API key, call Wordfence, discover a credential, or fetch a reference during an assessment. The operator remains responsible for obtaining and storing any export under the source's current terms. Wordfence's public documentation states that the V3 feed requires token authentication and describes distinct Production and Scanner formats. This scope accepts Production format only; it does not infer a format from a filename or fall back to the Scanner format.
External source identities
The provider's component kind and slug are retained as a source identity before Termivar attempts to match them to the native canonical component identity. This keeps source spelling and case visible instead of rewriting the supplied record. Native operator and observed component identities retain their existing lowercase canonical rules.
The mapping revision termivar-wordfence-v3-production/v2 uses the explicit
policy
termivar.wordfence-v3-exact-plus-ascii-lowercase-candidate/v1. Its four
outcomes have deliberately different authority:
exactmeans the source kind and slug already form the same canonical identity without rewriting. Only exact associations can support a decisive version-range result under an explicitly selected comparison profile.ascii_case_fold_candidatemeans ASCII lowercase produces a valid canonical lookup key and only one distinct source spelling maps to that key in the supplied snapshot. It can surface a potentially relevant association, but it remains indeterminate because Termivar has not established that the source considers the spellings equivalent.ascii_case_fold_ambiguousmeans multiple distinct source spellings map to the same candidate key. The collision count and original spelling are retained; Termivar does not select a winner or produce a decisive version relation from that candidate.unresolvedmeans no safe canonical candidate exists. The association and its ranges and patched-version declarations remain included in ingestion and resource accounting, but are not silently treated as irrelevant, absent, or unaffected.
Source slugs are bounded to 128 ASCII bytes. Controls and unsupported structural
forms are rejected; accepted noncanonical spellings remain inert identifiers
and are never interpreted as a path, URL, or normalization instruction.
Malformed values and resource violations still fail the import. Case folding is
a Termivar matching aid, not provider endorsement, package-directory validation,
installation authentication, or permission to fetch more data. Reports retain
identity_source_assurance=not_established and reconcile exact, candidate,
ambiguous, and unresolved association counts separately.
The declared source file is read completely within a separate bounded
external-import policy before runtime startup. The current largest resource
policy is termivar.wordfence-v3-bounded-capacity/v3. Its finite ceilings are
256 MiB of raw input, 100,000 records, 200,000 software associations, 768 KiB
per record, and 160 MiB of conservatively accounted import data. A
decoded object may contain at most 128 JSON members; one software association
may contain at most 128 affected ranges and 128 patched-version declarations.
The generic decoded-string and description ceilings are 4 KiB, while existing
stricter field-specific ceilings still apply. Source slugs have the separate
128-byte limit described above. The raw-input, record-count, and association-
count ceilings are unchanged from the earlier policy.
Inputs that remain within the historical 64 MiB envelope retain resource-policy
ID termivar.wordfence-v3-bounded-capacity/v1. V2 retains its historical
128 MiB ceiling, while V3 identifies imports that require the additive 160 MiB
envelope. The selected ID reflects the largest conservatively charged parse,
identity-resolution, or retained-index phase, so a temporary phase that exceeds
an older ceiling is not labelled as having fit it. Expanded source field/list
semantics still require at least V2 even when their byte charge is small.
These IDs describe Termivar's bounded decoder, not the provider's feed
completeness or a promise that every simultaneous maximum will fit. The limits
are joint, use checked accounting, and remain subject to narrower evaluator,
work, field, and report ceilings. The retained-import charge covers retained
records, strings, notices, indexes, and bounded parse-time identity structures;
it is a conservative application accounting policy, not an operating-system
RSS or heap guarantee.
Crossing any effective bound fails explicitly rather than
truncating the source. Root UUID keys, record IDs,
software associations, structured range bounds, patched-version declarations,
nullable source dates, references, and rights notices remain source-qualified
metadata. A patched flag or fixed-version declaration does not prove that the
selected installation contains a fix. A source CVSS value is not a
Termivar-calculated installed risk score.
When a selected record carries Defiant copyright terms, preflight requires an
existing source-declared Wordfence vulnerability-record reference. Only the
documented www.wordfence.com/threat-intel/vulnerabilities/ HTTP/HTTPS form is
accepted. The record link may have no query or exactly one literal source
parameter whose value is 1–64 unreserved ASCII characters; other, empty,
duplicate, encoded, or multi-parameter query forms are rejected. Termivar
neither invents nor fetches a missing link. The report emits that exact record
link together with the retained notice and licence text. A missing required
link rejects the input before the assessment starts rather than silently
publishing an attribution-incomplete copy.
The source format specifies structured range endpoints and inclusivity, but it
does not by itself establish a comparison algorithm for every vendor version.
Termivar therefore does not claim that the provider's semantics equal either
numeric-dotted/v1 or php-release-subset/v1. With no explicit selector,
source semantics remain unresolved. With one selector, the result is explicitly
qualified as a Termivar calculation under the operator-selected rule. The
1.0 versus 1.0.0 boundary remains an explicit regression case for this
distinction.
Only the complete endpoint value * is the supported unbounded sentinel;
1.* is an unsupported version spelling, not a glob. The selected profile is
used for both endpoint ordering and membership. A supported version in one
containing range can produce a qualified positive even when another range is
unsupported, but the report retains that partial range coverage. A negative
outside result requires every declared range to be supported and conclusively
outside. Unsupported ranges, missing or conflicting version evidence, reversed
bounds, and equal bounds made empty by an exclusive endpoint remain
indeterminate. Termivar does not swap, drop, pad, strip, or retry those values
under another comparator.
Source-declared patched versions remain advisory metadata and never establish
the installed patch state. Under an explicit profile, if a supported declared
patched version also falls inside a supported affected interval in the same
association, v5 reports
source_patched_version_within_affected_range and keeps the association
indeterminate. Unsupported patched-version spellings are retained verbatim as
source guidance; they are not retried under another profile or used to invent
affected bounds.
Reports identify the external format and mapping revision, preserve the exact-input byte length and digest, and retain required notices for material they display. V4 preserves its unresolved selected/unsupported accounting. V5 adds reconciled denominators for selected associations, decisive within/outside relations, indeterminate associations, and evaluated/unsupported/invalid/ not-evaluated ranges, including qualified-positive partial coverage.
The additive security.wordpress-review-audit/v6 shape is used when expanded
source-identity or V2/V3 resource-policy metadata is required. It retains the V4
or V5 comparison-policy meaning selected by the invocation and adds the actual
mapping and resource-policy IDs, accounted retained-import bytes, source identity
and mapping state for selected evaluations, collision metadata where applicable,
and the reconciled four-way identity totals. V6 also records exact
projected_identity_limitations and unprojected_identity_limitations counts.
At most 64 deterministically ordered identity_limitations detail rows are
expanded for structurally valid source identities that could not be represented
canonically. Their raw kind/slug, upstream identity, declared ranges/fix
information, and required attribution remain reviewable. The aggregate
unresolved count still covers the complete imported snapshot; rows outside the
detail projection are neither discarded from that accounting nor treated as
irrelevant or unaffected. Applicability and version comparison remain
explicitly not evaluated.
Historical audits v1 through v5
and their validation rules are unchanged. An unselected external comparator in
v6 therefore remains unresolved; the newer audit shape is not permission to
invent a comparison result.
Overlapping coverage qualifications are not added together as if they were
findings. These values identify the processed bytes and interpretation;
they do not authenticate the source, prove snapshot completeness, or establish
a collection timestamp. Source-declared published and updated values remain
distinct from collection time. A filesystem modification time is not substituted
for any of them.
The independently written
wordfence-v3 synthetic fixture
records the public-schema-only expectations used by the parser and end-to-end
acceptance. It is not a live export, no provider credential was used, and this
does not establish compatibility with a complete current feed snapshot.
Development acceptance evidence
The source tree keeps a separate, synthetic WordPress acceptance corpus under
crates/termivar-scanner/tests/fixtures/wordpress_acceptance. Its literal
expectations are maintained independently from the evaluator implementation,
and its reviewed and holdout inputs are labelled separately. The reviewed set
contains four records: one declared-data candidate, two missing-evidence
results, and one unsupported result. The holdout set contains two records: one
contradicted result and one missing-evidence result. These are conformance
denominators for the declared fixtures, not a detection-accuracy percentage.
The same acceptance layer covers missing inventory categories, must-use and drop-in plugin rows, parent themes, and plugin/theme identities that share a slug. A generated large-input case reads 4,096 synthetic external records and selects exactly one relevant association while accounting for 4,095 deliberate exclusions. It records byte and retained-data measurements plus non-contract timing observations; it does not model or certify a complete provider feed.
A process-level feature-enabled test performs two assessments against the same
benign loopback origin, changes only declared local WordPress inputs, verifies
both report bundles, and compares them in both directions. The expected
component-version and advisory range/fix dimensions are literal test oracles.
The two scans must retain the same request trace, while Verify and Compare add
no target request. Existing CI executes that test natively on Linux, Windows,
and macOS. The current development four-platform curated release check also
builds WordPress through release-bundle, then exercises its synthetic saved
input/report path from each extracted native archive. Runtime opt-in remains
explicit, and the published alpha.2 archives retain their earlier WordPress-free
composition.
No actual Wordfence vendor export is committed to or relabelled as part of this acceptance corpus. Public-schema synthetic cases exercise parser and policy contracts, but by themselves do not establish current full-snapshot compatibility, source authenticity, or complete provider coverage. Any authorized private real-snapshot acceptance and process measurements are kept as separately scoped task evidence without publishing the feed or its prose. The repository's small curated WordPress-project example retains its separate primary-source factual provenance; it is not relabelled as vendor-feed data.
Linux resource acceptance measurements
The exact-head WordPress Resource Acceptance CI job builds the
feature-minimal wordpress-review CLI and its ignored resource probe with Rust
1.88 in release profile and a fresh runner-temporary Cargo target. It runs each
synthetic case in a fresh child process and records Linux /usr/bin/time peak
resident set size in KiB together with observed elapsed time, input bytes,
record/association/range counts, relevant-selection counts, retained-import
accounting, and output bytes.
The bounded artifact contains only
wordpress-resource-acceptance.json and
wordpress-resource-acceptance.md.
The synthetic matrix covers a no-input process baseline, a small input, accepted distinct sparse inputs at 4,096, 16,384, 32,768, and 65,536 records, an explicit retained-import rejection at 81,920 records, and a many-relevant- records case. The 32,768- and 65,536-record cases exercise V2 and V3 respectively; their expected policies are independent literal oracles rather than inferred from probe output. A separate valid single-record case between 512 KiB and the additive 768 KiB ceiling proves the enlarged per-record envelope is accepted rather than testing only its upper rejection. Relevant rows occur late in the sparse inputs so a successful prefix parse cannot satisfy the oracle. Separate typed cases cover raw input, individual record, individual field, collection and object-member limits, retained-import limit-plus-one, malformed-tail, duplicate-identity, and interrupted-read boundaries; these cases are not presented as process-memory benchmarks.
Peak RSS is an operating-system observation for the measured child process, not an exact Rust heap measurement or the configured 160 MiB retained-import charge. Independent-process peaks are not subtracted to claim component memory, and noisy elapsed timings are evidence from that runner rather than a product deadline or performance guarantee. Inputs are generated synthetic data, not a Wordfence export. Consequently the checked-in measurement does not authenticate a source or establish that every allowed maximum-size combination or future provider snapshot will be accepted.
| Acceptance surface | Status | Evidence boundary |
|---|---|---|
| Saved plugin/theme/core inventories | Implemented and tested | Synthetic WP-CLI-shaped files; Termivar does not run WP-CLI |
| Declared Wordfence V3 Production input | Implemented and tested | Public regression data is synthetic; separately authorized private snapshot qualification does not redistribute vendor bytes or prose |
| Explicit external version interpretation | Implemented in development source | Operator-selected Termivar profile; source semantics remain not_established |
| Reviewed/holdout evaluator outcomes | Implemented and tested | Six fictional records with literal expected denominators |
| Cross-run semantic Report Compare | Implemented and tested | One benign exact-origin fixture; no remediation causality |
| Feature-enabled native CLI | Implemented and tested in CI | Linux, Windows, and macOS runners; not a fresh-machine certification |
| Linux process resource evidence | Measured in exact-head CI | Synthetic inputs and child-process peak RSS; not a heap cap or real-feed benchmark |
| Supplemental third-party release-byte check | Not run | The mandatory corpus uses original task-owned assets; no external release was downloaded or executed for this feature |
| Current development release bundle | Compiled Preview; explicit runtime opt-in | Untagged alpha.3 builds include wordpress-review; the default build and published alpha.2 archives do not |
| Private vendor-snapshot acceptance | Separately qualified task evidence | No vendor bytes, account, or API credential are committed; one accepted snapshot does not authenticate its source or guarantee future feeds |
| Live API retrieval | Unsupported | The assessment performs no Wordfence network request or key discovery |
| Exploit and impact validation | Unsupported | Both remain not_performed |
Reading the WordPress report
The HTML and Markdown assessment reports present operator-relevant fields from the same typed WordPress audit in structured sections. Source and coverage identifies the native catalogue or external snapshot, its normalization/comparison policy, saved inventory category coverage, and reconciled association counts. Component evidence keeps observed root-response hints distinct from operator-supplied inventory declarations and shows every retained version with its source and confidence class.
Advisory evaluations are mutually grouped as Review candidates, Contradicted on supplied facts, or Evaluation limitations. A review candidate means only that the supplied declarations matched the supported catalogue or explicitly selected interpretation rule. A contradicted result does not establish that the installation is safe. A qualified external match keeps any unsupported-range limitation visible. Missing or conflicting versions, unknown prerequisites, unsupported or invalid ranges, and unresolved source-comparison semantics remain limitations rather than disappearing from the report.
Fixed or patched versions and remediation text are labelled
source-declared remediation information. They are guidance for the
operator's normal compatibility and update process, not an automatic update or
a verified local fix. The report states exploit_execution=not_performed and
impact_validation=not_performed; association counts are not added to the one
WordPress surface observation as vulnerability counts.
JSON retains the complete versioned audit contract and CSV retains its existing bounded audit projection. Offline Verify checks the saved document and bundle contract, not advisory truth. The matching development Report Compare reader can add the semantic WordPress comparison described below; it remains a comparison of supplied documents, not remediation proof.
Fingerprint-audit evidence references are opaque report-local pointers. Current readers validate their syntax, counts, membership in the owning assessment item, and permitted entry/accepted-page source references, but the v1 audit does not cryptographically bind each opaque evidence pointer to one individual asset path. Verify therefore detects contradictory document structure, not a swap of two otherwise valid pointers inside the same fingerprint item's reference set; the manifest likewise establishes byte integrity, not source authenticity.
Offline WordPress comparison
When either supported input to termivar report compare contains a WordPress
audit, the output includes an optional wordpress_review_comparison section
with nested schema termivar-wordpress-review-comparison/v1, additive /v2
when either input carries discovery source content such as page-scoped records,
or additive /v3 when either input carries a validated asset-fingerprint audit.
Additive /v4 is used when either input carries a validated WordPress
supplied-session context.
The outer termivar-report-comparison/v1 schema and its four existing item
groups are unchanged. The section is absent when neither report has a WordPress
audit.
The required --same-scope flag remains only the operator's declaration that
the selected reports are comparable; it does not authenticate a site or prove
that both audits observed the same installation. Within that declared scope,
components match by kind and canonical slug. Native advisory records match by
catalog.id, upstream advisory ID, and component kind/slug. External records
from v4/v5 match by source namespace, upstream ID, and exact canonical component
kind/slug. V6 additionally preserves the raw source kind/slug in association
identity, so two different source spellings do not collapse merely because
they derive the same candidate canonical key. Titles, CVEs, array order, and
other display content do not replace those stable keys.
Discovery audits v2 and v3 also bind WordPress-specific comparison to the opaque
selected-application identity. Matching identities allow semantic facets to be
compared. Known different identities produce not_compared with an
application-scope mismatch; a historical audit without that identity produces
an unknown-scope result. --same-scope cannot force inventories from known
different applications to pair. A changed role base or collection policy within
one selected application is a methodology/source/coverage change, not a newly
installed component or verified remediation. For v3, page mode and association
coverage are methodology/coverage data, while page-to-component source edges
are source content; a lost page representation is not a component removal.
For supplied-session discovery v4, the ready V1/V2 collection policy /v1 and
form-login V3 collection policy /v2 remain distinct. The reader validates the
mode, actual final epoch and S01 resource cross-links before comparing them.
With WordPress session integration selected on only one side, Compare emits
status=not_compared and reason=supplied_session_context_unknown, even for the
same application and V3 policy. Methodology and coverage facets still report
their changes, but component/advisory pairing stays empty. This does not block
the separate top-level item comparison when both root S01 contexts match.
The section reports component evidence, advisory content, affected ranges,
source-declared fix/remediation, methodology, provenance, coverage, and
applicability changes as separate dimensions. It does not assign causality
between them. If one report has no WordPress audit, the result is
not_compared with coverage_changed; the available side is not treated as a
new vulnerability or a resolved condition. The same claim limit applies to an
advisory disappearance or applicability transition.
Supported security.wordpress-review-audit/v1 through /v8 inputs are
interpreted to the historical depth each contract actually contains; later
coverage or provenance fields are not invented for earlier versions. An exact
input SHA-256 identifies bytes, while semantic comparison uses validated typed
content. A byte change alone therefore does not establish a WordPress meaning
change, authenticity, or freshness. Compare imports the two saved reports and
does not rerun the scanner, WP-CLI, PHP, advisory retrieval, or any network
request.
For v5, a change in comparison_policy, comparison_profile, selection
basis, or source-assurance declaration is reported as a methodology change.
The same source record and component retain their stable identity across that
change. A transition between unresolved v4 and evaluated v5 is therefore not
labelled a newly introduced vulnerability or verified remediation.
For v6, changes to mapping revision, mapping state, collision metadata, resource policy, retained-import accounting, or the four-way identity coverage are likewise methodology or coverage changes. A candidate becoming ambiguous, an unresolved association becoming mappable, or an expanded decoder accepting more source rows is not automatically an installed-component change, a newly found vulnerability, or remediation. The matching development Compare and Verify readers validate v6 strictly without scanning, network access, or granting imported data runtime authority. They recompute only document-internal mapping, range, and profile consistency from the imported display data so contradictory saved claims are rejected.
For v7, the required v1–v6 review basis continues to control those same component and advisory checks. The wrapper adds the selected discovery policy, candidate/source coverage, and exact attempt accounting as methodology and coverage facts. An older binary that predates v7 cannot be assumed to read a v7 document even though the underlying basis is historical.
For v8, the underlying review basis still controls installed-version and advisory semantics. The fingerprint section compares stable component-kind/slug/path keys and reports resource-byte, catalogue/reference set, methodology/coverage, and candidate-set changes separately. Identical target bytes can produce a different candidate set when the finite catalogue changes; a missing or failed later acquisition is a coverage change, not a component removal or remediation. An older binary that predates v8 cannot be assumed to read a v8 document.
Result and claim limits
The WordPress audit belongs to the same final assessment report and evidence lifecycle as every other review. It shows observed and operator-declared component evidence, catalogue coverage, interval and prerequisite decisions, source references, uncertainty, and catalogue-supplied, source-attributed remediation text. It does not finalize a detached report or grant execution authority.
Supported structured signals from the existing root response may project one
technology.wordpress-surface-observed@1 item. That item is
Informational / KnowledgeOnly and describes only the observed surface
hints. Advisory applicability remains typed audit data: even
candidate_match_on_declared_facts does not mint a NeedsReview item because
the review has no differential verification evidence. Operator declarations
show what was declared, not authenticated live state. exploit_execution and
impact_validation remain not_performed. Termivar does not synthesize CVSS,
claim a confirmed vulnerability, prove that a fix is absent, or infer that the
installation is safe from a contradicted condition.
Reports describe the supplied catalogue identity, revision, retrieval date, source reference, and evaluation result. The curated example README separately records the exact-byte SHA-256 of its catalogue snapshot. A URL or digest can identify supplied bytes but does not authenticate the source or the operator's inventory. Report strings remain untrusted text subject to the existing output bounds and escaping; escaping is not generic secret removal.
The existing report bundle contains the same assessment rendered once into its
normal formats. Offline bundle verification checks supported structure and
bytes, not advisory truth. Offline Report Compare can show that WordPress
evidence or conclusions changed, but disappearance is not verified
remediation. The matching development reader accepts the optional WordPress
audit, including the current v7 wrapper and discovery audit v3. Older development
readers do not silently reinterpret later strict audit versions; they reject an
unsupported shape. The
already-published v0.10.0-alpha.2 Compare and Verify importers predate all
WordPress audit fields and reject WordPress-extended assessment JSON; use the
matching development reader or a later release for those files. Neither offline
command starts a scan or adds network requests.
Source and scope notes
The existing development references were reviewed on 2026-09-06. The pinned PHP implementation reference for the comparison profiles was additionally reviewed on 2026-09-07. The public-metadata discovery and asset-fingerprint references were reviewed through 2026-09-12:
- WordPress installation in a subdirectory and separate site/core addresses: https://developer.wordpress.org/advanced-administration/server/wordpress-in-directory/
- WordPress content-directory configuration boundaries: https://developer.wordpress.org/advanced-administration/wordpress/wp-config/
- WordPress generator function reference: https://developer.wordpress.org/reference/functions/get_the_generator/
- WordPress REST API discovery relation: https://developer.wordpress.org/rest-api/using-the-rest-api/discovery/
- Current
get_rest_url()implementation, including the no-pretty-permalinkindex.phpform: https://developer.wordpress.org/reference/functions/get_rest_url/ - Theme main stylesheet and bounded file-header behavior: https://developer.wordpress.org/themes/core-concepts/main-stylesheet/ and https://developer.wordpress.org/reference/functions/get_file_data/
- Conditional plugin asset enqueueing and page predicates: https://developer.wordpress.org/plugins/javascript/enqueuing/ and https://developer.wordpress.org/reference/functions/is_page/
- Plugin readme and Stable tag semantics: https://developer.wordpress.org/plugins/wordpress-org/how-your-readme-txt-works/
- Front-end script and stylesheet enqueueing, including cache-busting
verquery semantics: https://developer.wordpress.org/reference/functions/wp_enqueue_script/ and https://developer.wordpress.org/reference/functions/wp_enqueue_style/ - WP-CLI's whole-plugin WordPress.org checksum workflow, which is explicitly broader than this finite observed-asset comparison: https://developer.wordpress.org/cli/commands/plugin/verify-checksums/
- HTTP representation data, content codings, and message completeness: https://www.rfc-editor.org/rfc/rfc9110.html#section-8
- WP-CLI plugin inventory fields and states: https://developer.wordpress.org/cli/commands/plugin/list/
- WP-CLI theme inventory fields and states: https://developer.wordpress.org/cli/commands/theme/list/
- WP-CLI core version output: https://developer.wordpress.org/cli/commands/core/version/
- Wordfence V3 Production/Scanner format and authenticated access contract: https://www.wordfence.com/help/wordfence-intelligence/v3-accessing-and-consuming-the-vulnerability-data-feed/
- Wordfence Intelligence terms reviewed for copy/attribution requirements: https://www.wordfence.com/wordfence-intelligence-terms-and-conditions/
- Wordfence CLI comparison implementation pinned at commit
6f1dfc39b2caad558d097404aab4bb0a1c018308: https://github.com/wordfence/wordfence-cli/blob/6f1dfc39b2caad558d097404aab4bb0a1c018308/wordfence/util/versioning.py - PHP version comparison behavior (broader than Termivar's V1 profile): https://www.php.net/manual/en/function.version-compare.php
- PHP 8.3.0 comparison implementation pinned at commit
d26068059e83fe40de3430a512471d194119bee0: https://github.com/php/php-src/blob/d26068059e83fe40de3430a512471d194119bee0/ext/standard/versioning.c - WordPress security-release index: https://wordpress.org/news/category/security/
WordPress software is distributed under GPLv2 or later according to https://wordpress.org/about/license/. That software license is not presented here as a blanket licence for advisory prose or a vulnerability database. The curated example records only a few attributed factual identifiers and explicit version bounds from the linked official project advisory; it does not copy the advisory narrative or imply permission for bulk redistribution. WordPress is a registered trademark of the WordPress Foundation; Termivar is not affiliated with or endorsed by the WordPress project.