Skip to content

Security: validate persisted color data and postMessage senders #1

Description

@Zombiehamser

Summary

A static review of version 1.0.8 found two security issues. No network exfiltration, hidden downloads, shell/file-system access, credential handling, or deliberate obfuscation was found in the reviewed archive.

1. Unvalidated persisted color values can reach HTML sinks

Task-note data following the __task_colors__= marker is parsed as JSON and returned without type or color-format validation. Values from synced settings are also inserted into HTML strings. Those values subsequently reach concatenated htmlContent / innerHTML attributes and styles.

Affected examples:

  • plugin.js: getCurrentColor() around lines 23–35; task picker rendering around lines 571–577
  • index.html: side-panel rendering around lines 385–394

A malicious or corrupted synced/imported task note could supply a value that escapes a quoted attribute when the generated HTML is parsed. This may allow DOM XSS in the plugin context, including access to the plugin's task/tag APIs.

Recommended fix

  • Validate every loaded color using a strict allowlist such as /^#[0-9a-f]{6}$/i.
  • Reject invalid records in task notes and synced data.
  • Prefer DOM APIs (.value, .dataset, style properties) over concatenated HTML; escape any remaining dynamic attributes.

2. postMessage handlers do not validate sender

The iframe sends open-tag-colors with targetOrigin = '*' and accepts tag-colors-result without checking event.origin or event.source. The host listener also accepts open-tag-colors from any sender and replies using the sender-controlled event.origin.

Affected examples:

  • index.html: around lines 498–509
  • plugin.js: around lines 737–750

An untrusted window that obtains a reference to the plugin frame could trigger privileged UI/data flow or spoof a result message. Updates still require the user to save the dialog, which limits automatic modification.

Recommended fix

  • Use the known fixed origin instead of *.
  • Verify both event.origin and event.source against the expected iframe/parent window.
  • Apply a narrow message schema and bind request/response pairs with a per-frame nonce.

Scope

This was a static review only; the Super Productivity runtime's CSP and plugin isolation were not dynamically verified.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions