All notable changes to vastlint are documented here. Versions follow Semantic Versioning. GitHub Releases: https://github.com/aleksUIX/vastlint/releases
Dependency maintenance only. No rule changes, no behaviour changes; the catalog stays at 223 and any tag that validated on 0.11.7 validates identically here.
js-yamloverride raised to 4.3.1, closing GHSA-5p4m-2wfm-xmqj (quadratic CPU consumption resolving!!omap). The package reaches this repo only as a transitive@vscode/vscedev dependency, so exposure was limited to extension packaging, never to shipped code.
clap4.6.4 to 4.6.6,rmcp3.0.1 to 3.1.1.ovsx1.0.2 to 1.1.0 (VS Code publishing, dev only).- CI actions:
github/codeql-action/upload-sarif4.37.4 to 4.37.6,actions/attest-build-provenance4.1.1 to 4.2.2,Swatinem/rust-cache2.9.1 to 2.9.2, anddtolnay/rust-toolchainmoved to a newer pinned commit.
One new rule for a class nothing was checking: elements that repeat where the spec allows one. A tag that validated clean on 0.11.6 can report findings here. Catalog: 222 to 223.
-
VAST-2.0-duplicate-singular-element(error). Every VAST schema from 2.0 to 4.2 caps children such as<AdSystem>,<AdTitle>,<AdServingId>,<VASTAdTagURI>,<Duration>and<ClickThrough>at one occurrence per parent. Nothing here had ever counted them, and a repeat is genuinely ambiguous: two<VASTAdTagURI>in a wrapper do not say which chain hop the player should follow, and two<Duration>in a<Linear>do not say how long the ad is.The gap was invisible while the XSD covered it.
vast_4.4.xsdwrapsvastInLine_typeandvastWrapper_typein<xs:choice minOccurs="0" maxOccurs="unbounded">, which drops cardinality for every child it contains, so on a 4.4 document neither the schema nor this validator noticed. Reported upstream as IAB VAST issue #58 with a fix in PR #59; the rule stands regardless of how that lands, because the prose has required these cardinalities since 2.0.Forty (parent, child) pairs, derived from
vast_4.2.xsdrather than by hand, dispatched through the element walker so a container is covered wherever it appears. Verified against the full IABVAST_Samplescorpus and every fixture in this repo: 196 files, zero false positives. A guard test fails the build if the rule ever fires on a fixture not namederr_duplicate_*.
- Rule count corrected across the surfaces that had drifted:
ROADMAP.md,docs/tutorial.md,docs/mcp-agentic.md, thevastlint-corecrate description, the MCPserver.json, and the Chrome, VS Code and npm package manifests all quoted 220 against a catalog of 222.
Two new rules and one newly-reachable existing rule. A tag that validated
clean on 0.11.5 can report findings here: <Duration> is now format-checked
under <NonLinear>, and QR geometry without an image is reported. Catalog:
220 to 222.
VAST-4.4-qrcode-missing-image-url(warning). The mirror of-missing-scan-urlfor the other asset. Geometry tells the platform where to draw a QR code and how big; without<QrCodeImageUrl>there is nothing to draw. Like every rule in that group it comes from the guidance rather than the XSD, which requires none of the four QR children (they sit in an unbounded choice withminOccurs="0").VAST-3.0-adparameters-xmlencoded-value(warning).xmlEncodedis a boolean that nothing had ever looked at, soxmlEncoded="yes"validated clean. The player reads it to decide whether to XML decode the payload before handing it to VPAID or SIMID, and a value it cannot parse means it guesses. Acceptstrue,false,1and0, case-insensitively.
<Duration>is format-checked under<NonLinear>. The CTV Ad Portfolio put a<Duration>there on 4.x, and the format rule had only ever been reached under<Linear>and, since 0.11.4, inside the extension container. No traversal was written for the NonLinear case and no fixture covered it, so nothing failed. Migrating the rule to element dispatch covered it without a line of code aimed at the case, which is the clearest evidence so far that the 0.11.5 architecture does what it claims.
-
<Icon>,<Tracking>and<Duration>migrated to element dispatch, removing the last of the duplicated element traversals.<Icon>had three call sites across two modules;<Tracking>had two.Two constraints surfaced doing it, both now explicit and tested.
<Tracking>is genuinely position-dependent:<Verification>tracking uses its own vocabulary (verificationNotExecuted) and its own checker, so the Linear and NonLinear event enum must not be applied there. And the element gates had to become per element rather than per subtree, because<AdParameters>is valid under a VAST 2.0<NonLinear>while the 4.x content model around it is not.Both were found by diffing the full fixture corpus rather than by the test suite, which is the argument for running that diff on every migration of this kind. Regression tests added for each.
Rules whose subject really is a location keep their own traversals:
VAST-2.0-nonlinear-resource,SIMID-1.0-simid-mediafile-required, the quartile-coverage warning, and the CTV Ad Portfolio container rules.
No rule changed and no finding moved. All 631 findings across the 127-fixture corpus are byte-identical to 0.11.4, paths included. This release changes how the element rules reach their subject, not what they report, so a tag that validated a particular way on 0.11.4 validates identically here.
-
Element rules are dispatched by name instead of by hand-written traversal. Twice now the same defect has shipped: the CTV Ad Portfolio moved
<MediaFiles>somewhere new, the rules that check its contents did not follow, and a defective<MediaFile>validated clean until someone wrote another traversal. 0.11.1 fixed it for<NonLinear>, 0.11.4 for the VAST 2.0 extension container. A third location would have been a third fix.rules/walk.rsvisits every element belonging to the document proper, paired with the location to report it at. It descends into standardised IAB extension containers, whose children are real VAST, and stops at any other<Extension>or<CreativeExtension>, whose payload is a vendor's private format. That allowlist is now the entire maintenance surface for a new container: one entry, rather than one traversal per rule module.rules/elements.rsregisters the position-independent rules against the element name.<MediaFile>,<Mezzanine>and<InteractiveCreativeFile>move first, collapsing three call sites into one dispatch.Position-dependent rules keep their own traversals, which is correct: their subject really is a location.
SIMID-1.0-simid-mediafile-requiredstays Linear-only, and thectv_portfolio.rsrules are about the container rather than any one child. The two version constraints survive intact and are tested:<Mezzanine>attributes stay 4.1+, and the NonLinear content model stays 4.x so a 3.0 document still reports the construct once as an unknown child rather than twice.
Measured against 0.11.4 with criterion, same bench file on both revisions.
The pods and the CTV Ad Portfolio fixtures are at parity (no change detected,
p > 0.05). Small fixtures are within 0-3%. valid_4.3_with_verification is the
outlier at roughly +10%, about 1.3 µs on a 16 µs validation.
The cost lands on documents that gain nothing from the dispatch, which is worth stating plainly. Where a portfolio container exists the walker replaces the hand-written traversals it deletes, so the work nets out; where one does not, the walk is pure addition. It is largest on small, element-dense tags, where a fixed per-node cost is a bigger fraction of a smaller total.
An eager first implementation built a path string for every visited node and cost +16% to +19% across the board. Paths are now joined on demand, and sibling counting is limited to elements that carry an index.
- Benchmark cases for the CTV Ad Portfolio path (
ctv_portfolio_9_ads,ctv_portfolio_clean) and for element-count scaling (pod_50_ads). None of the previous cases contained a standardised extension container, so the benchmark could not see the cost of the code that walks one. the_same_defective_media_file_reports_identically_everywhere: the same bare<MediaFile>placed in every container the spec has put one in must report the same rules. This test fails on 0.11.0 and again on 0.11.3, so it would have caught both original bugs. Verified non-vacuous by mutation.
Upgrading can fail a build that passed. Existing rules now fire inside the
CTV Ad Portfolio extension container, at their existing severities, most of them
errors. No rule id is new and no setting changed, so a tag that validated clean
on 0.11.3 can report errors without anything on your side changing. Only VAST
2.0 and 3.0 tags using <Extension type="ctv_ad_portfolio"> are affected. If
you gate CI on vastlint and serve the CTV Ad Portfolio on the legacy path,
validate a sample before you bump.
-
The CTV Ad Portfolio extension container now reuses the element rules that already existed. This is the same defect 0.11.1 fixed for the 4.x
<NonLinear>content model, one container over. That release taught the element rules to traverse<NonLinear><MediaFiles>; the VAST 2.0 container has the identical shape and did not get the same treatment, so everything inside it arrived with no validation at all.<Extension type="ctv_ad_portfolio">exists to give VAST 2.0 and 3.0 the media delivery model those versions have no element for. Its children are the ad's real media, duration, icons and tracking, not a vendor payload. A<MediaFile>in there with nodelivery,type,widthorheightproduced nothing; the identical element under<Linear>produces three errors. Since the container is the only delivery vehicle on the legacy path, a portfolio tag could carry an unusable media file and validate clean.No new rule ids.
VAST-2.0-mediafile-delivery,-type,-dimensions, theVAST-2.0-mediafile-*value rules,VAST-4.1-mezzanine-*,VAST-4.0-interactive-creative-no-api,VAST-4.1-interactive-creative-type,VAST-3.0-icon-program,-width,-height,-xposition,-yposition,-attrs,VAST-2.0-duration-formatandVAST-4.1-tracking-event-valuenow fire wherever their element appears. Catalog stays at 220. Both theInLineandWrappercontainers are covered.Unlike the NonLinear traversal, this one is not version gated. The container is the legacy encoding, so gating on 4.x would switch the checks off in the only place it is ever used. Per-rule version gating still applies:
VAST-4.1-mezzanine-*stays 4.1+, so a 2.0 document is not told about a requirement its schema never had.Scoped by
Node::is_standardised_iab_extensionrather than by a literal type string. A vendor<Extension>carrying its own private<MediaFile>is left alone, and a future IAB container inherits the traversal by being added to that one allowlist rather than by hand-writing another walk.
err_ctv_portfolio_legacy_all_modes.xml, which exercises every failure mode of the legacy extension path in one document, nine ads isolating rules that are mutually exclusive inside a single container. Three tests read it: an exact-set assertion that fails in both directions, a guard that every legacy-reachable rule in the category appears in it (checked against the catalog, so the fixture cannot fall behind), and a 2.0-versus-3.0 comparison.- Regression tests that the extension path is not version gated: both 2.0
fixtures re-declared as 3.0 must report identical issue sets, and the 4.x
<Extension ext="adcom">shape rules must stay off below 4.0 whichever legacy version the document declares. - A test that the two gating models compose. The traversal ignores the document
version; the rules it reaches do not.
<Mezzanine>proves it, firing inside the container on 4.2 and staying silent on 2.0, which has no such requirement. A traversal that deliberately ignores the version is exactly where the 0.11.2 bug class would come back. - A test that a vendor
<Extension>carrying its own<MediaFile>and<Duration>is left alone, so the traversal cannot start reporting private payloads as defective VAST.
Dependency maintenance. No rule changed, nothing was added or removed from the catalog, and the MCP surface is byte-identical, so a tag that passed on 0.11.2 produces the same findings on 0.11.3.
-
Three transitive dev-dependencies of
@vscode/vscehad advisories open against the versions the extension pinned. All three are packaging-time tools, so none of them ship in the CLI, the WASM package, or the published extensions.undici7.28.0 → 7.29.0, clearing five advisories: GHSA-4cwx-7wf7-3272 (high, cross-user information disclosure and parse-time crash via degenerate private cache directives), GHSA-8xcm-r25x-g524 (downstream response desynchronization via the retry interceptor), GHSA-m8rv-5g2x-5cg5 (CRLF injection via a blob-like bodytypeproperty), GHSA-jr45-8vmc-qm54 (cross-user information disclosure via whitespace around equals inCache-Controldirectives), and GHSA-v3r7-h72x-cjcm (cookie attribute injection via an unsanitized domain and unparsedsetCookiefields).brace-expansion5.0.8 → 5.0.9 (GHSA-rgw5-rvv9-x895, high): denial of service via unbounded intermediate arrays, which bypasses the CVE-2026-14257 mitigation.fast-uri3.1.4 → 3.1.5 (GHSA-7p8r-x3mc-p8w7, high): host confusion via a backslash authority introducer.
-
brace-expansionandfast-uriwere both pinned in 0.10.1 against earlier advisories. Each of those pins has now been superseded by a new finding against the version that fixed the last one. -
npm audit: 0 vulnerabilities invscode/,npm/,chrome/and the root workspace.cargo audit: no known vulnerabilities across 252 crates.
rmcp2.2.0 → 3.0.1, a major version of the MCP SDK. 3.0 makesschema_for_outputinfallible: it returnsArc<JsonObject>where 2.x returnedOption<Arc<JsonObject>>. vastlint's local wrapper existed only to unwrap thatOption, so it is gone and the upstream function is aliased at the import instead. The server still advertises protocol version2024-11-05, the sametoolscapability and the same seven tools, so MCP clients see no change.toml1.1.3+spec-1.1.0 → 1.1.4+spec-1.1.0, semver-compatible inside the existing range. Lockfile-only.- CI action pins refreshed:
github/codeql-action/upload-sarif4.37.3 → 4.37.4,docker/login-action4.5.1 → 4.6.0. - VS Code extension:
@types/node26.1.1 → 26.1.2 (dev-dependency).
This release only removes findings. Four checks reported against documents that their own schema accepts, all of them because a requirement introduced by one VAST version was applied to every version. Nothing new fires, so a tag that passed on 0.11.1 still passes. Tags that failed on attributes their spec never required now pass, or report warnings instead of errors.
-
A VAST 1.0 document is named as one instead of being called malformed. VAST 1.0's root element is
<VideoAdServingTemplate>, so every 1.0 tag collectedVAST-2.0-root-elementat error severity, message "Root element must be<VAST>". That describes a document written to a published IAB spec as broken. Legacy 1.0 tags are still served, and the four samples IAB ships in its own VAST 1-2.0 sample set are all of this shape.The rule now recognises the root, reports it as a warning naming the version ("Document is VAST 1.0 ... superseded by VAST 2.0 and is not validated further"), and stops. VAST 1.0 has its own element vocabulary (
<Video>,<URL>inside<Impression>), so continuing would report every element of a correct 1.0 document as unknown. -
<Mezzanine>attributes are required from 4.1, not from 4.0. VAST 4.0 declares<Mezzanine>as a barexs:anyURIelement with no attributes at all (<xs:element name="Mezzanine" type="xs:anyURI">).delivery,type,widthandheightarrive in 4.1, where it becomes a complexType. A 4.0 document with a mezzanine URL produced four errors for attributes its schema does not define. The fourVAST-4.1-mezzanine-*rules are now gated on 4.1+. -
<Icon>placement attributes drop to warning on 4.x. VAST 3.0 §2.3.6.4 marksprogram,width,height,xPositionandyPositionrequired, and they stay errors there. The 4.1 and 4.2 XSDs declare all five without auseattribute, which makes them optional, and the 4.3 Icon attribute table has no required column at all. A player still needs dimensions and position to place an icon, so 4.x omissions are still reported, as warnings. -
vendorandapiFrameworkdrop to warning on 4.0. VAST 4.0 introduced<AdVerifications>withvendoron<Verification>andapiFrameworkon<JavaScriptResource>bothuse="optional"in vast4.xsd. The requirement is 4.1 prose (the 4.1 and 4.2 schemas still say optional; it only reaches the schema in the 4.4 draft).VAST-4.1-verification-vendorandVAST-4.1-js-resource-apiframeworkstay errors from 4.1 and are warnings on 4.0.Default severities in the rule catalog are unchanged: they describe the version that made the attribute required, and
RULES.mdlists defaults.
-
Every in-repo version number now equals the released tag. They had drifted across 0.5.0 (
server.json), 0.6.2 (crates and npm), 0.9.1 (Chrome) and 0.10.1 (VS Code) while every published artifact carried the tag, because CI stamps the tag into each of them at build time and never pushes the result. Nothing shipped was ever wrong. The stale values only misled readers of the repo, and made a localcargo run --versionreport 0.6.2.RELEASE.mdis the cause and is rewritten: it asked for manual VS Code and Chrome version bumps that CI overwrites, so those numbers were maintained by hand and published by nobody. It now states the tag as the single source, lists which job stamps which file, and drops the manual steps.Two earlier entries name extension versions that were never published (0.9.0/0.8.0 in 0.10.0, 0.10.0/0.9.0 in 0.11.0). The Marketplace received 0.10.0 and 0.11.0. Left as written rather than rewriting released notes.
-
README.mdshowedvastlint-core = "0.1"as the dependency snippet andROADMAP.mdstill described the core library as v0.8. Both now say 0.11.
Upgrading from 0.11.0 can fail a build that passed. Existing rules now fire in places they previously skipped, at their existing severities, most of them errors. No rule id is new and no setting changed, so a tag that validated clean on 0.11.0 can report errors on 0.11.1 without anything on your side changing. Every one of those findings was always true; 0.11.0 simply could not see them. Only CTV Ad Portfolio NonLinear creative is affected. If you gate CI on vastlint and serve NonLinear ads, validate a sample before you bump.
-
The CTV Ad Portfolio content model now reuses the element rules that already existed. Moving
<MediaFiles>under<NonLinear>and<Icons>under<NonLinearAds>changed where those elements live, not what they require, but the rules that check them only ever traversed<Linear>. A<MediaFile>inside a NonLinear with nodelivery,type,widthorheightproduced nothing at all; the identical element under<Linear>produced three errors. Same for an<Icon>under<NonLinearAds>missingprogram,width,height,xPositionoryPosition, which is five errors and a warning under<Linear>.This was the widest hole in the 4.4 support, since delivering video through NonLinear is the entire point of the new content model. Both traversals are gated on 4.x, the same gate the content model itself uses, so a 3.0 document still reports the construct once as an unknown child rather than twice.
The rest of the container had the same problem, so the fix covers it:
<Mezzanine>and<InteractiveCreativeFile>inside a NonLinear<MediaFiles>were unvalidated too, and the SIMID pattern the guidance calls preferred puts an<InteractiveCreativeFile>exactly there.No new rule ids.
VAST-2.0-mediafile-delivery,-type,-dimensions,VAST-4.1-mezzanine-delivery,-type,-width,-height,VAST-4.0-interactive-creative-no-api,VAST-4.1-interactive-creative-type,VAST-3.0-icon-program,-width,-height,-xposition,-ypositionandVAST-3.0-icon-attrsnow fire wherever their element appears. Catalog stays at 220. -
VAST-4.0-interactive-creative-no-apiandVAST-4.1-interactive-creative-typereported the line and column of the enclosing<Linear>rather than of the<InteractiveCreativeFile>they are about. Editors and CI annotations pointed at the wrong element.
- Fixtures for the three CTV Ad Portfolio formats that had none: Screensaver
(
plcmt6), Squeezeback (8) and In-Scene (9), modelled on IAB's published examples. Every format in the portfolio now has a tag that must validate clean, which is what proves the Format-to-Signal rules do not false-positive on the reference implementation. - Regression tests for the four CTV rules that shipped without one:
VAST-4.4-nonlinear-mediafiles-empty,VAST-4.4-qrcode-size-percent,VAST-4.4-qrcode-position-attrsandVAST-2.0-ctv-portfolio-mediafiles-empty. All four were reachable and correct; none had a test asserting they fire. Every rule in the category now has one.
- The VS Code and Chrome extensions pick up the corrected traversal. Both ship at the release version, as they always have.
-
CTV Ad Portfolio support for the VAST 2.0 extension path. The pull request that landed
vast_4.4.xsdon 2026-07-17 also addedextensions/ctv_ad_portfolio.mdandextensions/ctv_qrcode.md, which deliver the same six formats through VAST 2.0 rather than 4.x. That path had no coverage: none of the AdCOM value or QR checks were reachable, and the generic extension rules reported IAB's own examples as defective. Six new rules for the container's own failure modes:VAST-2.0-ctv-portfolio-creative-id-required(the binding that fails silently when an ad has multiple creatives),VAST-2.0-ctv-portfolio-creative-id-unmatched,VAST-2.0-ctv-portfolio-mediafiles-required,VAST-2.0-ctv-portfolio-mediafiles-empty,VAST-2.0-ctv-portfolio-no-renderable-assetandVAST-2.0-ctv-portfolio-no-duration. Catalog: 212 to 218 rules. -
Format-to-Signal consistency, two rules that read the AdCOM signals as a set rather than one at a time:
VAST-4.4-adcom-pos-format-mismatchandVAST-4.4-adcom-playbackmethod-format-mismatch.plcmt,posandplaybackmethodcan each sit inside its own enumeration and still describe two formats at once, which is invisible to the per-value rules:plcmt=5(Pause) withpos=12(a Squeezeback layout) passed all three. The scope is the container that carries the signals, so both encodings are covered: the whole<Extensions>block on 4.x, the single<Extension type="ctv_ad_portfolio">on 2.0. Catalog: 218 to 220 rules.Both stay quiet where the guidance hedges.
posis checked for the four formats with a published value set, never for In-Scene, whose table cell reads "NA", and never forpos=0, AdCOM "unknown".playbackmethodis checked only against the two rows the reference table states outright, 8/9 Pause and 10/11 Screensaver, in both directions; the other three rows read "typically 1 or 2", which is advice rather than a constraint, so an Overlay declaring an autoplay method is not reported. An out-of-rangeplcmtsuppresses both, sinceVAST-4.4-adcom-plcmt-valuehas already reported it and the pairing would be guesswork. -
explain_rulein the MCP server returns fix guidance for the CTV Ad Portfolio rules instead of the generic fallback.
VAST-2.0-extension-misplaced-elementno longer fires inside a standardised IAB extension container.<Extension type="ctv_ad_portfolio">carriesMediaFiles,Duration,TrackingEvents,IconsandAdParametersby design, because it exists to give VAST 2.0 a NonLinear delivery model it never had.- Version inference ignores elements inside those containers. A conforming
2.0 tag carrying a SIMID
InteractiveCreativeFilein its extension was reported asVAST-2.0-version-mismatch, which is the one thing the extension is designed to do. VAST-4.4-adcom-attr-not-motionaccepts 19 and 20. AdCOM 1.0-202607 added 19 Contains advertiser QR Code and 20 Support alpha channel transparency alongside the three motion attributes, and IAB's own examples declare 19.- The media rules only apply when the extension is the delivery vehicle. A
creative that renders from a native
StaticResourceand uses the container only to declare AdCOM signals is conforming.
All five VAST 2.0 examples published in the two extension documents now validate clean.
- VS Code extension 0.10.0 and Chrome extension 0.9.0 pick up the new rules.
The
vastlint.vastVersionsetting offers4.4, which the CLI has accepted since 0.10.0. specs/vast_4.4_reference.mdcovers the VAST 2.0 extension path, the Format-to-Signal cross-check and its deliberate gaps, and corrects theattr=20entry in its list of defects in IAB's published examples: AdCOM 1.0-202607 defines 20, so it was never a defect.
- Dependency updates via Dependabot:
tokio1.53.0 → 1.53.1,clap4.6.2 → 4.6.4 (clap_derive4.6.1 → 4.6.4, which moves its macro backend tosyn3). Both are semver-compatible inside the existingtokio = "1"andclap = "4"ranges, so the change is lockfile-only. - CI action pins refreshed:
actions/checkout7.0.0 → 7.0.1,github/codeql-action/upload-sarif4.37.1 → 4.37.3,docker/login-action4.4.0 → 4.5.1,ossf/scorecard-action2.4.3 → 2.4.4. cargo audit: no known vulnerabilities.npm audit: 0 vulnerabilities, unchanged since 0.10.1.
- Cleared all six open Dependabot alerts. Every one is a transitive dev-dependency of the build tooling, so none of them ship in the CLI, the WASM package, the published extensions, or any other runtime artifact.
- VS Code extension build chain (transitive under the
@vscode/vscedev-dependency):brace-expansion5.0.7 → 5.0.8 (GHSA-mh99-v99m-4gvg, unbounded expansion length causing an out-of-memory crash),fast-uri3.1.2 → 3.1.4 (GHSA-4c8g-83qw-93j6 host confusion via failed IDN canonicalization, GHSA-v2hh-gcrm-f6hx host confusion via a literal backslash authority delimiter),linkify-it5.0.1 → 5.0.2 (GHSA-v245-v573-v5vm, quadratic-complexity DoS in themailto:validator scan-loop). - Example apps
vastlint-client-browser,vastlint-client-react-scratchandvastlint-react-demo(transitive undervite):postcss8.5.15 → 8.5.23 (GHSA-r28c-9q8g-f849, path traversal in previous-source-map auto-loading viasourceMappingURLleading to arbitrary.mapfile disclosure). - The
brace-expansion5.0.7 pin from 0.9.1 was superseded by a new advisory a week after it landed. 5.0.8 raises its floor to Node 20. - All four fixes resolved in-range, so no
overridesentries were added and nopackage.jsonchanged. Lockfile-only. npm audit: 0 vulnerabilities in the root workspace,npm/,vscode/,chrome/and all three example apps.cargo audit: no known vulnerabilities across 248 crates.
-
VAST 4.4 and the IAB CTV Ad Portfolio (
ctv_portfolio.rs, new rule category). IAB Tech Lab finalised the CTV Ad Portfolio signaling guidance on 2026-07-22 and landedvast_4.4.xsdin the VAST repo on 2026-07-17. vastlint now recognisesversion="4.4", accepts the new content model, and adds 17 rules for it. Catalog: 195 → 212 rules.The two sources are at different stages of the same process and vastlint weights them accordingly. The signaling guidance is final, so rules derived from it (
RuleSource::CtvAdPortfolio, new) carry normal weight. The 4.4 XSD is a working-group draft by its own annotation, so schema-only findings stay at warning or info rather than getting ahead of the working group. The only errors are constructs malformed under any version: a non-integer AdCOM payload, a QR position in pixels.New content model accepted on any VAST 4.x document, not just 4.4:
<MediaFiles>,<Duration>and<NonLinearCustomClick>under<NonLinear>, and<Icons>under<NonLinearAds>. This is deliberate: every VAST example in IAB's final guidance declaresversion="4.2"while using these constructs, so gating on 4.4 would have rejected conforming CTV traffic.VAST-2.0-nonlinear-resourcenow accepts<MediaFiles>as a fourth resource form. VAST 3.0 and below are unchanged; a regression fixture guards this.Rules:
VAST-4.4-version-attribute(info, flags the draft status),VAST-4.4-nonlinear-no-renderable-asset,VAST-4.4-nonlinear-mediafiles-empty,VAST-4.4-nonlinear-simid-iframe,VAST-4.4-nonlinear-video-no-duration,VAST-4.4-adcom-extension-unknown-signal,VAST-4.4-adcom-extension-type-mismatch,VAST-4.4-adcom-signal-not-integer,VAST-4.4-adcom-plcmt-value,VAST-4.4-adcom-playbackmethod-value,VAST-4.4-adcom-pos-value,VAST-4.4-adcom-attr-not-motion,VAST-4.4-qrcode-position-attrs,VAST-4.4-qrcode-position-percent,VAST-4.4-qrcode-size-attr,VAST-4.4-qrcode-size-percent,VAST-4.4-qrcode-missing-scan-url. -
SIMID in NonLinear
<MediaFiles>: the CTV Ad Portfolio guidance names<InteractiveCreativeFile apiFramework="SIMID">inside the NonLinear<MediaFiles>container "the preferred VAST 4.4 pattern" and deprecates<IFrameResource apiFramework="SIMID">. The existing SIMID type/URL/HTTPS/variableDuration rules now apply there.SIMID-1.0-simid-mediafile-requiredstays Linear-only (VAST-4.4-nonlinear-no-renderable-assetcovers the NonLinear case, where a static resource is an equally valid fallback), andSIMID-1.1-nonlinear-simid-no-iframeno longer fires when the creative already uses the preferred form. -
specs/vast_4.4_reference.md: the delta against 4.3, the full AdCOM signal tables (plcmt 5–9, playbackmethod 8–11, pos to 17, attr 21–23), and two things worth knowing before trusting the draft schema. The draft is scoped to the CTV work:AltText,BlockedAdCategories,ExpiresandIconClickFallbackImage(s)are not carried over, and since nothing in the guidance touches them, vastlint reads that as scope rather than deprecation and models 4.4 as 4.3 plus the additions. The draft is also stricter onExtensionthan 4.2 (@typerequired, custom children restricted to##other), which would flag a lot of deployed tags, so vastlint waits for the schema to settle before enforcing either.
--vast-versionaccepts4.4.VAST-2.0-root-version-valuerecognises4.4so a 4.4 tag is told about the draft status rather than that its version string is unrecognised.RuleSourcegainsCtvAdPortfoliofor rules sourced from the finalised signaling guidance, distinct fromVastXsdfor rules sourced from the draft schema.- VS Code extension 0.9.0 and Chrome extension 0.8.0 pick up the 4.4 rule set.
chrome/package.jsonandchrome/manifest.jsonare unified on one version number.
- Dependency updates via Dependabot:
tokio1.52.3 → 1.53.0,criterion0.7.0 → 0.8.2 (dev-dependency, benches),serde1.0.228 → 1.0.229,serde_json1.0.150 → 1.0.151,toml1.1.2+spec-1.1.0 → 1.1.3+spec-1.1.0,regex1.13.0 → 1.13.1,clap4.6.1 → 4.6.2. - CI action pins refreshed:
softprops/action-gh-release3.0.1 → 3.0.2,github/codeql-action/upload-sarif4.37.0 → 4.37.1,dtolnay/rust-toolchainpinned commit refreshed,taiki-e/setup-cross-toolchain-action1.41.0 → 1.42.0,actions/setup-node6.4.0 → 7.0.0. cargo audit: no known vulnerabilities.
- Content quality rules (
quality.rs, new rule category):VAST-2.0-adtitle-quality(warning) flags known placeholder<AdTitle>values (test,Ad 1,untitled, empty, ...);VAST-2.0-adsystem-quality(info) flags placeholder<AdSystem>values;VAST-2.0-adsystem-no-version(info) flags<AdSystem>without aversionattribute. Conservative placeholder lists (phf sets) keep false positives near zero; all three can be disabled per-rule invastlint.toml. Catalog: 191 → 195 rules. VMAP-1.0-display-break-no-companions(info): an<AdBreak>whosebreakTypeincludesdisplaybut whose inline<VASTAdData>VAST contains no<CompanionAds>has nothing to display.<AdTagURI>sources are not checked (zero-I/O core).vastlint init: generates a startervastlint.tomlwith every rule listed at its default severity, commented out.--out <path>and--forceflags; refuses to overwrite without--force(exit 2).- Criterion benchmark (
cargo bench -p vastlint-core): validation throughput per fixture plus a 10-ad pod. Confirms the sub-1ms ARCHITECTURE.md target with wide margin (7-16 µs typical, ~140 µs for the pod). - WASM:
document_typein validation results:validate()/validateWithOptions()now returndocument_type: "VAST" | "VMAP" | "DAAST", matching the CLI and MCP surfaces. npm TypeScript definitions updated.
- Namespace-prefixed attributes no longer false-flag as
VAST-2.0-unknown-attribute: attributes in a foreign namespace (xsi:type,xsi:schemaLocation,xmlns,xmlns:*, vendor prefixes) are not governed by VAST's per-element attribute allowlists and are now skipped. Schema-annotated, spec-compliant tags stay clean. Separately,AdID(the VAST 2.0 casing of the<Creative>creative-id attribute, renamedadIdin 3.0+) is now accepted on<Creative>. Both were false positives flagged on real-world compliant tags.
- VS Code extension build broken by TypeScript 7:
vscode/tsconfig.jsonsetmoduleResolution: "node"(alias fornode10), an option TypeScript 7.0 removed outright (error TS5108). Introduced by thetypescript6.0.3 → 7.0.2 dependabot bump in v0.8.2; PR CI didn't catch it because the VS Code build only runs inside the release workflow's smoke-test job, not the PR CI matrix, so it surfaced as two failed release runs (v0.8.2, v0.8.3) instead. Removed the explicit option;tscinfers the equivalent resolution frommodule: "commonjs".
quick-xml0.40.1 → 0.41.0 infuzz/Cargo.lock(RUSTSEC-2026-0194, quadratic-time duplicate-attribute check; RUSTSEC-2026-0195, unbounded namespace-declaration allocation).fuzz/is excluded from the main workspace and resolves independently; its lockfile had drifted behind the rootCargo.lock, which has carried the patched version since v0.8.1.- Removed
crates/vastlint-nif/Cargo.lock.vastlint-nifis a workspace member, so real builds (including the release workflow'scargo build -p vastlint_nif) always resolve against the rootCargo.lock. This nested lockfile was orphaned since Release 0.4.24, still pinnedcrossbeam-epoch 0.9.18/quick-xml 0.40.1, and was never consumed by any actual build — but Scorecard's OSV scan reads everyCargo.lockin the repo regardless of whether cargo would use it, which is why alert #11 (RUSTSEC-2026-0194/0195/0204) kept reappearing across releases going back to April despite the real, shipped artifacts never being affected.
No source or runtime behavior changes; both fixes are lockfile-only.
regex1.12.4 → 1.13.0.rmcp2.1.0 → 2.2.0 (MCP server).@types/node26.1.0 → 26.1.1 (vscode dev dependency).typescript6.0.3 → 7.0.2 (vscode dev dependency).- CI action pin refreshed:
github/codeql-action/upload-sarif4.36.3 → 4.37.0. - Fixed a stale branch-protection configuration on
main: required status check contexts referenced CI jobs (CI / Clippy,CI / Test (*)) from before the CI workflow consolidated clippy into thetestjob, which was blocking every PR merge regardless of actual CI result. Updated to the current job names (Test (ubuntu-latest),Test (macos-latest),Test (windows-latest),Security audit).
No source or behavior changes; dependency and infra maintenance only. cargo audit clean (0 vulnerabilities), 0 open Dependabot security alerts.
crossbeam-epoch0.9.18 → 0.9.20 (RUSTSEC-2026-0204): thefmt::Pointer/fmt::Displayimpl forAtomic/Shareddereferenced the underlying pointer, causing an invalid dereference when that pointer was null. Transitive dependency viarayon(used by the Erlang NIF); not reachable at runtime, patched in the lockfile.
rmcp1.8.0 → 2.1.0 (MCP server): upgrade to the 2.x line of the Rust MCP SDK. MCP smoke tests (initialize, tools/list, and tools/call across validate_vast, fix_vast, list_rules, explain_rule) pass unchanged.- Dependency update via Dependabot:
@types/node26.0.1 → 26.1.0 (vscode dev dependency). - CI action pins refreshed:
docker/build-push-action7.2.0 → 7.3.0,docker/setup-qemu-action4.1.0 → 4.2.0,docker/setup-buildx-action4.1.0 → 4.2.0,docker/login-action4.2.0 → 4.4.0,github/codeql-action/upload-sarif4.36.2 → 4.36.3, anddtolnay/rust-toolchainpinned commit refreshed.
- IAB Content Taxonomy authority validation (4 new rules, catalog now 191):
authorityattribute values on<Category>(4.0+) and<BlockedAdCategories>(4.1+) are validated for URL well-formedness (VAST-4.0-category-authority-not-uri,VAST-4.1-blockedadcategories-authority-not-uri, Warning) and recognition against the IAB Content Taxonomy registry hosts,iabtechlab.comand subdomains plusiab.com, including version-qualified forms such asiabtechlab.com/IABTC/2.2(VAST-4.0-category-authority-unknown,VAST-4.1-blockedadcategories-authority-unknown, Info). Custom taxonomies stay legal; only malformed values warn.
- New
GOVERNANCE.md,CODE_OF_CONDUCT.md(Contributor Covenant 2.1), rewrittenCONTRIBUTING.md(DCO, coding standards, test policy), and a SECURITY.md assurance case, as part of OpenSSF Best Practices Silver work. - Release workflow: crates.io publish failures are no longer silently swallowed (v0.6.3 through v0.7.2 never reached crates.io; re-published via the new
publish-crates.ymlrecovery workflow).
--share: uploads a validation report (rule IDs, severities, XPath locations, summary counts) to vastlint.org and prints a publicvastlint.org/r/<id>link for pasting into Slack/GitHub/PRs. Never sends the input XML.--contribute-sample: opt-in only, off by default. Sends the tag's raw XML to vastlint.org to help refine rules. Known tracking identifiers (device IDs, IPs, consent strings) are redacted server-side before storage; samples are never made public. Independent of--shareand--telemetry.
ws8.20.1 → 8.21.0 (GHSA-96hv-2xvq-fx4p): memory-exhaustion DoS from tiny fragments and data chunks. Transitive dev-tooling dependency (jsdom), not shipped in any runtime artifact.quinn-proto0.11.14 → 0.11.15 (RUSTSEC-2026-0185): transitive dependency advisory.
- Dependency updates via Dependabot:
rmcp1.7.0 → 1.8.0 (MCP server),@types/node26.0.0 → 26.0.1 andovsx1.0.0 → 1.0.2 (vscode dev dependencies). - CI action pins refreshed:
github/codeql-action/upload-sarif3.28.18 → 4.36.2,actions/attest-build-provenance4.1.0 → 4.1.1.
- VAST macro validation: a new
macrosrule chain inspects[MACRO]substitution tokens in tracking, click, error, impression, and media URLs against the IAB VAST macro list. Five new rules:VAST-2.0-macro-unknown(warning): bracketed token that is not a recognised IAB macro (typo, or a vendor-specific macro to allowlist).VAST-2.0-macro-lowercase(warning): a recognised macro not written in uppercase, which players never substitute.VAST-4.1-macro-deprecated(info):[CONTENTPLAYHEAD]/[MEDIAPLAYHEAD], deprecated in 4.1 in favour of[ADPLAYHEAD](fires on 4.1+ only).VAST-2.0-macro-wrong-context(info): a context-restricted macro used where it has no defined value ([ERRORCODE]outside<Error>,[REASON]outsideverificationNotExecuted).VAST-2.0-macro-uri-unencoded(warning, RFC 3986): a macro-bearing URL with characters that must be percent-encoded.
- The scanner is a single bounded, linear byte pass (
MAX_MACRO_LEN): only URL-bearing elements are inspected (free text such as<AdTitle>is never treated as a macro), numeric array indices likekey[0]are skipped, and adversarial input (large runs of[) cannot cause super-linear scanning. Root-level<Error>beacons are scanned with the correct context. - Rule catalog grown to 187 (
all_rules(), RULES.md, VS Code rule list).
- RULES.md and VS Code README updated: the five macro rules listed with severities and spec references; rule count updated to 187.
- Rule count updated to 187 across README.md, ROADMAP.md, docs/tutorial.md, docs/mcp-agentic.md, crate/npm/vscode/chrome/MCP descriptions, and the vastlint.org site (new "Macros" rule category with per-rule doc pages).
- Dependency updates via Dependabot:
phf0.13.1 → 0.14.0 (core crate),@types/node25.9.3 → 26.0.0 (vscode dev dependency). - CI action pins refreshed:
actions/checkout6.0.3 → 7.0.0,softprops/action-gh-release3.0.0 → 3.0.1,dtolnay/rust-toolchainadvanced to the latest pinned SHA.
- Patched 16 Dependabot alerts across npm dependency trees:
undici7.28.0,form-data4.0.6,js-yaml4.2.0,tmp0.2.7 (vscode);vite6.4.3 (3 example manifests);esbuild0.28.1 (chrome extension).
- Aligned all package versions (npm, vscode extension, chrome extension, all Rust crates) to the core release version. Previously these drifted across 0.4.24 and 0.5.0.
- VMAP 1.0 validation:
validate()now recognises<vmap:VMAP>documents and runs a dedicated 24-rule VMAP chain —<AdBreak>structure,timeOffset/breakType/repeatAfterformats,<AdSource>content constraints (exactly one ofVASTAdData/AdTagURI/CustomAdData, CDATA requirements), VMAP tracking events, andrepeatAfter/timeOffsetconflict detection. Inline VAST inside<vmap:VASTAdData>is validated with the full VAST rule chain; issues surface with/VMAP/AdBreak[i]/AdSource/VASTAdData-prefixed paths and document-absolute line/col. - DAAST 1.0 validation:
<DAAST>documents run a dedicated 29-rule chain covering the audio-specific deltas from VAST 3.0 — required<Category>, optional<AdSystem>,<DAASTAdTagURI>wrappers,<AdInteractions>(with detection of VAST leftovers like<VideoClicks>and<VASTAdTagURI>), audio MediaFile attributes, the DAAST tracking event set, DAAST pricing models (incl.cpo), root-level<Error>URI presence, and[ERRORCODE]macro inclusion. ValidationResult.document_type: newDocumentTypeenum (Vast/Vmap/Daast) reporting which rule chain ran.versionstays VAST-specific and isUnknownfor VMAP/DAAST documents. NewRuleSourcevariants:VmapSpec,DaastSpec,DaastXsd. Breaking change: callers that destructureValidationResultmust add the new field.- Rule catalog grown to 182 (
all_rules(), RULES.md, VS Code rule list).
- RULES.md and VS Code README updated: all 182 rules listed with correct severities and spec references.
- README.md rule count updated: three occurrences of "129 rules" updated to "182 rules"; VMAP 1.0 and DAAST 1.0 added to category list.
- vastlint.org rule pages: 53 new per-rule doc pages for all VMAP and DAAST rule IDs, with category impact descriptions, spec links, and XML examples.
- Semver bump to 0.5.0: breaking change due to new public
document_typefield onValidationResult.
- OMID coverage expanded inside core validation:
validate()andvalidate_with_context()now validate OMID compatibility blocks carried in pre-4.1Extension type="AdVerifications"payloads, enforce Verification tracking semantics, and warn whenverificationNotExecutedtracking URLs omit the[REASON]macro. - Verification tracking tightened:
<Tracking>under<Verification>now only acceptsevent="verificationNotExecuted", matching the VAST verification schema instead of the generic Linear tracking event set.
- Public docs updated for shipped OMID support: README, ROADMAP, MCP docs, tutorial copy, and package metadata now describe OMID validation as shipped instead of upcoming.
- Rule count aligned to 129: release-facing docs and package descriptions now reflect the current catalog size.
- Channel versions aligned: bumped the Rust crates, npm package, VS Code extension, Chrome extension, and tracked lockfiles to
0.4.24.
- VASTlint display branding aligned: updated the user-facing product name across the root docs, Chrome extension UI, VS Code extension metadata, MCP docs, and package descriptions to use
VASTlintwhile keeping commands, repo names, package names, URLs, and config keys unchanged.
- Channel versions aligned: bumped the Rust crates, npm package, VS Code extension, Chrome extension, and tracked lockfiles to
0.4.23.
- Ecosystem smoke stabilized: fixed the Erlang path/build issues in the published package smoke workflow so release verification now covers the shipped ecosystem again.
- Release workflow inputs refreshed: updated pinned GitHub Action dependencies including
actions/checkoutanddocker/setup-qemu-actionfor the next patch release.
- Packaging toolchain patched: bumped
@vscode/vsceto3.9.2,@types/nodeto25.9.2, and keptovsxon1.0.0in the extension release surface.
- Client package refreshed: pulled in the current
vastlint-clientupdate frommainfor the patch release train.
- Channel versions aligned: bumped the Rust crates, npm package, VS Code extension, Chrome extension, workflow version pins, and tracked lockfiles to
0.4.22.
vastlint-clientCDATA parsing hardened: replaced the regex-based CDATA stripper with linear scanning so untrusted XML no longer hits the polynomial-regex CodeQL finding.- VS Code packaging dependencies patched: forced
tmpto0.2.6in the extension build lockfile to clearGHSA-ph9p-34f9-6g65. - Repo artifact hygiene tightened: stopped tracking generated example
dist/output so demo WASM bundles are no longer committed as source artifacts. - Action smoke workflow pinned: the published
vastlint-actionsmoke workflow now uses thev1commit SHA instead of a mutable tag.
- Issue panel rendering fixed: the inline/floating panel now mounts lint issue rows as DOM nodes instead of coercing them into
[object HTMLDivElement]strings inside the shadow-root template.
- Channel versions aligned: bumped the Rust crates, npm package, VS Code extension, Chrome extension, and tracked lockfiles to
0.4.20for the patch release.
- MCP canary aligned with the live endpoint: the GitHub Actions canary now parses
list_rulesas an array payload and validatesget_adcp_capabilitiesagainst the current MCP contract without the removed top-levelstatusfield. - Channel versions aligned: bumped the Rust crates, npm package, VS Code extension, Chrome extension, lockfiles, example lockfile references, and tracked generated package metadata to
0.4.19.
- Version floor consistency fixed: declared newer VAST versions no longer raise false
VAST-2.0-version-mismatchwarnings when the XML only uses older structural features. - Malformed XML short-circuits cleanly: parse failures now emit
VAST-2.0-parse-errorwithout cascading required-field noise.
- Channel versions aligned: bumped the Rust crates, npm package, VS Code extension, Chrome extension, lockfiles, and tracked generated package metadata to
0.4.17. - Packaged runtime sanity-covered: the npm runtime smoke path now validates the built package against the shared fixture corpus before release tagging.
- Corpus expanded: added malformed XML, wrapper-heavy, and mixed vendor pod fixtures plus directory-wide fixture sweeps to reduce regression gaps before release.
- Browser UI hardening: removed HTML string rendering paths in the Chrome extension and popup so untrusted VAST content is rendered via DOM nodes instead of
innerHTMLsinks. - Release supply-chain tightening: pinned mutable GitHub Actions, Docker image inputs, and CLI install versions used by the release workflow and local packaging script.
- Channel versions aligned: bumped the Rust crates, npm package, VS Code extension, and Chrome extension manifests and lockfiles to
0.4.16so shipped artifacts match the tagged security release.
- React drop-in example: added
npm/examples/VastLintTakeHomePage.jsx, a copy-paste frontend starting point with live validation, issue filtering, line-aware source navigation, and auto-fix preview. - Main README onboarding link: linked the root README directly to the new
npm/examplesguide so frontend consumers can find the example without hunting through the repo.
- Homebrew tap sync: the release workflow now updates
aleksUIX/homebrew-tapautomatically after tagged releases so the formula version and SHA-256 checks stay aligned with published CLI assets.
- Channel versions aligned: bumped the Rust crates, npm package, VS Code extension, and Chrome extension manifests and lockfiles to
0.4.14so all published artifacts share the same release version. - Chrome popup version sourced from the manifest: the extension footer now reads
chrome.runtime.getManifest().versioninstead of a hardcoded string, preventing future UI version drift.
- Packaging compatibility restored: aligned the extension typing floor with
engines.vscodesovsce packageworks without raising the minimum supported VS Code version above 1.85. - Release metadata aligned: the VS Code package manifest and lockfile now carry the correct extension version for packaged builds.
- Rust dependency refresh: bumped
quick-xmlto0.40.0andtokioto1.52.3. - Tooling updates: bumped VS Code dev typing support to
@types/node 25.7.0and upgradedactions/dependency-review-actiontov5.0.0.
- Formatting gate fixed: normalized Rust test formatting so the cross-platform CI matrix and dependency PR reruns pass cleanly again.
- CDATA-aware leaf payload checks: the parser now preserves adjacent text and CDATA segments, retains entity references in plain text, and enables accurate warnings for URL,
Extension, andCreativeExtensionpayloads that should be wrapped in CDATA. - New advisory rules: added
VAST-2.0-url-cdata,VAST-2.0-extension-cdata, andVAST-2.0-creative-extension-cdataas warning-level guidance for fragile leaf-text payloads.
- Rules catalog fixed:
RULES.mdnow matches the shipped 121-rule catalog, including SIMID entries, the quartile-tracking warning, and correct warning severity for the HTTP transport rules. - Count drift removed: refreshed stale
118 rulesreferences across the main README, package metadata, MCP docs, roadmap, and architecture docs. - VS Code README resynced: restored the embedded rule list with
vastlint.orglinks and added parity coverage so the extension README stays aligned with the canonical rule catalog.
- Rules markdown parity: added
crates/vastlint-core/tests/rules_markdown.rssoRULES.mdcount, IDs, and severities must stay aligned withall_rules().
- Cleaner diagnostics UI: Problems entries now stay focused on the human-readable issue text while hovers link straight to the per-rule docs page. Quick fixes still resolve the correct rule ID even when VS Code does not surface
diagnostic.codedirectly. - Utility coverage: extracted template-ignore, multi-block extraction, and block-relative position mapping into
vscode/src/utils.ts, with dedicated unit tests covering template masking and embedded multi-block coordinates.
- SIMID fixtures expanded: added valid and invalid SIMID integration fixtures covering missing media files, missing MIME types, HTTPS enforcement, and variable-duration warnings.
- Large-tag regression coverage: added large Publica-style fixtures so oversized production tags stay covered by integration tests.
- MCP canary sweep:
validate_vastcanary coverage now runs a deterministic 15-case batch across VAST 2.0-4.2, wrappers, SIMID, and representative error fixtures instead of sampling a smaller XML pool. - Release smoke tests: the release workflow now asserts that default CLI text output includes severity and a rule reference, and it runs the VS Code extension unit test suite before publishing.
- Fuzz install compatibility: removed
--lockedfromcargo install cargo-fuzzin CI to avoid the nightlyrustixresolver failure.
- Updated the main README's VS Code section to match the cleaned Problems/hover experience.
- Corrected the public rules catalog header to reflect the current 118-rule set.
- Version alignment: bumped all crate
Cargo.tomlfiles to0.4.10in-repo socrates.ioalways reflects the current release even when the CI auto-bump skips a version.
- Line/column in output:
vastlintCLI now printsfile:line:col(orfile:line) alongside the XPath location for every issue, making it easier to jump to the exact source position.
- Release stamping fixed: both Chrome publish workflows now inject the extension version correctly before build instead of dropping the
versionfield frommanifest.json. - Release guardrail: both workflows now assert that
manifest.jsoncontains the expected version before packaging or publishing. - Chrome package version: bumped source
chrome/package.jsonandchrome/manifest.jsonto0.4.9for the next Web Store release.
- Main README no longer says the Chrome Web Store listing is pending review.
- Release checklist now documents the actual Chrome publish paths and reminds you to commit both Chrome version files.
- Smoke test: updated MCP tool-count assertion from 5 → 6 to include
inspect_vast; addedinspect_vastto the per-tool name check. - Chrome extension publish: replaced non-existent
trmcnvn/upload-google-chrome-extensionaction (fake SHA) with the correctmnao305/chrome-extension-upload@fdfe79400af990f5145a319e834aee64907ccff4(v6.0.0); corrected input nameextension→file-path; pinnedactions/setup-nodein chrome job to SHA48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e(v6.4.0).
- Committed docs, GitHub workflow helpers, METHODOLOGY.md, and test fixtures that were staged but not included in the v0.4.6 release commit.
inspect_vast— new tool that follows a VAST wrapper chain hop-by-hop, returning creative metadata (AdSystem, AdTitle, Duration, impressions, tracking events, media files, companions) and full validation results for every level of the chain. Accepts a starting URL and an optionalmax_depth(default 5). Returnshop_count,resolved,chain_valid,total_errors,total_warnings, and astopped_reason(resolved|max_depth|fetch_error|parse_error) alongside per-hop detail.quick-xmladded as a direct dependency for hop metadata extraction.- Server info string updated to describe all six tools.
- Updated extension description — flyout text in the VS Code extensions panel now reflects CLI backend, any-file-type support, and 108 rules.
- Settings table — README now documents all six settings including
vastlint.templateIgnoreRegex,vastlint.vastVersion, andvastlint.cliPath. - Full rule reference — collapsed rule table (108 rules, each linking to
vastlint.org/docs/rules/{id}/) added to the extension README.
- CLI backend — the extension now spawns the local
vastlintbinary (searched on PATH and common install locations:~/.cargo/bin,/opt/homebrew/bin,/usr/local/bin) and falls back to the bundled WASM when no binary is found. Using the CLI enablesvastlint.tomlconfig, rule overrides, and future CLI features automatically. - Multi-block support — files with multiple
<VAST>…</VAST>documents (e.g. batch response files) are now fully validated; each block gets its own set of squiggles with accurate positions. - Template ignore regex (
vastlint.templateIgnoreRegex) — a JS regex whose matches are replaced with same-length zeros before validation, preserving all line/col offsets. Strips Mustache{{…}}, ERB<%…%>, Go templates, or any ad-server macro syntax without shifting squiggle positions. - VAST version override (
vastlint.vastVersion) — force a specific spec version (2.0–4.3) regardless of theversion=attribute. Passed as--vast-versionto the CLI. - Any file type — activation changed to
onStartupFinished;<VASTanywhere in a file triggers linting regardless of file extension (.erb,.go,.html, etc.). vastlint.cliPathsetting — override the binary path when not on PATH.
--vast-version <version>(checkandfix) — override the VAST version used for validation, ignoring theversion=attribute in the XML. Accepts2.0,3.0,4.0,4.1,4.2,4.3. Useful for enforcing a floor version across all incoming tags or testing how a tag scores against a target version.--ignore-pattern <regex>(checkandfix) — replace all matches of the supplied regular expression with a valid HTTPS placeholder before validation. Designed for ad-server templating macros (${IMPRESSION_URL},%%CACHEBUSTER%%) that would otherwise trigger URL-format errors on unresolved placeholders. The substitution is in-memory only — the original file is never modified.
ValidationContextgainsforced_version: Option<VastVersion>— whenSome, skips XML version detection entirely and uses the supplied value. Used by the CLI flags above; available to library consumers.VastVersionnow derivesCopy.
vastlint daemonsubcommand — speaks the Erlang{:packet, 4}binary framing protocol over stdin/stdout. Reads 4-byte big-endian length + raw UTF-8 VAST XML; writes 4-byte big-endian length + JSON validation result. Safe for production Elixir pipelines viaNimblePool(each worker holds one persistentPort). Does not require the NIF.
- Kiro compatibility: lowered
engines.vscodeminimum from1.116.0to1.85.0so the extension installs on Amazon Kiro and other VS Code forks with older API versions
- Hover tooltip redesign: severity icons replaced with flat color squares (🟥 error, 🟨 warning, 🟦 info); 🔧 for fix hints
- Compact hover layout: collapsed from 5 spaced lines to 3 tight lines per issue
- Rule ID links to docs: each rule ID in the hover footer is now a clickable link to
vastlint.org/docs/rules/<id>/ - Fix hints coverage: added missing hints for
VAST-3.0-bitrate-conflict,VAST-3.0-minmaxbitrate-pair,VAST-2.0-nonlinear-resource,VAST-4.0-interactive-creative-no-api,VAST-4.1-interactive-creative-type,VAST-3.0-pricing-model-case; removed stale keyVAST-2.0-mediafile-bitrate-conflict
- New
RuleSource::IndustryBestPractice- distinct fromVastSpecandInferred; renders as"revenue impact"in all output formats - New
RuleMeta::revenue_impact()- returnstruefor 12 rules where a structural defect causes direct measurement or delivery loss; no catalog field added, no breaking schema change - 5 rules reclassified from
Inferred→IndustryBestPractice:VAST-2.0-mediafile-https,VAST-2.0-tracking-https,VAST-2.0-duplicate-impression,VAST-4.1-mezzanine-recommended,VAST-4.1-vpaid-in-interactive-context - HTTP tracker rules promoted
Info→Warning:VAST-2.0-mediafile-httpsandVAST-2.0-tracking-https- on HTTPS inventory these are guaranteed delivery failures, not advisory notices - New rule
VAST-2.0-linear-tracking-quartiles(Warning,IndustryBestPractice) - fires when a<Linear>creative has no<TrackingEvents>containing any ofstart,firstQuartile,midpoint,thirdQuartile, orcomplete; absence of all five is a complete measurement blackout. Spec reference: IAB VAST 4.1 §3.14.2
--fail-on-warning- exits non-zero when any warning is found; all 12 revenue-impact rules fire atWarningorErrorseverity, making this flag sufficient for a CI revenue gate- URL input with wrapper chain following -
vastlint check https://…fetches the tag and recursively follows<VASTAdTagURI>wrapper chains --max-depth N(default5) - controls how deep wrapper chains are followed, matching the IAB VAST 4.x recommendation--summary- prints aggregate pass/fail counts after validation; includes a$revenueline when any revenue-impact rules fired; works in both plain and JSON output modes
- New
$column - marks the 12 revenue-impact rules - Legend line added at the bottom of the table
118 rules total (was 108 before v0.3.x additions; 117 before this release).
- CI: harden release pipeline (SLSA provenance, deploy key scoping)
- VS Code: align
engines.vscodeto^1.116.0
- Chrome extension: v0.2.0 - HTML-rendered VAST detection, inline overlay annotations, privacy policy; CWS submission workflow
- CI: SLSA provenance signing; Smithery and MCP Registry idempotent publish
- Security: patched two advisories (
idnaRUSTSEC-2024-0421,rustls-webpkiRUSTSEC-2026-0098/0099); addedcargo auditto CI - Fuzz: cargo-fuzz targets for
validate,fix, andvalidate_wrapper
- SIMID rules: 9 rules covering SIMID 1.0 (linear) and SIMID 1.1 (nonlinear) - type, URL, HTTPS,
variableDuration,<MediaFile>fallback,<IFrameResource>presence - Docs: SIMID coverage expanded to all spec versions (1.0, 1.1, 1.2) on vastlint.org
- MCP server:
vastlint-mcpcrate published to MCP Registry; tools:validate_vast,validate_vast_url,list_rules,explain_rule,fix_vast
- Auto-fix in VS Code: inline quick-fix actions wired up; fix API exported from npm package
- Open VSX: extension now published to Open VSX Registry in addition to VS Code Marketplace
- Erlang/Elixir NIF (
vastlint_nif): native binding for BEAM-based ad servers and RTB platforms - Performance docs updated to production-realistic benchmarks (17–44 KB tags)
- Build: idempotent
cargo publishandvsce publish(skip if version already exists) - WASM: smoke test fixes; both targets built before assemble step
- npm + WASM packages added;
vastlintavailable on npm for browser and Node.js use
- Line/column positions: all issues now include
lineandcolin JSON output and VS Code diagnostics
- FFI C layer (
vastlint-ffi):libvastlintshared library with C header; Go binding (vastlint-go) backed by the same core mimallocglobal allocator in CLI and FFI for lower memory overhead
- Release pipeline fixes: provenance cascade on skipped jobs, version bump order
- Telemetry endpoint fix
- Go binding (
vastlint-go): full Go FFI wrapper; same 108 rules, zero CGO complexity for callers - Version equalization: all crates and bindings move to a unified version scheme
- Initial public release
- vastlint-core: 108 rules derived from IAB VAST 2.0–4.3; zero-dependency, zero-I/O Rust library; validates in under 1 ms on typical production tags
- CLI:
vastlint checkwith single-file, glob, stdin, JSON output;vastlint fixauto-repair with--dry-runand--out - Web validator: vastlint.org/validate - client-side WASM, no data leaves the browser
- VS Code extension: inline diagnostics with rule IDs and spec references
- REST API:
/api/validateon RapidAPI, WASM-powered, sub-millisecond response - Homebrew tap:
brew install aleksUIX/tap/vastlint