nickna / nickna/SharpTS

Feature: Add post-build Authenticode signing support for --compile -t exe

Open
#42 0 comments 0 reactions 0 assignees View on GitHub
good first issue
Dominant language
C#
Stars
154
Forks
4
Avg merge
2h 46m
Merged PRs (30d)
189

Description

## Motivation

Compiled single-file executables (`sharpts --compile script.ts -t exe`) are unsigned and get flagged by behavioral AV engines as suspected packers/droppers — see #4 (WithSecure DeepGuard quarantines the output as `W32/Malware!DeepGuard.n`).

This pattern is intrinsic to the apphost-plus-appended-bundle format that both our `ManualBundler` and the `SdkBundler` (Microsoft's `HostWriter`) produce. Rewriting the bundler does not fix it — a differently-shaped unsigned bundle is still an unsigned bundle. Authenticode signing is the real long-term mitigation: once the signing cert earns some reputation, heuristic flags largely stop firing.

## Proposed behavior

Add post-build signing as an opt-in step after bundling, for `-t exe` output only.

CLI surface (rough sketch, open to revision):

```
sharpts --compile script.ts -t exe \
--sign \
[--sign-pass ] \
[--sign-timestamp ] \
[--sign-digest sha256]
```

- `--sign` accepts either a `.pfx` path or a certificate store thumbprint (`sha1:ABCD...`). Default digest `sha256`. Default timestamp server is the DigiCert RFC3161 endpoint; overridable.
- Password can also come from `SHARPTS_SIGN_PASS` env var to keep it out of shell history.
- Signing runs after the bundler produces the final `.exe`. On failure, the `.exe` remains on disk unsigned and the tool exits non-zero with a clear error.

## Implementation notes

- On Windows, shell out to `signtool.exe` if it is on `PATH` or resolvable from a Windows SDK install. Fallback to `SignTool` from the .NET SDK if present.
- On Linux/macOS, shell out to `osslsigncode` if available; otherwise emit a clear error saying the platform needs `osslsigncode` installed and point at install instructions.
- Keep the signing logic in `PEPacker` (separate repo/package) alongside the bundling code, so the `--sign` flag is thin glue in SharpTS.
- The apphost is already signed by Microsoft when shipped in the SDK; our byte-patching invalidates that signature, which is expected. Our re-signing replaces it.

## Out of scope for this issue

- EV / OV certificate procurement guidance — covered in a separate docs issue.
- Authenticode signing for DLL output (`-t dll`) — possible, but drop-in `dotnet script.dll` execution does not really need it. Can be a follow-up.

## Docs work

- Add `docs/antivirus-false-positives.md` explaining why single-file .NET outputs trigger behavioral AV and pointing to `--sign` as the fix.
- README section on `--sign` with a worked example (self-test with a self-signed cert on a dev machine; production example with a real code-signing cert).

## Acceptance criteria

- `sharpts --compile hello.ts -t exe --sign test.pfx --sign-pass ...` produces an `.exe` that `signtool verify /pa /v hello.exe` reports as `Successfully verified`.
- On sign failure (bad password, expired cert, missing `signtool`), the tool prints an actionable error and exits non-zero.
- CI gains a test that produces a signed exe with a self-generated test cert and verifies the signature. Cross-platform via `osslsigncode` if feasible.

## Related

- #4 — WithSecure / F-Secure DeepGuard false-positive on compiled exe. Signing does not guarantee the FP goes away on day one (cert reputation needs to accrue) but is the recognized long-term path.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.