Skip to content

Show port provenance and prevent source name/prefix spoofing#331

Open
JeodC wants to merge 2 commits into
PortsMaster:mainfrom
JeodC:vuln-source
Open

Show port provenance and prevent source name/prefix spoofing#331
JeodC wants to merge 2 commits into
PortsMaster:mainfrom
JeodC:vuln-source

Conversation

@JeodC

@JeodC JeodC commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Adds visible provenance for every port and hardens source identity against impersonation.

Changes

Provenance display

  • New port_info.source template variable for themes. The default theme renders Source: X in the port info panels for all ports (official ones show Source: PortMaster).
  • The source line is also appended to the port description text, so users on themes that predate the new variable still see provenance for every port.
  • Installed ports show where they were actually downloaded from (status.source); the Unknown/file/url placeholders used for sideloaded ports fall back to the source currently offering the port, or show nothing if no source does.

Spoofing resistance

  • load_sources() reserves the bundled prefixes: a file claiming pm or pmmv only loads if it is the bundled source file, independent of filename sort order. Neither a later file (old last-wins bug) nor an earlier-sorting 000_spoof.source.json can take the official prefix. Duplicate prefixes between third party files fall back to first-file-wins with a logged error, instead of silently replacing the earlier source.
  • The bundled source names only display clean when they come from the bundled source files (020_portmaster.source.json, 021_portmaster.multiverse.source.json). A third party source calling itself "PortMaster" displays as PortMaster (040_spoof). Name collisions between third party sources qualify all parties the same way.
  • Downloads record the qualified display name in status.source, so an installed port keeps honest provenance even after the offering source is removed.

Notes

  • The default theme's info template strings changed, so existing themes.po translations were regenerated in a separate commit. This can easily be reverted if the diff is undesirable.
  • Theme zips were updated separately: Update themes for new Source label PortMaster-Themes#6
  • No schema changes to port.json or ports.json; source.repo is attached at runtime only.
image

@JeodC

JeodC commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Want to highlight this note: the default theme's info template strings changed, so existing themes.po translations were regenerated in a separate commit. I can easily revert the commit if the diff is undesirable.

@bmdhacks bmdhacks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this increased verbosity is worth the benefit.

Comment thread PortMaster/pugwash
if port_source in (None, "", "Unknown", "file", "url"):
port_source = port_info.get('source', {}).get('repo', None) or ""

description = port_info['attr']['desc']

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why inject the source into the description? Wouldn't it be better to keep these two semantic things distinct and have the source displayed in the layout side of things instead of polluting description?
Ok, I read the submission wall of text, but why would I care about the source of a port if I was running an old version of portmaster that doesn't pull from any different sources? Also I don't think we want to broadcast MV sources.

@JeodC JeodC Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback you're referring to targets old theme zips, not old GUI versions. It allows old theme zips to display the new variable. As for description injection, fine to remove if not wanted but the port info list is a small font that can be overlapped by a screenshot. I was going for visibility.

With MV, it's due to the presence of two files. The source comes from the name key. I can add a way to merge the two official sources into a single display.

"Körtid: {port_info.runtime} ({port_info.runtime_status}){endif}"

#: default_theme/theme.json:347, default_theme/theme.json:846
msgid ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this newline correct? This seems like a lot of auto-generated redundancy but perhaps I don't know how .po files work.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's standard gettext multi-line syntax, it's correct. But yeah I'm not all too sure about these .po changes; the themes I don't think have been updated in two years, so it could just be an artifact of running the generator script for the first time since new port info was added. Again I can revert the commit that generated them if it's easier to do it with an automation or something.

source_data['last_checked'] = None
source_data['data'] = {}

# A reserved prefix only loads from its bundled file, even if the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of extra code to protect against a security issue that's not really relevant. "Imposter" source files are fine if somebody wants to do that on their handheld.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're looking at user control, and I agree, however my approach has more to do with injection. Not that I would do this, but take Pharos for example. Pharos can already download my gmtoolkit.aarch64 binary and put it in $controlfolder for a user. It is entirely possible for a bad actor to sideload a third-party repo file without the user's consent.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

??? we run ports at root, a bad actor can do whatever tf they want. Unless you can demonstrate a remote vuln without device or access to an installed port (and don't give me DNS poisoning like I mean something real) this is a dead end and I would vote against adding cruft for a non-issue.

This feels to me like an LLM told you that this is a security vulnerability because the LLM doesn't understand the zero-security model we use for handheld devices where we run unaudited chinese firmwares and assume that devices are already rootkitted so our model is to just keep any sensitive info off-device (and frankly suggest partitioning your home network too).

@JeodC JeodC Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is me recognizing JohnnyOnFlame's concerns, finding that when I use portmaster gui's infrastructure as advertised that my port is indistinguishable from a portmaster official port, and promptly fixing it because if I can do it, then so can anyone else.

Ok not to add. Just pointing it out and attempting to contribute a solution.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants