Skip to content

build: harden release inputs and isolate test bundles - #355

Merged
PathGao merged 1 commit into
sftwrdotdev:masterfrom
PathGao:build/harden-release-inputs
Aug 2, 2026
Merged

build: harden release inputs and isolate test bundles#355
PathGao merged 1 commit into
sftwrdotdev:masterfrom
PathGao:build/harden-release-inputs

Conversation

@PathGao

@PathGao PathGao commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Use npm ci in release builds so the resolved dependency graph matches the lockfile.
  • Stop uploading portable Windows executables under installer-like names.
  • Add an isolated macOS test-bundle builder requiring a non-production bundle identifier.
  • Add workflow regression tests and update release documentation.

Scope

This is independent of the current Stack. It is related to the release hardening tracked in #334, but does not claim to resolve the pending Defender investigation.

Validation

  • npm ci
  • npm run check
  • npm test (152 passing)

@PathGao

PathGao commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator Author

@alecdotdev This PR also removes the release-workflow behavior that uploaded the portable executable a second time under the misleading MarkpadInstaller_* name. The actual NSIS installer remains the *-setup.exe artifact.

For #334: I saw that the issue was closed after identifying the custom uninstall script as the likely Defender trigger, but I cannot find a corresponding code change. Do you want to rework that uninstall path before the next Windows release, or was the issue closed because the release was removed and no further change is planned?

@PathGao
PathGao merged commit d70e0a5 into sftwrdotdev:master Aug 2, 2026
5 checks passed
@PathGao
PathGao deleted the build/harden-release-inputs branch August 2, 2026 16:05
PathGao added a commit that referenced this pull request Aug 6, 2026
VirusTotal scores the shipped binary `Trojan:Win32/Wacatac.B!ml`, and #334
already identified the cause: the uninstall path writes a batch file to %TEMP%,
writes a .vbs that runs it with a hidden window and then deletes itself, and
launches the pair through wscript.exe. That sequence is close to the definition
of the behaviour classifier that flags it. It is a false positive, but the
pattern it matches is really there, so the only way to clear it is to remove
the pattern.

The install half was already unreachable. `get_app_mode` entered installer mode
only on a filename containing `installer` or an explicit `--install`, and #355
stopped publishing any installer-named artifact, so nothing shipped could reach
`install_app`, `create_shortcut`, `check_install_status` or Installer.svelte.

The uninstall half was reachable, through `UninstallString = "…\Markpad.exe"
--uninstall` written by custom installs from 2.6.x. Two things now cover those:

  * NSIS rewrites that value. `Section Install` in the bundler template writes
    `UninstallString` to `$INSTDIR\uninstall.exe` with no `$UpdateMode` guard
    anywhere in the section, so it lands on a plain install and on an updater
    run alike. Both sides address the same key -- the custom installer used
    APP_NAME and the template uses ${PRODUCTNAME}, which are both "Markpad".
  * A hook drops the stale entry when the two disagree on a hive. Section
    Install writes through SHCTX, so an old install that chose the other hive
    keeps its row in Add/Remove Programs. The hook matches on the `--uninstall`
    tail, which only the custom installer ever wrote, and deletes just that key.

`forward_legacy_uninstall` is the backstop for the case both miss: a stray
`--uninstall` hands off to uninstall.exe beside the binary and exits. If there
is nothing to hand off to it starts normally, because a window the user did not
ask for is still a better answer than a click that does nothing.

Signing would not have helped. A certificate addresses SmartScreen reputation,
not a behaviour classifier's score.

Drops mslnk and chrono, which setup.rs was the only user of.

Verified: `npm run check` 0 errors, `npm test` 728/728, `cargo test` 125/125
(down 32, the count of `#[test]` inside the deleted setup.rs). The Windows-only
`forward_legacy_uninstall` is not built by a macOS `cargo check`, so it was type
-checked separately against x86_64-pc-windows-msvc. The NSIS macro cannot be
compiled here at all; makensis runs in the Windows build-test leg, which is
where a malformed macro would surface.

Co-authored-by: PathGao <gaoyanbo@gaoyanbodeMacBook-Air.local>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
PathGao added a commit that referenced this pull request Aug 7, 2026
* fix(windows): remove dead installer code that survives in the binary

The code that triggered Trojan:Win32/Wacatac.B!ml was removed in #481,
but three things that still look like an installer to a behaviour
classifier were left in the binary:

- forward_legacy_uninstall() — answered --uninstall from pre-2.7 custom
  installs.  The NSIS template writes UninstallString = uninstall.exe
  unconditionally, and the auto-updater passes /UPDATE which runs Section
  Install, so every install that has ever updated through 2.7.1 already
  has the corrected registry entry.  The hooks.nsi macro also drops
  stray --uninstall entries from both hives as a backstop.

- is_installer_mode detection — checked for --install or 'installer' in
  the exe name.  No shipped artifact has triggered either since #355
  stopped publishing an installer-named file, and #481 removed
  Installer.svelte, install_app, and the invoke_handler registrations
  that were the only consumers of the resulting window label.

- The 'installer' window label in capabilities/default.json — window
  that no code can create any more.

None of these do anything observable today, but a scanner that reads the
file rather than tracing reachability still sees them.  Removing them
shrinks the binary by the 55 lines of Rust that remained from the old
custom installer.

Addresses the setup.exe detection reported in #466 (comment).

* chore(deps): bump mermaid 11.16.0 -> 11.16.1 to fix npm audit

npm audit reports 5 moderate vulnerabilities in mermaid <= 11.16.0:
- GHSA-c4c3-pg64-4m4v (prototype pollution)
- GHSA-6x64-9x62-f2gx (CSS injection)
- GHSA-3rrr-jr9j-h3q3 (prototype pollution)
- GHSA-2v8p-3f2j-5mp7 (infinite loop DoS)
- GHSA-rhh3-jpg6-66xh (DoS)

All fixed in 11.16.1.

---------

Co-authored-by: PathGao <PathGao@users.noreply.github.com>
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