Skip to content

build(deps): bump feedparser-rs from 0.5.5 to 0.7.0 - #23

Open
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/cargo/feedparser-rs-0.7.0
Open

dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/cargo/feedparser-rs-0.7.0

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Bumps feedparser-rs from 0.5.5 to 0.7.0.

Release notes

Sourced from feedparser-rs's releases.

v0.7.0

Installation

Rust (crates.io):

cargo add feedparser-rs

Python (PyPI):

pip install feedparser-rs

Node.js (npm):

npm install feedparser-rs

Links

See https://github.com/bug-ops/feedparser-rs/blob/HEAD/CHANGELOG.md for details.

What's Changed

Full Changelog: bug-ops/feedparser-rs@v0.6.0...v0.7.0

v0.6.0

Installation

Rust (crates.io):

cargo add feedparser-rs
</tr></table> 

... (truncated)

Changelog

Sourced from feedparser-rs's changelog.

[0.7.0] - 2026-09-04

Security

  • Updated h2 to 0.4.16, fixing RUSTSEC-2026-0258: unbounded empty DATA frames could be queued without limit if streams were not actively drained, leading to unbounded memory usage or a panic on length overflow. Pulled in transitively via hyper/reqwest (HTTP fetcher); low severity, no code changes required (#495).
  • Node.js JSON Feed extension values (jsonExtensions) are now sanitized before crossing the napi serde bridge: a nested __proto__/constructor/prototype object key inside a captured value previously reached napi's map serializer untouched, which uses V8 [[Set]] semantics (set_named_property) rather than JSON.parse's [[DefineOwnProperty]] — a feed-controlled {"_evil": {"__proto__": {"isAdmin": true}}} could silently rewrite the containing object's prototype instead of becoming a normal property. Such keys are now dropped recursively within captured extension subtrees. Top-level extension keys were never affected (_evil itself is filtered by the existing _ + letter naming rule).
  • Node.js JSON Feed extension values containing a non-negative integer above u32::MAX (e.g. _big: 4294967296) previously became a JS BigInt (napi's serialize_u64 fast-paths values <= u32::MAX to a plain Number and otherwise calls napi_create_bigint_uint64), and a single BigInt anywhere in the returned object graph makes JSON.stringify() throw for the entire parse result. Such integers are now converted to their decimal string form before reaching napi.

Changed

  • feedparser-rs-py: migrated compat.rs's field-alias maps from once_cell::sync::Lazy to std::sync::LazyLock, matching the pattern already used in feedparser-rs-core. LazyLock has been stable since Rust 1.80, well below this project's MSRV (1.88.0), so once_cell is no longer a direct dependency of any workspace crate and was removed from [workspace.dependencies] (#491).
  • Updated quick-xml from 0.41.0 to 0.42.0 (#500). This is a large upstream API change: element/attribute names and text/attribute values are now &str/Cow<str> instead of &[u8]/Cow<[u8]>. Updated every parser call site accordingly; internal namespace-matching helpers (is_dc_tag, is_itunes_tag, etc.) keep their existing &[u8] signatures, with .as_bytes() conversions added at their call sites. No behavior change.
  • Removed memchr and regex from feedparser-rs-core's direct dependencies (flagged unused by cargo machete): neither crate had any call site in the codebase.
  • Breaking: raised the workspace MSRV from 1.88.0 to 1.91.0 (#492). This unlocks Ipv4Addr::from_octets/Ipv6Addr::from_segments, now used in util/ssrf.rs in place of Ipv4Addr::new/Ipv6Addr::new for a clearer octet/segment-array round-trip with the existing octets()/segments() getters, and Duration::from_mins/from_hours in a couple of test-only call sites in http/client.rs/tests/http_integration.rs (both surfaced by the new clippy duration_suboptimal_units lint now in-MSRV); behavior is unchanged in all cases.

Added

  • JSON Feed extension objects (underscore-prefixed custom keys, e.g. _cast): a new json_extensions: HashMap<String, serde_json::Value> field on FeedMeta and Entry captures spec-sanctioned custom objects (JSON Feed 1.1 §Extensions — a key starting with _ followed by a letter) at feed-root and item-root scope, keyed by the raw JSON key including the leading underscore (#493). Only the JSON Feed parser populates this field; RSS and Atom feeds always leave it empty. Values are captured verbatim and never interpreted, and capture never sets bozo. Nested scopes (authors[], attachments[], hubs[]) are not captured in this pass — an MVP limitation, not an oversight. Exposed in Node.js as jsonExtensions: Record<string, any> (sanitized for safe JS exposure, see Security above) and in Python as a native dict via feed.json_extensions/entry.json_extensions (eagerly converted once per parse, matching the existing namespaces getter pattern)
  • Breaking: ParserLimits gained a new field, max_json_extensions (default 100, strict 20, permissive 500), bounding how many extension keys are captured per scope; per-value size is bounded by the existing max_text_length. Callers constructing ParserLimits with an exhaustive struct literal instead of ..Default::default() need to add this field (#493)

Fixed

  • FeedMeta::with_rss_capacity and Entry::with_capacity called Vec::with_capacity(0) for their contributors field, tripping the new clippy with_capacity_zero lint; switched to Vec::new() (#497).
  • CI: the release.yml npm build/publish jobs used npm ci against a stale package-lock.json that had drifted from package.json's devDependencies, failing every npm platform build during the v0.6.0 release. Switched those jobs to pnpm (matching ci.yml and the cargo-make Node.js tasks) and removed package-lock.json, so the workspace has a single Node.js lockfile (pnpm-lock.yaml) instead of two that can drift out of sync. Also switched the security CI job's npm audit back to pnpm audit, which had regressed to npm audit despite pnpm already being installed in that job.
  • Python bindings: feedparser-rs-py/src/limits.rs's PyParserLimits::to_core_limits built the core ParserLimits via an exhaustive struct literal with four stale hardcoded values that had drifted from ParserLimits::default(). Switching that literal's tail to ..CoreParserLimits::default() (done as part of the max_json_extensions field addition, #493) raised those four limits to match core's actual defaults: max_podcast_alternate_enclosures 10→20, max_podcast_podroll 20→50, max_podcast_social_interact 10→20, max_podcast_follow 10→20. Python callers relying on feedparser_rs.ParserLimits()'s implicit values for these four fields will now see more permissive limits.

[0.6.0] - 2026-08-17

Security

  • Close an SSRF protection bypass in FeedHttpClient (CWE-918, #436): HTTP redirects (Location headers) are now re-validated against the same SSRF checks as the initial request, and DNS resolution results are re-checked at connect time via a custom resolver, closing a DNS-rebinding gap where a domain resolving to a public IP at validation time could be repointed to a private/loopback/link-local/metadata address before the connection was made. Also consolidated util::base_url::is_safe_url to reuse the same validation rules as http::validation::validate_url instead of a separate, weaker, duplicated implementation.
  • FeedHttpClient no longer honors the system/environment HTTP proxy (HTTP_PROXY/HTTPS_PROXY/ALL_PROXY): a proxy resolves its own target hostname, which silently bypassed the DNS-rebinding-safe resolver above.
  • Close an IPv6 embedded-IPv4 SSRF bypass in validate_ipv6: IPv4-mapped (::ffff:127.0.0.1), deprecated IPv4-compatible (::127.0.0.1), and NAT64 (64:ff9b::/96, RFC 6052) address forms are now unwrapped to their embedded IPv4 address and validated against the same IPv4 rules, instead of silently passing every native IPv6 check. 6to4 (2002::/16, RFC 3056) is a known, deliberately deferred follow-up: relay infrastructure for it has been widely decommissioned and it is not standard on any current cloud network, unlike the three forms fixed here.
  • [P1] Close a trailing-dot FQDN SSRF bypass in validate_domain (#452): one or more trailing DNS root-label dots (e.g. http://metadata.google.internal./, http://localhost../) resolve identically to the un-dotted form via every standard stub resolver, but previously bypassed every domain-string blocklist check (LOCALHOST_VARIANTS, INTERNAL_TLDS, METADATA_DOMAINS) because the trailing dot(s) broke exact-match and ends_with comparisons. The domain is now normalized by stripping all trailing . characters before those checks run.
  • Add several missing IANA special-purpose address ranges to validate_ipv4/validate_ipv6 as defense-in-depth (#453): IPv4 multicast (224.0.0.0/4), reserved (240.0.0.0/4), IETF protocol assignments (192.0.0.0/24), 6to4 relay anycast (192.88.99.0/24), and benchmarking (198.18.0.0/15); IPv6 Teredo (2001::/32), ORCHIDv2 (2001:20::/28), documentation (2001:db8::/32), the discard-only prefix (100::/64), and the RFC 8215 NAT64 local-use prefix (64:ff9b:1::/48).
  • [P4] Add the remaining IANA AS112/AMT/PCP anycast sub-ranges to validate_ipv4/validate_ipv6 — newly-blocked IANA special-purpose ranges not covered by the existing IETF-assignment-block checks, closing the registry-completeness gap left by #453/#455 (#462): IPv4 AS112-v4 (192.31.196.0/24, RFC 7535), AMT (192.52.193.0/24, RFC 7450), and Direct Delegation AS112 Service (192.175.48.0/24, RFC 7534); IPv6 PCP Anycast (2001:1::1/128, RFC 7723), TURN Relay Anycast (2001:1::2/128, RFC 8155), AMT (2001:3::/32, RFC 7450), AS112-v6 (2001:4:112::/48, RFC 7535), and Direct Delegation AS112 Service (2620:4f:8000::/48, RFC 7534). These are publicly routed anycast service addresses, not private/internal ranges — no attack scenario, registry completeness only.
  • [P3] Block 2001:1::3/128 (DNS-SD Service Registration Protocol Anycast, RFC 9665) and 100:0:0:1::/64 (Dummy IPv6 Prefix, RFC 9780) in validate_ipv6_special_purpose (#471): both addresses were registered in the IANA IPv6 Special-Purpose Address Registry after #469 shipped and previously fell through as allowed, sitting directly inside/adjacent to ranges the registry now reserves. Registry-completeness only, same as #462 — no attack scenario.
  • [P4] Add the remaining uncovered IANA IPv6 special-purpose ranges to validate_ipv6 (#471): Benchmarking (2001:2::/48, RFC 5180), the deprecated (previously ORCHID) range (2001:10::/28, RFC 4843) and DRIP Entity Tags (2001:30::/28, RFC 9374), the documentation range extension (3fff::/20, RFC 9637), and Segment Routing (SRv6) SIDs (5f00::/16, RFC 9602). 2002::/16 (6to4) remains deliberately out of scope; see the #469 entry above.
  • [P2] Collapsed the individual 2001::/23 IETF Protocol Assignments sub-range checks (Teredo, ORCHIDv2, the deprecated ORCHID range, DRIP, PCP/TURN/DNS-SD anycast, AMT, AS112-v6) in validate_ipv6_special_purpose/validate_ipv6_special_purpose_extended into a single segments[0] == 0x2001 && (segments[1] & 0xFE00) == 0 check (#474). Enumerating sub-ranges one at a time kept reopening the same registry-completeness gap (#453/#455/#462/#471) every time IANA registered a new address inside the block; blocking the whole 2001::/23 block outright closes it structurally instead. 2001:1:: itself (no anycast suffix) is now blocked too — previously allowed despite being unassigned within the block. 2001:db8::/32 (documentation, RFC 3849) is a separate top-level IANA allocation, not a sub-range of 2001::/23, and keeps its own explicit check.
  • SSRF rejection reasons (from redirect re-validation or DNS-rebinding checks) are no longer swallowed by reqwest::Error's Display, which only prints the outer error kind and URL — FeedHttpClient::get now walks the full error source chain so callers see the actual reason a request was blocked.
  • [P0] ParseOptions.sanitize_html (documented default: true) was never consumed by any parse entry point, so HTML-bearing feed fields (titles, summaries, content:encoded, etc.) reached applications unsanitized — a stored XSS vector for any consumer that renders parsed feed content. Added a fail-closed sanitize_feed post-parse pass (util::sanitize::sanitize_feed) that walks every HTML-bearing field of ParsedFeed, matching Python feedparser's can_contain_dangerous_markup coverage, and wired it into parse/parse_with_limits/parse_with_options and their parse_url* counterparts (#438)
    • Behavior change: HTML entities are now escaped and unrecognized tags dropped by default, e.g. an RSS title Rust & C++: a <comparison> becomes Rust &amp; C++: a — this matches Python feedparser's sanitizer. Pass sanitize_html: false (Rust/Python) or { sanitizeHtml: false } (Node) to opt out for fully-trusted feed sources. Sanitization is idempotent
    • Fixed a related bypass: Atom type="text/html", type="application/xhtml+xml", and case variants like type="HTML" previously fell through to the plain-text default and were never sanitized even before this fix; the type attribute is now parsed case-insensitively with unrecognized values treated as HTML (fail-closed)
    • title/subtitle/summary fields on RSS 2.0, RSS 1.0, and JSON Feed have no per-element type indicator and were initially (during development of this fix) still mislabeled as always-safe plain text, leaving the P0 partially open for those fields; they are now treated as potentially unsafe HTML by default, matching how <description> was already handled
    • An Atom text construct (<title>, <summary>, <subtitle>, <rights>) with no type attribute at all is now sanitized too. RFC 4287 §3.1.1 defaults an absent type to "text" for display purposes, but that default carries no safety assertion — only an explicit type="text" is now trusted to skip sanitization
    • sanitize_html's underlying HTML5 tree builder is quadratic-time on pathologically deep tag nesting within a single field; added ParserLimits::max_html_nesting_depth (default 100) so content nested deeper is escaped as plain text in O(n) instead of being handed to the sanitizer unbounded, closing a remote CPU-DoS on the newly-default-on sanitization path. The depth estimate tracks matched tag names on a small stack, scope-aware per HTML5's "has an element in scope" algorithm (a closing tag cannot pop through a <table>/<td>/<th>/<caption> scope barrier to reach a same-named ancestor — this is what makes ("<div><table></div>")*n genuinely nest rather than closing cleanly each iteration), and recognizes HTML void elements (<br>, <img>, ...), auto-closing elements (<li>, <p>, <td>, <th>, <tr>, <option>), and HTML5 "formatting elements" (<b>, <font>, <i>, ..., which — verified empirically — do not create the same pathological nesting a structural element does even when repeated thousands of times unclosed). An additional O(1) backstop caps total tags per field at 10,000, bounding worst-case behavior independent of how closely this model tracks html5ever's real semantics. Together these mean ordinary valid or mildly malformed HTML — image galleries, unclosed <li>/<p> runs, tables, runs of unclosed inline formatting — is never misjudged as pathologically nested, while every discovered way to genuinely hide deep nesting from the guard is closed
    • Hoisted the sanitizer's tag/attribute allow-lists and ammonia::Builder to a LazyLock built once, instead of rebuilding them on every call — this was a measurable regression once sanitization went from zero call sites to dozens per entry
  • Added resolve: bool to BaseUrlContext so ParseOptions::strict()'s resolve_relative_uris: false now actually disables relative URL resolution (previously a no-op). The safety checks (dangerous-scheme filter, SSRF/private-IP validation) always run regardless of this setting — disabling resolution only skips joining a relative URL against the feed's base URL, so an absolute dangerous URL supplied directly by the feed is still blocked

Added

... (truncated)

Commits
  • a25831e release: prepare v0.7.0 (#502)
  • d806b31 fix(core): use https URL in build_request timeout test (#501)
  • 1ff3738 chore(deps): bump quick-xml from 0.41.0 to 0.42.0 (#500)
  • 427174d chore(deps): bump the patch-updates group with 2 updates (#499)
  • 45e3606 chore: bump @​biomejs/biome (#498)
  • 0ecf0fd chore: bump @​biomejs/biome (#496)
  • 513b64d fix(core): replace Vec::with_capacity(0) with Vec::new() (#497)
  • 51c6c4a fix(deps): update h2 to 0.4.16, fixing RUSTSEC-2026-0258 (#495)
  • 23a10a0 chore(deps): bump lewagon/wait-on-check-action from 1.9.0 to 1.9.1 (#494)
  • 640307b feat(core,node,py): capture JSON Feed extension objects (#493)
  • Additional commits viewable in compare view

@dependabot dependabot Bot added dependencies Pull requests that update a dependency file rust Pull requests that update rust code labels Oct 23, 2026
@socket-security

socket-security Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedcargo/​feedparser-rs@​0.5.5 ⏵ 0.7.09910093100100

View full report

Bumps [feedparser-rs](https://github.com/bug-ops/feedparser-rs) from 0.5.5 to 0.7.0.
- [Release notes](https://github.com/bug-ops/feedparser-rs/releases)
- [Changelog](https://github.com/bug-ops/feedparser-rs/blob/main/CHANGELOG.md)
- [Commits](bug-ops/feedparser-rs@v0.5.5...v0.7.0)

---
updated-dependencies:
- dependency-name: feedparser-rs
  dependency-version: 0.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot
dependabot Bot force-pushed the dependabot/cargo/feedparser-rs-0.7.0 branch from bb6335f to 884e32e Compare October 23, 2026 09:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants