Skip to content

Fix audit#624

Draft
aalex wants to merge 20 commits into
devfrom
fix-audit
Draft

Fix audit#624
aalex wants to merge 20 commits into
devfrom
fix-audit

Conversation

@aalex

@aalex aalex commented Jul 3, 2026

Copy link
Copy Markdown
Member

Multiple fixes after I've done an audit.

aalex added 17 commits July 2, 2026 00:22
oscpack throws osc::Exception on any malformed input. The parse in
OscReceiver::byteArrayToVariantList was unguarded, so a bad datagram
unwound through the Qt event loop up to MainApplication::notify(), which
aborted the entire readyRead batch and logged unhelpfully.

Wrap the parse in a local try/catch: a malformed datagram is now dropped
with a clear qWarning, the receive loop keeps draining pending datagrams,
and a message is forwarded only when it parsed cleanly.
The event-loop guard already caught std::exception, but logged via qDebug
(stripped from release builds) and let non-standard throws escape.

Log at qCritical so incidents are visible in shipped builds, add a
catch(...) so any exception type is contained, and null-guard the event
pointer when reporting the type.
MapMap started its OSC receiver on all interfaces (QHostAddress::Any) at
launch, so any device on the venue network could drive mappings or quit
the show — OSC has no authentication.

Bind to 127.0.0.1 by default and thread an acceptFromNetwork flag through
OscInterface into OscReceiver, which widens the bind to all interfaces
only when set. Expose it as an "Accept OSC from the network (less secure)"
checkbox in Preferences > OSC Setup, off by default. The value is
persisted and applied live: PreferenceDialog sets it before setOscPort()
rebinds the receiver. Also check the bind() result and log the address.
/mapmap/quit closed the application with no guard, so any OSC sender could
end a show. Gate it behind a new "Allow OSC to quit the application"
preference (off by default): applyAction now checks isOscQuitAllowed()
and drops the quit with a warning otherwise.

Wire the setting through MainWindow (persisted) and Preferences > OSC
Setup, and document the loopback-by-default and quit-disabled behavior in
docs/informations/osc.md.
OscReceiver logged every incoming datagram unconditionally, spamming the
log and costing CPU under high OSC rates (e.g. per-frame vertex control).

Add a verbose flag to OscReceiver, propagated from OscInterface::setVerbose
(driven by the --verbose flag), and only log received messages when it is
set. The one-time bind message stays.
MM::VERSION was hardcoded ("0.6.3") and duplicated the qmake VERSION.
Define the human-facing version once (MAPMAP_VERSION in mapmap.pro),
inject it via DEFINES, and have MM::VERSION read it (with a matching
literal fallback for direct compiles). qmake VERSION stays numeric
(1.0.0) so bundle and library versioning remain valid.

Relax SUPPORTED_FILE_VERSIONS to accept an optional -prerelease/+build
suffix and anchor it, so projects saved by 1.0.0-alpha.1 still load and
malformed version strings are rejected explicitly.
Guard the project-file version contract ProjectReader uses to accept or
reject a .mmp: MM::SUPPORTED_FILE_VERSIONS must keep matching x.y.z and the
pre-release/build suffixes that 1.0.0-alpha.1 writes, while rejecting
malformed strings.

The unit-test harness deliberately excludes the GUI/multimedia layer, so
MappingManager and a full ProjectReader/Writer round-trip cannot link here
without a larger harness change; those remain open under QUA-4 in AUDIT.md.
Seven call sites opened a QFile with the result cast to (void), so a
missing or locked file silently yielded empty content. Check each open()
and qWarning() with the file name on failure.

ConsoleWindow::writeLogFile is the exception: it is reached from the
installed message handler, so it returns quietly on failure rather than
warning (which would recurse).
src.pri is pulled in once per sub-project (.pri), so QMAKE_BUNDLE_DATA
gained the syphon_framework entry five times, producing five identical
copy rules and make's "overriding commands for target
...Syphon.framework" warnings.

Guard the entry with !contains() so it is added once. Regenerating the
Makefile now yields a single rule and no warning.
The Spanish catalogue shipped with 0 of 298 strings translated while the
app still advertised Spanish support. Translate all 298 UI strings;
lrelease now reports 298 finished, 0 unfinished.

Note: a few OSC-preference strings added on this branch are not yet in any
.ts catalogue; a future lupdate pass is needed to extract and translate
them across languages (tracked under QUA-5 in AUDIT.md).
parseSource() and parseLayer() logged a null result from newInstance() and
then dereferenced it via ->read(obj), so a corrupt or incompatible .mmp
entry whose registered class cannot be instantiated crashed the load.

Bail out on a null source/layer instead of calling read(): skip the source
with a warning, and report a clear error string for a failed layer so
readFile() returns false and the user sees a message.
The embedded MCP HTTP server accepted any request on its loopback port, so
any local process — or a web page the operator visited, via a spoofed Host
(DNS-rebinding) — could drive MapMap and read project/file metadata.

Reject requests whose Host (or Origin, when present) is not loopback, and
require an "Authorization: Bearer <token>" header. The token is generated
per process at startup and logged alongside the endpoint so a local MCP
client can be configured.

Note: MCP still starts by default on its port; making it opt-in (port 0
default) remains a separate product decision, tracked under SEC-4 in
AUDIT.md.
Both ProjectReader and ProjectWriter use QJsonDocument, so .mmp files are
JSON. The datasheet and the English manual still described the save format
as XML; correct them. Historical CHANGELOG entries about the original
XML-based v0.1 format are left as-is.
DEFAULT_MCP_PORT was 49452, so the MCP HTTP control surface started on
every launch. Default it to 0 (disabled); the user opts in by setting a
port in Preferences > MCP Setup, which already renders 0 as "Disabled".

Also reorder startMcpServer() so port 0 tears down any running server,
making a runtime switch to "Disabled" actually stop listening.
Two unchecked reads in the load path:
- MShape::read indexed each vertex sub-array as vertex[0]/vertex[1] with
  no size check, so a malformed [x] or [] entry read out of bounds. Skip
  vertices that do not have at least two components.
- parseProject cast Video/Image sources with Q_CHECK_PTR (a no-op in
  release builds) and then dereferenced the result, so a corrupt source
  claiming a media type it cannot cast to would crash. Null-check the cast.

Deeper fuzzing of ProjectReader still needs a headless model-layer test
harness (tracked with the QUA-4 remainder).
The .ts files were stale (last synced in May), so ~68 in-code strings
(Syphon, layer reorder, MCP, the OSC preferences, the professional-services
menu items) were never extracted and message locations were out of date.
Run lupdate to sync all five catalogues to the current sources.

Translate every new string in French and Spanish, including the Syphon
plural form: both now report 350/350 finished (0 unfinished). English (the
source language) falls back to source text at runtime; Chinese remains
partially translated and needs a translator.
Convert the string-based SIGNAL()/SLOT() connects to the compile-checked
pointer-to-member syntax in AboutDialog, ConsoleWindow, SourceGui,
SyphonServerDialog and MapperGLCanvasToolbar (13 connections).

Migrating surfaced a latent bug: the zoom dropdown connected to
QComboBox::activated(QString), which Qt 6 removed, so the connection was
silently dead. Switch it to &QComboBox::textActivated so "zoom from menu"
works again.

First QUA-1 increment. MainWindow.cpp (119 connects, many to overloaded
signals) and the two overloaded-setValue property-manager connects remain
for follow-up commits.
@aalex aalex added this to the 1.0 milestone Jul 3, 2026
@aalex aalex self-assigned this Jul 3, 2026
@aalex aalex marked this pull request as draft July 3, 2026 03:01
aalex added 3 commits July 2, 2026 23:12
Convert ~123 string-based SIGNAL()/SLOT() connections to the compile-checked
pointer-to-member form: MainWindow.cpp (116, including its disconnect() pairs),
PreferenceDialog.cpp (5), and the property-manager connects in LayerGui and
SourceGui (2, via qOverload for the overloaded setValue slot).

The only connection deliberately left string-based is
preferencesAction -> PreferenceDialog::exec, whose exec() is a private custom
override reachable only that way.

With the earlier dialog/toolbar increment, no string-based connects remain
outside vendored contrib code (and that one documented exception).
Thirteen menu/action labels used ASCII "..." while newer labels used the
Unicode ellipsis "…". Standardize on "…" (the platform convention for a
command that opens a dialog before acting). Patch the matching <source>
and <translation> entries across all catalogues in lockstep so French and
Spanish stay complete (350/350) and the runtime string lookup keeps
matching the code.
New WelcomeDialog: on first launch (until the user unticks "Show this
window at startup") it greets the user, points at the first steps —
"Import a media file…" (emits importMediaRequested, wired to importMedia)
and "Read the documentation" — and is reachable any time from
Help > "Welcome to MapMap…". The startup preference is stored in QSettings.

Note: the bundled example project from the UX-1 spec is intentionally left
out for now — it needs a runtime-tested .mmp asset, which cannot be
verified in this build-only environment. The new dialog strings still need
an lupdate pass to be translated.
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.

1 participant