Skip to content

fix: harden MediaEntry against invalid numeric fields and dict2entry error type - #429

Draft
JarbasAl wants to merge 1 commit into
devfrom
fix/ocp-dataclass-hardening
Draft

fix: harden MediaEntry against invalid numeric fields and dict2entry error type#429
JarbasAl wants to merge 1 commit into
devfrom
fix/ocp-dataclass-hardening

Conversation

@JarbasAl

Copy link
Copy Markdown
Member

🤖 Auto-generated by Claude Fable 5 (claude-fable-5) via Claude Code — NOT human-reviewed. Verify before acting.

MediaEntry.mpris_metadata built the mpris:length field as Variant('d', self.length). MPRIS2 defines mpris:length as an int64 in microseconds, signature x, not a double. Any consumer speaking real MPRIS2 (a desktop shell, playerctl, GNOME's media controls) would read that field with the wrong type, and if length ever held a non-finite value (NaN, inf, or something non-numeric) dbus_next raises SignatureBodyMismatchError at Variant construction, crashing the property outright. The fix uses signature x with an explicit int cast, and omits the key entirely when the length is missing or not a finite number rather than raising.

That length field could get poisoned in the first place because MediaEntry.update() copied every key from an incoming dict straight onto the dataclass with setattr, with no type checking. A malformed dict (or a plugin returning length: None or a string) would silently overwrite a good numeric value, and that bad value then flows into things like Playlist.length, which sums e.length for e in self.entries — one poisoned entry makes the whole playlist duration wrong or throws downstream. update() now validates length, position, and match_confidence against a simple finite-number check (rejecting bools, NaN, and inf) and keeps the previous value with a debug log when the incoming value doesn't qualify, instead of accepting whatever came in.

dict2entry() is meant to be a boundary function — the thing callers use to turn untrusted dict input into a MediaEntry, PluginStream, or Playlist — but it only validated dicts that were missing the right keys; it never checked whether the input was a dict at all. Passing None, an int, a string, or a list raised a bare AttributeError from the first .get() call instead of the ValueError the function otherwise promises. Callers that catch ValueError as their validation signal would see an unhandled AttributeError instead. The fix adds an explicit isinstance(track, dict) check up front so every rejection path raises ValueError consistently; the existing behavior of raising (not swallowing) on bad input is unchanged.

All three defects were verified against current origin/dev source before fixing: the Variant('d', ...) line, the raw setattr loop in update(), and the missing type guard in dict2entry() were all present as described. Fail-before evidence: the new tests in test/unittests/test_ocp_extra.py were run against the unmodified source (via a reverted patch) and 15 of the 16 new cases failed — the MPRIS tests failed on the wrong signature or an unguarded crash, the update() tests failed because bad values overwrote good ones, and the dict2entry tests failed with AttributeError instead of ValueError. After the fix, the same 16 tests pass, and the full existing suite (977 passed, 1 skipped) shows no regressions.

ovos-media, the OCP-native playback daemon, already works around all three issues on its own side — it never fed non-numeric lengths through this path and doesn't rely on mpris:length's signature matching the MPRIS2 spec — so this only affects other consumers of ovos_utils.ocp that hit these same code paths with less careful input.

…error type

mpris_metadata used Variant('d', length) for mpris:length, but MPRIS2
requires signature 'x' (int64 microseconds); a non-finite length also
crashed at Variant construction. update() set attributes via raw setattr
with no validation, letting a non-numeric length/position/match_confidence
poison later consumers such as Playlist.length's sum(). dict2entry raised
AttributeError on non-dict input instead of a consistent ValueError.
@github-actions github-actions Bot added the fix label Aug 15, 2026
@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d07e1fc4-7c82-4e7b-8656-df6b95924bcb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Reporting for duty! The automated checks have completed. 🎖️

I've aggregated the results of the automated checks for this PR below.

🔍 Lint

The automated checks have finished their work. 🏁

ruff: issues found — see job log

🏷️ Release Preview

Checking for any potential release blockers. 🚧

Current: 0.13.13a1Next: 0.13.14a1

Signal Value
Label (none)
PR title fix: harden MediaEntry against invalid numeric fields and dict2entry error type
Bump build

✅ PR title follows conventional commit format.


🚀 Release Channel Compatibility

Predicted next version: 0.13.14a1

Channel Status Note Current Constraint
Stable Too new (must be <0.9.0) ovos-utils>=0.8.1,<0.9.0
Testing Too new (must be <0.8.5) ovos-utils>=0.8.4,<0.8.5
Alpha Compatible ovos-utils>=0.13.12a2

🔒 Security (pip-audit)

Locking the doors and checking the windows... 🔒

✅ No known vulnerabilities found (47 packages scanned).

📋 Repo Health

Ensuring the repo isn't allergic to new features. 🤧

✅ All required files present.

Latest Version: 0.13.13a1

ovos_utils/version.py — Version file
README.md — README
LICENSE — License file
pyproject.toml — pyproject.toml
⚠️ setup.py — setup.py
CHANGELOG.md — Changelog
ovos_utils/version.py has valid version block markers

⚖️ License Check

Double-checking the fine print for any surprises. 🔍

✅ No license violations found.

Policy: Apache 2.0 (universal donor). StrongCopyleft / NetworkCopyleft / WeakCopyleft / Other / Error categories fail. MPL allowed.

📊 Coverage

Quantifying the robustness of your changes. 🏋️

85.4% total coverage

Files below 80% coverage (5 files)
File Coverage Missing lines
ovos_utils/log_parser.py 48.4% 225
ovos_utils/__init__.py 63.6% 16
ovos_utils/file_utils.py 74.4% 56
ovos_utils/thread_utils.py 76.9% 12
ovos_utils/geolocation.py 78.4% 22

Full report: download the coverage-report artifact.

🔨 Build Tests

Ensuring the gears are properly lubricated. 💧

✅ All versions pass

Python Build Install Tests
3.10
3.11
3.12
3.13
3.14

Your digital assistant in the world of OVOS 🤖

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant