17 Commits

Author SHA1 Message Date
MrSphay
f5303fc536 Modernize MrTrust GUI
All checks were successful
Build MrTrust / build (push) Successful in 5m57s
2026-05-16 13:43:52 +02:00
MrSphay
a1819900b1 Fix GUI hover animation color capture
All checks were successful
Build MrTrust / build (push) Successful in 5m47s
2026-05-16 13:16:32 +02:00
MrSphay
46f9f95dcf Add diagnostics tab and UI animations
All checks were successful
Build MrTrust / build (push) Successful in 5m31s
2026-05-16 13:05:12 +02:00
MrSphay
8ce78741bb Make README a retro trust advertisement
All checks were successful
Build MrTrust / build (push) Successful in 4m13s
2026-05-16 04:13:17 +02:00
MrSphay
63f134e55e Update user-facing README
All checks were successful
Build MrTrust / build (push) Successful in 4m21s
2026-05-16 04:03:06 +02:00
MrSphay
e37a0b56c4 Limit release upload to push runs
All checks were successful
Build MrTrust / build (push) Successful in 4m24s
2026-05-16 03:28:54 +02:00
MrSphay
a5cea35069 Add Linux signing helper payload
All checks were successful
Build MrTrust / build (push) Successful in 5m9s
2026-05-16 03:20:41 +02:00
MrSphay
f3c353b821 Fix Gitea workflow YAML parsing
Some checks failed
Build MrTrust / build (push) Failing after 40s
2026-05-16 03:16:28 +02:00
MrSphay
d3234e03b8 Make release asset upload idempotent 2026-05-16 03:14:48 +02:00
631a66dab1 0.1.3
Added Ubuntu-runner signing support through osslsigncode and PFX secrets.
2026-05-16 03:08:43 +02:00
16e5d1377c Parse Gitea release id as JSON
All checks were successful
Build MrTrust / build (push) Successful in 4m20s
2026-05-16 02:42:55 +02:00
7a2a643d15 Use public Gitea API for release asset upload
Some checks failed
Build MrTrust / build (push) Failing after 4m22s
2026-05-16 02:37:41 +02:00
e885978aba Attach release ZIP from runner
Some checks failed
Build MrTrust / build (push) Failing after 2m47s
2026-05-16 02:33:38 +02:00
d6e2d5ab52 Prepare MrTrust 0.1.2 release
All checks were successful
Build MrTrust / build (push) Successful in 2m37s
2026-05-16 02:28:20 +02:00
84a5df7216 Add autonomous MrTrust target integration contract
All checks were successful
Build MrTrust / build (push) Successful in 2m45s
2026-05-16 02:03:25 +02:00
01148f4703 Clarify MrTrust target project integration for agents
All checks were successful
Build MrTrust / build (push) Successful in 2m31s
2026-05-16 01:46:36 +02:00
93ca15a881 Make MrTrust executable standalone
All checks were successful
Build MrTrust / build (push) Successful in 3m49s
2026-05-16 01:21:52 +02:00
15 changed files with 1424 additions and 333 deletions

View File

@@ -10,6 +10,8 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
MRTRUST_VERSION: 0.1.4
steps: steps:
- name: Checkout - name: Checkout
@@ -24,32 +26,25 @@ jobs:
shell: bash shell: bash
run: | run: |
set -euo pipefail set -euo pipefail
version="0.1.1"
dotnet publish src/MrTrustLauncher.csproj \ dotnet publish src/MrTrustLauncher.csproj \
--configuration Release \ --configuration Release \
--runtime win-x64 \ --runtime win-x64 \
--output dist/build \ --output dist/build \
-p:EnableWindowsTargeting=true \ -p:EnableWindowsTargeting=true \
-p:PublishSingleFile=true \ -p:PublishSingleFile=true \
-p:SelfContained=false -p:SelfContained=true
cp dist/build/MrTrust.exe dist/MrTrust.exe cp dist/build/MrTrust.exe dist/MrTrust.exe
- name: Build release ZIP - name: Build release ZIP
shell: bash shell: bash
run: | run: |
set -euo pipefail set -euo pipefail
version="0.1.1" version="${MRTRUST_VERSION}"
package_root="dist/MrTrust-${version}" package_root="dist/MrTrust-${version}"
rm -rf "$package_root" "dist/MrTrust-${version}.zip" rm -rf "$package_root" "dist/MrTrust-${version}.zip"
mkdir -p "$package_root/scripts" "$package_root/assets/certificates" "$package_root/docs" mkdir -p "$package_root"
cp dist/MrTrust.exe "$package_root/" cp dist/MrTrust.exe "$package_root/"
cp MrTrust.ps1 README.md "$package_root/" cp README.md "$package_root/"
cp assets/MrTrust.ico "$package_root/assets/"
cp scripts/Install-MrTrust.ps1 scripts/Uninstall-MrTrust.ps1 scripts/Start-MrTrustGui.ps1 "$package_root/scripts/"
cp assets/certificates/MrSphay-LocalTrust-Root.cer "$package_root/assets/certificates/"
cp assets/certificates/MrSphay-CodeSigning.cer "$package_root/assets/certificates/"
cp assets/certificates/thumbprints.txt "$package_root/assets/certificates/"
cp docs/security-model.md "$package_root/docs/"
(cd dist && zip -r "MrTrust-${version}.zip" "MrTrust-${version}") (cd dist && zip -r "MrTrust-${version}.zip" "MrTrust-${version}")
- name: Show package contents - name: Show package contents
@@ -60,5 +55,48 @@ jobs:
- name: Upload release artifact - name: Upload release artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: MrTrust-0.1.1 name: MrTrust-${{ env.MRTRUST_VERSION }}
path: dist/MrTrust-0.1.1.zip path: dist/MrTrust-${{ env.MRTRUST_VERSION }}.zip
- name: Attach ZIP to Gitea release
if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch'
shell: bash
env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
version="${MRTRUST_VERSION}"
asset_name="MrTrust-${version}.zip"
api="https://git.wilkensxl.de/api/v1/repos/MrSphay/MrTrust"
release_response="$(mktemp)"
status="$(curl -sS -o "$release_response" -w "%{http_code}" -H "Authorization: token ${GITEA_TOKEN}" "${api}/releases/tags/v${version}")"
if [ "$status" = "404" ]; then
release_json="$(VERSION="$version" python3 -c 'import json, os; version = os.environ["VERSION"]; print(json.dumps({"tag_name": f"v{version}", "target_commitish": "main", "name": f"MrTrust v{version}", "body": f"MrTrust v{version} release built by the Gitea runner.", "draft": False, "prerelease": False}))')"
curl -fsS \
-X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "$release_json" \
"${api}/releases" > "$release_response"
elif [ "$status" != "200" ]; then
cat "$release_response" >&2
exit 1
fi
release_json="$(cat "$release_response")"
release_id="$(printf '%s' "$release_json" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')"
if [ -z "$release_id" ]; then
echo "Could not resolve release id for v${version}" >&2
exit 1
fi
existing_asset_id="$(RELEASE_JSON="$release_json" ASSET_NAME="$asset_name" python3 -c 'import json, os; release = json.loads(os.environ["RELEASE_JSON"]); asset_name = os.environ["ASSET_NAME"]; print(next((str(asset.get("id", "")) for asset in release.get("assets", []) if asset.get("name") == asset_name), ""))')"
if [ -n "$existing_asset_id" ]; then
curl -fsS \
-X DELETE \
-H "Authorization: token ${GITEA_TOKEN}" \
"${api}/releases/${release_id}/assets/${existing_asset_id}"
fi
curl -fsS \
-X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-F "attachment=@dist/${asset_name}" \
"${api}/releases/${release_id}/assets?name=${asset_name}"

View File

@@ -16,8 +16,10 @@ MrTrust manages explicit Windows certificate trust for MrSphay software.
- `assets/certificates/` contains public certificates only. - `assets/certificates/` contains public certificates only.
- `private/` is ignored and may contain local signing material. - `private/` is ignored and may contain local signing material.
- `docs/integration-prompt.md` is the prompt for adding MrTrust to other projects. - `docs/integration-prompt.md` is the prompt for adding MrTrust to other projects.
- `docs/agent-target-integration.md` is the autonomous runbook for agents modifying target projects.
- `mrtrust.integration.json` is the machine-readable integration contract.
- `docs/security-model.md` documents the intended behavior and limits. - `docs/security-model.md` documents the intended behavior and limits.
- `MrTrust.ps1 gui` is the user-facing GUI entry point. - `MrTrust.exe` is the user-facing standalone trust installer. The PowerShell scripts are source/build internals.
## Verification ## Verification

View File

@@ -1,5 +1,23 @@
# Changelog # Changelog
## 0.1.4
- Added a diagnostics tab for checking `.exe`, `.msi`, `.dll`, and `.cat` files.
- Added signature status, MrSphay signer matching, and Mark-of-the-Web detection.
- Added an in-app SmartScreen explanation to clarify reputation versus local certificate trust.
- Added subtle Modrinth-style hover, press, progress, and accent animations.
## 0.1.3
- Added Ubuntu-runner signing support through `osslsigncode` and PFX secrets.
## 0.1.2
- Made `MrTrust.exe` a standalone user-facing release artifact.
- Added an autonomous target-project integration runbook for agents.
- Added `mrtrust.integration.json` as a machine-readable integration contract.
- Updated agent-facing documentation to prefer the standalone `MrTrust.exe` integration path.
## 0.1.1 ## 0.1.1
- Added a custom MrTrust application icon and embedded it into the launcher. - Added a custom MrTrust application icon and embedded it into the launcher.

306
README.md
View File

@@ -1,122 +1,240 @@
# MrTrust # MrTrust
MrTrust is a small Windows trust-onboarding kit for MrSphay software. ## Brought To You By The Fine People Of MrSphay
It is designed for this workflow: Good morning, citizen.
1. MrSphay creates a private code-signing certificate once. Has Windows ever looked at your freshly downloaded MrSphay program and said:
2. MrSphay publishes only the public trust certificate with MrTrust.
3. A user runs MrTrust once and explicitly approves installing that public certificate.
4. MrSphay projects signed with the matching certificate chain are shown as trusted on that PC.
MrTrust does not bypass Microsoft Defender or SmartScreen. Windows can still scan, quarantine, or warn about suspicious files. This project only manages normal Windows certificate trust with visible user consent.
## What It Contains
- `MrTrust.ps1 gui` opens a simple Windows interface for installing or removing trust.
- `scripts/New-MrTrustCertificate.ps1` creates a local root certificate and a code-signing certificate for the publisher.
- `scripts/Install-MrTrust.ps1` installs the public trust certificate for the current user or the local machine.
- `scripts/Uninstall-MrTrust.ps1` removes the MrTrust certificate again.
- `scripts/Sign-MrTrustProject.ps1` signs `.exe`, `.msi`, `.ps1`, and other Authenticode-compatible files.
- `scripts/New-MrTrustRelease.ps1` builds a distributable ZIP package.
- `docs/integration-prompt.md` is a prompt you can paste into other Windows projects.
## Quick Start For MrSphay
Create the certificates:
```powershell
.\scripts\New-MrTrustCertificate.ps1
```
This writes:
- public certificates to `assets\certificates\`
- private signing material to `private\`
The `private\` directory is ignored by git. Do not publish `.pfx` files or passwords.
Install the public trust certificate on your own PC:
```powershell
.\MrTrust.ps1 install
```
Open the GUI:
```powershell
.\MrTrust.ps1 gui
```
Sign another project build:
```powershell
.\MrTrust.ps1 sign `
-Path "C:\Path\To\App.exe" `
-PfxPath ".\private\MrSphay-CodeSigning.pfx"
```
Remove the trust certificate:
```powershell
.\MrTrust.ps1 uninstall
```
Build a user-facing ZIP release:
```powershell
.\scripts\New-MrTrustRelease.ps1 -Version 0.1.1
```
The Gitea workflow `.gitea/workflows/build.yml` builds the Windows launcher EXE on an `ubuntu-latest` runner with .NET Windows cross-targeting, then uploads the ZIP as an artifact.
## User Installation
For normal users, distribute MrTrust with the public certificate file:
```text ```text
assets\certificates\MrSphay-LocalTrust-Root.cer Unknown publisher? Sounds suspicious, pal.
assets\certificates\MrSphay-CodeSigning.cer
``` ```
The user runs: Then step right up to **MrTrust**, the cheerful little trust-onboarding utility that helps your PC recognize signed MrSphay software without poking holes in Windows security.
```powershell One click. One confirmation. A brighter tomorrow for properly signed applications.
.\MrTrust.ps1 gui
MrTrust installs public certificates only after you say so. It does not disable Microsoft Defender, SmartScreen, UAC, firewall rules, company policies, common sense, or the big red security lever nobody should touch.
## Download Your Complimentary Trust Appliance
Latest release page:
```text
https://git.wilkensxl.de/MrSphay/MrTrust/releases
``` ```
By default, MrTrust installs trust only for the current Windows user: Download the newest:
```text
MrTrust-<version>.zip
```
Extract it, then run:
```text
MrTrust.exe
```
That is the normal user version. It is standalone and carries the public MrSphay certificates it needs.
## Operating Your Trust-O-Matic 3000
Inside the friendly GUI:
- `Install trust` tells Windows to trust MrSphay public signing certificates.
- `Remove trust` politely takes that trust back out again.
- `Refresh` checks whether your PC is currently feeling cooperative.
Default installation scope:
```text ```text
Root certificate -> Cert:\CurrentUser\Root Root certificate -> Cert:\CurrentUser\Root
Code-signing certificate -> Cert:\CurrentUser\TrustedPublisher Code-signing certificate -> Cert:\CurrentUser\TrustedPublisher
``` ```
For all users on the machine, run PowerShell as Administrator: That means the trust applies only to the current Windows user.
```powershell For all users on the PC, run `MrTrust.exe` as Administrator and choose the all-users option. Please operate administrator privileges responsibly. The future depends on it.
.\MrTrust.ps1 install -Scope LocalMachine
## How The Magic Works
There is no magic. That is how you know it is working.
The approved flow:
1. A MrSphay app is signed during its release build.
2. You run `MrTrust.exe`.
3. You review the certificate details.
4. You confirm the trust installation.
5. Windows can validate signed MrSphay apps on that PC.
If the app is not signed, MrTrust cannot help it. Even the finest paperwork cannot identify a person who never showed up.
## Safety Notice From The Department Of Not Breaking Windows
MrTrust does not:
- make unsigned programs trusted
- bypass Defender
- bypass SmartScreen
- remove UAC prompts
- silently install certificates
- install private signing keys on user machines
- make sketchy software less sketchy
Windows may still scan, block, warn, quarantine, or ask questions. MrTrust only handles normal certificate trust.
## Public Certificate Values
These values are public and safe to use in documentation, agent prompts, and integration metadata:
```text
Publisher:
MrSphay
Root certificate thumbprint:
39F7458E6E2C1126E93E6A1F228196006B174DF2
Code-signing certificate thumbprint:
A024A89200469F099EC4A172B4F96F6428AFD41B
``` ```
## Using This Repo With Other Agents They are also stored here:
Yes. Give another agent this repository URL and the target Windows project, then paste `docs/integration-prompt.md`. ```text
assets/certificates/thumbprints.txt
mrtrust.integration.json
```
Both sides have to be wired: ## For The Workshop Crew
- MrTrust side: users install the public trust certificates once. Local maintainer commands:
- Target project side: release artifacts are signed with the MrSphay code-signing certificate.
- Installer side, optional: the target app can offer "Open MrTrust" or bundle the MrTrust ZIP, but it must not silently change trust.
If the target project is not signed, MrTrust cannot make it trusted. ```powershell
.\MrTrust.ps1 gui
.\MrTrust.ps1 install
.\MrTrust.ps1 uninstall
```
## Important Limits Create or refresh local certificates:
- This only helps for programs signed with the matching MrSphay certificate chain. ```powershell
- It does not make unsigned programs trusted. .\scripts\New-MrTrustCertificate.ps1
- It does not disable Defender, SmartScreen, UAC, or enterprise policies. ```
- Public distribution without warnings is still best handled with a recognized commercial code-signing certificate.
## Recommended Project Integration Build a release ZIP locally:
Use `docs/integration-prompt.md` in another Windows project. The prompt tells Codex or another assistant to add a visible trust check, a link or bundled copy of MrTrust, and a signing step without hiding security changes from the user. ```powershell
.\scripts\New-MrTrustRelease.ps1 -Version 0.1.4
```
Sign an artifact locally on Windows:
```powershell
.\MrTrust.ps1 sign `
-Path "C:\Path\To\App.exe" `
-CertificateThumbprint A024A89200469F099EC4A172B4F96F6428AFD41B
```
Private signing material belongs only in:
```text
private/
Bitwarden
Gitea repository secrets
```
Never commit `.pfx` files, private keys, passwords, or Base64-encoded signing material. That is not trust. That is handing out the vault keys at the snack counter.
## Gitea Secrets For Other Projects
For another project to sign Windows release artifacts on an Ubuntu Gitea runner, add these secrets to that target repository:
```text
MRTRUST_CODESIGN_PFX_BASE64
MRTRUST_CODESIGN_PFX_PASSWORD
```
Optional timestamp override:
```text
MRTRUST_TIMESTAMP_URL
```
The first two values are private signing credentials. Keep them in Bitwarden and Gitea Secrets only.
Ubuntu helper script:
```text
scripts/Sign-MrTrustProjectLinux.sh
```
It signs supported Windows artifacts with `osslsigncode`:
```text
.exe
.msi
.dll
.cat
```
PowerShell scripts should be signed on Windows, not Ubuntu.
## Installing MrTrust Into Another Project
Give your coding agent this repository:
```text
https://git.wilkensxl.de/MrSphay/MrTrust
```
Tell it to read:
```text
mrtrust.integration.json
docs/agent-target-integration.md
docs/integration-prompt.md
```
The target project should end up with:
- signed Windows release artifacts
- a visible optional MrTrust setup path
- a link to or bundled copy of `MrTrust.exe`
- documentation for installing and removing trust
- no committed private signing material
Remember the two-part handshake:
- MrTrust side: the user installs public trust certificates once.
- Target project side: the app is signed with the MrSphay code-signing certificate.
No signature, no trust. No trust, no victory parade.
## Current Build
The Gitea workflow builds `MrTrust.exe` on `ubuntu-latest` with .NET Windows cross-targeting.
On pushes to `main`, it:
1. builds the standalone Windows executable
2. packages `MrTrust-0.1.4.zip`
3. uploads the workflow artifact
4. attaches the ZIP to the Gitea release
Manual `workflow_dispatch` runs build artifacts but do not attach release assets. This prevents duplicate release uploads, which are bad for morale and paperwork.
## Final Safety Reminder
MrTrust is intentionally visible and reversible:
- the GUI shows the trust state
- installation requires confirmation
- removal is available in the same tool
- public certificates are embedded in the executable
- private signing material is never needed on user machines
For broad public distribution without SmartScreen reputation delays, a recognized commercial code-signing certificate is still the cleanest option.
Thank you for choosing MrTrust. Stay signed, stay verified, and keep your release pipeline tidy.

View File

@@ -0,0 +1,209 @@
# Agent Target Integration Runbook
Use this file when you are an agent modifying another Windows project to support MrTrust.
## Non-Negotiable Goal
Modify the target project so that, after a user has installed trust with `MrTrust.exe`, the target project's MrSphay-signed Windows release artifacts start and validate normally.
Do not modify Windows security policy, Defender, SmartScreen, UAC, or certificate stores silently. MrTrust must remain visible, optional, and reversible.
## Inputs To Read First
1. Read `mrtrust.integration.json`.
2. Read `docs/integration-prompt.md`.
3. Inspect the target project for:
- build commands
- release artifact paths
- installer technology
- existing signing steps
- user-facing docs
- CI workflows
If the target project already has equivalent trust or signing integration, update it instead of duplicating it.
## Required Target Project Changes
Apply every item that fits the target project:
1. Add a user-facing MrTrust entry point.
- If the project has an installer, add an optional action named `Open MrTrust` or `Trust MrSphay signed apps`.
- If the project has an app settings/about/help screen, add a button or menu item that launches `MrTrust.exe`.
- If the project has no UI or installer, add release documentation linking to or bundling `MrTrust.exe`.
2. Bundle or link the standalone executable.
- Preferred bundled path: `MrTrust\MrTrust.exe`.
- Source repository: `https://git.wilkensxl.de/MrSphay/MrTrust`.
- Release page: `https://git.wilkensxl.de/MrSphay/MrTrust/releases`.
- Do not require users to run `MrTrust.ps1`.
- Do not copy MrTrust source scripts into user-facing release output unless the target project explicitly ships source.
3. Add signing for Windows artifacts.
- Sign `.exe`, `.msi`, `.dll`, `.ps1`, `.psm1`, `.psd1`, and `.cat` release artifacts.
- Use the public thumbprint from `mrtrust.integration.json`.
- Only run signing where private signing material is securely available.
- Do not commit `.pfx`, private keys, passwords, tokens, or signing secrets.
- On the available `ubuntu-latest` Gitea runners, use `osslsigncode` with PFX secrets for `.exe`, `.dll`, `.msi`, and `.cat`.
- PowerShell script signing (`.ps1`, `.psm1`, `.psd1`) still requires a Windows signing environment. If no Windows runner exists, document that limitation instead of blocking PE/MSI signing.
4. Add verification.
- Verify the target project still builds.
- Verify the release output contains the app artifacts and either `MrTrust.exe` or clear MrTrust download instructions.
- Verify signing status where the OS/tooling is available.
- Verify no private signing material is tracked or present in release output.
## Default Commands
Open MrTrust from a bundled release:
```powershell
.\MrTrust\MrTrust.exe
```
Sign one artifact from a Windows release runner:
```powershell
.\MrTrust\MrTrust.exe sign -Path .\dist\App.exe -CertificateThumbprint A024A89200469F099EC4A172B4F96F6428AFD41B
```
Sign a release directory:
```powershell
.\MrTrust\MrTrust.exe sign -Path .\dist -CertificateThumbprint A024A89200469F099EC4A172B4F96F6428AFD41B
```
Check a signature:
```powershell
Get-AuthenticodeSignature .\dist\App.exe | Format-List Status,SignerCertificate,StatusMessage
```
Sign a release directory on an Ubuntu Gitea runner:
```bash
bash ./MrTrust/scripts/Sign-MrTrustProjectLinux.sh ./dist
```
## Installer Patterns
### Inno Setup
Bundle `MrTrust.exe` and add an optional task or post-install action:
```ini
[Files]
Source: "MrTrust\MrTrust.exe"; DestDir: "{app}\MrTrust"; Flags: ignoreversion
[Run]
Filename: "{app}\MrTrust\MrTrust.exe"; Description: "Open MrTrust"; Flags: postinstall skipifsilent nowait
```
### NSIS
```nsis
SetOutPath "$INSTDIR\MrTrust"
File "MrTrust\MrTrust.exe"
CreateShortcut "$SMPROGRAMS\$StartMenuFolder\Open MrTrust.lnk" "$INSTDIR\MrTrust\MrTrust.exe"
```
### WiX
Install `MrTrust.exe` as a regular file under an application `MrTrust` folder and expose a Start Menu shortcut or installer UI action. Do not run it silently during install.
### Electron Builder
Add `MrTrust\MrTrust.exe` to `extraResources`, then add a Help/About action that launches the copied executable with the platform shell API. Keep the action user-initiated.
### Portable ZIP
Place `MrTrust.exe` next to the app under:
```text
MrTrust\MrTrust.exe
```
Document that users should run it once before launching signed MrSphay apps if Windows does not yet trust the publisher.
## CI Signing Patterns
### Gitea Actions On Ubuntu Runner
Use this when only `ubuntu-latest`, `ubuntu-24.04`, or `ubuntu-22.04` runners are available.
Required Gitea secrets:
```text
MRTRUST_CODESIGN_PFX_BASE64
MRTRUST_CODESIGN_PFX_PASSWORD
```
Create `MRTRUST_CODESIGN_PFX_BASE64` locally from the private `.pfx`:
```powershell
[Convert]::ToBase64String([IO.File]::ReadAllBytes(".\private\MrSphay-CodeSigning.pfx")) | Set-Clipboard
```
Then paste the clipboard value into the Gitea secret. Do not commit the `.pfx` or the base64 value.
Ubuntu workflow step:
```yaml
- name: Install signing tool
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y osslsigncode
- name: Sign Windows artifacts
shell: bash
env:
MRTRUST_CODESIGN_PFX_BASE64: ${{ secrets.MRTRUST_CODESIGN_PFX_BASE64 }}
MRTRUST_CODESIGN_PFX_PASSWORD: ${{ secrets.MRTRUST_CODESIGN_PFX_PASSWORD }}
run: |
bash ./MrTrust/scripts/Sign-MrTrustProjectLinux.sh ./dist
```
This signs `.exe`, `.dll`, `.msi`, and `.cat` artifacts. It does not sign PowerShell script files.
### Gitea Actions On Windows Runner
```yaml
- name: Sign Windows artifacts
shell: powershell
run: |
.\MrTrust\MrTrust.exe sign -Path .\dist -CertificateThumbprint A024A89200469F099EC4A172B4F96F6428AFD41B
```
Use this only on a runner where the matching private code-signing certificate is installed in `Cert:\CurrentUser\My` or `Cert:\LocalMachine\My`.
### Local Secure Release Machine
```powershell
.\MrTrust\MrTrust.exe sign -Path .\dist -CertificateThumbprint A024A89200469F099EC4A172B4F96F6428AFD41B
```
Run this after build and before packaging.
## Autonomy Rules
Make reasonable target-project-specific choices without asking the user when:
- artifact paths are discoverable from existing build scripts
- installer technology is obvious from repository files
- there is already a docs or release notes location
- CI already has a Windows release job you can extend
Stop and ask the user only when:
- signing requires a private certificate that is not present and no secret mechanism exists
- the target project has multiple conflicting release systems and no primary release path is identifiable
- a requested change would silently alter trust or weaken security policy
## Completion Checklist
- Target project has a visible MrTrust user path.
- Target project links to or bundles standalone `MrTrust.exe`.
- Supported Windows release artifacts are signed or the blocker is explicitly documented.
- User docs explain install and remove trust.
- No private signing material is committed.
- Target project build/release verification ran, or the exact blocker is documented.

View File

@@ -2,6 +2,10 @@
Use this prompt in another Windows project when you want Codex or another agent to add MrTrust support. Use this prompt in another Windows project when you want Codex or another agent to add MrTrust support.
Before changing the target project, read:
- `mrtrust.integration.json`
- `docs/agent-target-integration.md`
```text ```text
Integrate MrTrust support into this Windows project. Integrate MrTrust support into this Windows project.
@@ -10,22 +14,33 @@ Goal:
- The project must not disable Defender, SmartScreen, UAC, or Windows security policy. - The project must not disable Defender, SmartScreen, UAC, or Windows security policy.
- All trust changes must be visible and require explicit user confirmation. - All trust changes must be visible and require explicit user confirmation.
Agent contract:
- You are modifying the target Windows project, not MrTrust itself.
- Use `mrtrust.integration.json` and `docs/agent-target-integration.md` as the authoritative integration contract.
- Add MrTrust as a visible, optional trust-onboarding path for users.
- Add or update the target project's release/signing pipeline so produced Windows artifacts are signed.
- Do not mark the task complete after only copying documentation. The target project needs user-facing integration and signing support where applicable.
Implementation requirements: Implementation requirements:
- Add a docs section that explains MrTrust and links to or bundles the MrTrust release ZIP. - Add a docs section that explains MrTrust and links to or bundles the MrTrust release ZIP.
- Treat `MrTrust.exe` from the release ZIP as the user-facing integration point. Do not require users to copy or run MrTrust PowerShell source files.
- Add a build or release signing step that signs Windows artifacts with the MrSphay code-signing certificate. - Add a build or release signing step that signs Windows artifacts with the MrSphay code-signing certificate.
- Do not commit any .pfx file, private key, certificate password, or signing secret. - Do not commit any .pfx file, private key, certificate password, or signing secret.
- If the project has an installer, add an optional "Open MrTrust" action that runs: - If the project has an installer, add an optional "Open MrTrust" action that runs:
powershell.exe -ExecutionPolicy Bypass -File .\MrTrust\MrTrust.ps1 gui .\MrTrust\MrTrust.exe
- Prefer CurrentUser certificate installation by default. - Prefer CurrentUser certificate installation by default.
- Only offer LocalMachine installation when the user explicitly chooses an all-users install and the process is elevated. - Only offer LocalMachine installation when the user explicitly chooses an all-users install and the process is elevated.
- Add an uninstall path or documentation that runs: - Add an uninstall path or documentation that opens MrTrust again and tells the user to choose "Remove trust".
powershell.exe -ExecutionPolicy Bypass -File .\MrTrust\MrTrust.ps1 uninstall
- Keep the UI wording clear: the user is trusting MrSphay signed software, not bypassing Windows security. - Keep the UI wording clear: the user is trusting MrSphay signed software, not bypassing Windows security.
- If this project produces an .exe, .msi, .dll, .ps1, .psm1, .psd1, or .cat release artifact, sign it with: - If this project produces an .exe, .msi, .dll, or .cat release artifact on an Ubuntu Gitea runner, sign it with:
powershell.exe -ExecutionPolicy Bypass -File .\MrTrust\MrTrust.ps1 sign -Path <artifact-path> -CertificateThumbprint A024A89200469F099EC4A172B4F96F6428AFD41B bash ./MrTrust/scripts/Sign-MrTrustProjectLinux.sh <artifact-path>
- Treat the certificate thumbprint as public metadata, but never commit private signing material. - Treat the certificate thumbprint as public metadata, but never commit private signing material.
- Configure Gitea secrets `MRTRUST_CODESIGN_PFX_BASE64` and `MRTRUST_CODESIGN_PFX_PASSWORD` for Ubuntu runner signing.
- If the target project needs .ps1, .psm1, or .psd1 signing, use a Windows signing environment or document that script signing is not available on the current Ubuntu-only runners.
- Do not put private signing material into the target repository or user-facing release ZIP.
Verification: Verification:
- Confirm the target project's user-facing release contains either a link to the MrTrust release ZIP or a bundled copy of `MrTrust.exe`.
- Confirm unsigned builds still show as unsigned. - Confirm unsigned builds still show as unsigned.
- Confirm signed builds validate after MrTrust installation. - Confirm signed builds validate after MrTrust installation.
- Confirm the MrTrust certificate can be removed again. - Confirm the MrTrust certificate can be removed again.

View File

@@ -1,7 +1,16 @@
{ {
"name": "codex-agent-repository-kit", "name": "codex-agent-repository-kit",
"version": "1.0.5", "version": "1.0.6",
"description": "Universal repository baseline for Codex-assisted projects.", "description": "Universal repository baseline for Codex-assisted projects.",
"mrtrustIntegration": {
"contract": "mrtrust.integration.json",
"agentRunbook": "docs/agent-target-integration.md",
"prompt": "docs/integration-prompt.md",
"standaloneExecutable": "MrTrust.exe",
"publicThumbprint": "A024A89200469F099EC4A172B4F96F6428AFD41B",
"sourceRepository": "https://git.wilkensxl.de/MrSphay/MrTrust",
"releasePage": "https://git.wilkensxl.de/MrSphay/MrTrust/releases"
},
"agentResponsibilities": [ "agentResponsibilities": [
"Read manifest.json before copying files.", "Read manifest.json before copying files.",
"Use copyMap target paths unless the repository already has an equivalent convention.", "Use copyMap target paths unless the repository already has an equivalent convention.",

View File

@@ -13,6 +13,34 @@
"description": { "description": {
"type": "string" "type": "string"
}, },
"mrtrustIntegration": {
"type": "object",
"required": ["contract", "agentRunbook", "prompt", "standaloneExecutable", "publicThumbprint"],
"properties": {
"contract": {
"type": "string"
},
"agentRunbook": {
"type": "string"
},
"prompt": {
"type": "string"
},
"standaloneExecutable": {
"type": "string"
},
"publicThumbprint": {
"type": "string"
},
"sourceRepository": {
"type": "string"
},
"releasePage": {
"type": "string"
}
},
"additionalProperties": true
},
"agentResponsibilities": { "agentResponsibilities": {
"type": "array", "type": "array",
"items": { "items": {

86
mrtrust.integration.json Normal file
View File

@@ -0,0 +1,86 @@
{
"schemaVersion": 1,
"name": "MrTrust",
"purpose": "Add explicit MrSphay trust onboarding and signing support to Windows target projects.",
"sourceRepository": "https://git.wilkensxl.de/MrSphay/MrTrust",
"releasePage": "https://git.wilkensxl.de/MrSphay/MrTrust/releases",
"userFacingReleaseArtifact": {
"fileName": "MrTrust.exe",
"releaseZipNamePattern": "MrTrust-<version>.zip",
"recommendedBundledPath": "MrTrust\\MrTrust.exe",
"distribution": "Bundle this file directly or link to the MrTrust release ZIP.",
"launchCommand": ".\\MrTrust\\MrTrust.exe",
"removeTrustInstruction": "Open MrTrust and choose Remove trust."
},
"certificate": {
"publisher": "MrSphay",
"publicThumbprint": "A024A89200469F099EC4A172B4F96F6428AFD41B",
"defaultTrustScope": "CurrentUser",
"allUsersTrustScope": "LocalMachine",
"privateMaterialPolicy": "Never commit .pfx files, private keys, passwords, tokens, or signing secrets."
},
"signing": {
"supportedExtensions": [
".exe",
".msi",
".dll",
".ps1",
".psm1",
".psd1",
".cat"
],
"ubuntuRunner": {
"supportedExtensions": [
".exe",
".msi",
".dll",
".cat"
],
"requiredTool": "osslsigncode",
"helperScript": "scripts/Sign-MrTrustProjectLinux.sh",
"requiredSecrets": [
"MRTRUST_CODESIGN_PFX_BASE64",
"MRTRUST_CODESIGN_PFX_PASSWORD"
],
"preferredCommand": "bash ./MrTrust/scripts/Sign-MrTrustProjectLinux.sh <artifact-path>"
},
"windowsRunner": {
"supportedExtensions": [
".exe",
".msi",
".dll",
".ps1",
".psm1",
".psd1",
".cat"
],
"preferredCommand": ".\\MrTrust\\MrTrust.exe sign -Path <artifact-path> -CertificateThumbprint A024A89200469F099EC4A172B4F96F6428AFD41B"
},
"preferredCommand": "bash ./MrTrust/scripts/Sign-MrTrustProjectLinux.sh <artifact-path>",
"ciGuidance": "On ubuntu-latest runners, sign PE/MSI/CAT artifacts with osslsigncode using a PFX stored in Gitea secrets. Use Windows runners only when signing PowerShell scripts or when Windows certificate store signing is required.",
"unsignedBehavior": "Unsigned builds should remain unsigned. MrTrust only makes correctly signed MrSphay artifacts validate after the user has installed trust."
},
"targetProjectAgentContract": {
"modifyTargetProject": true,
"requiredOutcomes": [
"Expose a visible optional Open MrTrust or trust setup path.",
"Link to or bundle the standalone MrTrust.exe.",
"Sign Windows release artifacts when the target project produces supported artifact types.",
"Document how users install and remove MrTrust trust.",
"Verify no private signing material is present in the target repository or release artifacts."
],
"forbiddenOutcomes": [
"Do not silently install certificates.",
"Do not bypass Defender, SmartScreen, UAC, firewall, or Windows security policy.",
"Do not claim MrTrust makes unsigned software trusted.",
"Do not commit private signing material."
],
"fallbackWhenNoInstallerExists": [
"Add release documentation that links to or bundles MrTrust.exe.",
"Add a release signing step for supported Windows artifacts.",
"Add verification notes explaining that the app starts normally after the user installs MrTrust and the artifact signature validates."
]
},
"agentRunbook": "docs/agent-target-integration.md",
"prompt": "docs/integration-prompt.md"
}

View File

@@ -16,6 +16,22 @@ $sourcePath = Join-Path $root "src\MrTrustLauncher.cs"
$iconPath = Join-Path $root "assets\MrTrust.ico" $iconPath = Join-Path $root "assets\MrTrust.ico"
$resolvedOutputPath = Resolve-FullPath $OutputPath $resolvedOutputPath = Resolve-FullPath $OutputPath
$outputDirectory = Split-Path -Parent $resolvedOutputPath $outputDirectory = Split-Path -Parent $resolvedOutputPath
$payloadFiles = @(
@{ Path = "MrTrust.ps1"; ResourceName = "MrTrust.Payload.MrTrust.ps1" },
@{ Path = "scripts\Build-MrTrustExe.ps1"; ResourceName = "MrTrust.Payload.scripts.Build-MrTrustExe.ps1" },
@{ Path = "scripts\Install-MrTrust.ps1"; ResourceName = "MrTrust.Payload.scripts.Install-MrTrust.ps1" },
@{ Path = "scripts\New-MrTrustCertificate.ps1"; ResourceName = "MrTrust.Payload.scripts.New-MrTrustCertificate.ps1" },
@{ Path = "scripts\New-MrTrustIcon.ps1"; ResourceName = "MrTrust.Payload.scripts.New-MrTrustIcon.ps1" },
@{ Path = "scripts\New-MrTrustRelease.ps1"; ResourceName = "MrTrust.Payload.scripts.New-MrTrustRelease.ps1" },
@{ Path = "scripts\Sign-MrTrustProject.ps1"; ResourceName = "MrTrust.Payload.scripts.Sign-MrTrustProject.ps1" },
@{ Path = "scripts\Sign-MrTrustProjectLinux.sh"; ResourceName = "MrTrust.Payload.scripts.Sign-MrTrustProjectLinux.sh" },
@{ Path = "scripts\Start-MrTrustGui.ps1"; ResourceName = "MrTrust.Payload.scripts.Start-MrTrustGui.ps1" },
@{ Path = "scripts\Uninstall-MrTrust.ps1"; ResourceName = "MrTrust.Payload.scripts.Uninstall-MrTrust.ps1" },
@{ Path = "assets\MrTrust.ico"; ResourceName = "MrTrust.Payload.assets.MrTrust.ico" },
@{ Path = "assets\certificates\MrSphay-LocalTrust-Root.cer"; ResourceName = "MrTrust.Payload.assets.certificates.MrSphay-LocalTrust-Root.cer" },
@{ Path = "assets\certificates\MrSphay-CodeSigning.cer"; ResourceName = "MrTrust.Payload.assets.certificates.MrSphay-CodeSigning.cer" },
@{ Path = "assets\certificates\thumbprints.txt"; ResourceName = "MrTrust.Payload.assets.certificates.thumbprints.txt" }
)
if (-not (Test-Path -LiteralPath $sourcePath)) { if (-not (Test-Path -LiteralPath $sourcePath)) {
throw "Launcher source not found: $sourcePath" throw "Launcher source not found: $sourcePath"
@@ -25,6 +41,13 @@ if (-not (Test-Path -LiteralPath $iconPath)) {
& (Join-Path $root "scripts\New-MrTrustIcon.ps1") -OutputPath $iconPath & (Join-Path $root "scripts\New-MrTrustIcon.ps1") -OutputPath $iconPath
} }
foreach ($payloadFile in $payloadFiles) {
$payloadPath = Join-Path $root $payloadFile.Path
if (-not (Test-Path -LiteralPath $payloadPath)) {
throw "Payload file not found: $payloadPath"
}
}
New-Item -ItemType Directory -Force -Path $outputDirectory | Out-Null New-Item -ItemType Directory -Force -Path $outputDirectory | Out-Null
$compilerCandidates = @( $compilerCandidates = @(
@@ -37,20 +60,29 @@ if (-not $compiler) {
throw "csc.exe was not found. Run this build on a Windows Gitea runner with .NET Framework installed." throw "csc.exe was not found. Run this build on a Windows Gitea runner with .NET Framework installed."
} }
& $compiler ` $compilerArguments = @(
/nologo ` "/nologo",
/target:winexe ` "/target:winexe",
/optimize+ ` "/optimize+",
/platform:anycpu ` "/platform:anycpu",
/out:$resolvedOutputPath ` "/out:$resolvedOutputPath",
/win32icon:$iconPath ` "/win32icon:$iconPath",
/reference:System.Windows.Forms.dll ` "/reference:System.Windows.Forms.dll",
/reference:System.Drawing.dll ` "/reference:System.Drawing.dll"
$sourcePath )
foreach ($payloadFile in $payloadFiles) {
$payloadPath = Join-Path $root $payloadFile.Path
$compilerArguments += "/resource:$payloadPath,$($payloadFile.ResourceName)"
}
$compilerArguments += $sourcePath
& $compiler @compilerArguments
if ($LASTEXITCODE -ne 0) { if ($LASTEXITCODE -ne 0) {
throw "csc.exe failed with exit code $LASTEXITCODE." throw "csc.exe failed with exit code $LASTEXITCODE."
} }
Write-Host "Created EXE:" Write-Host "Created standalone EXE:"
Write-Host " $resolvedOutputPath" Write-Host " $resolvedOutputPath"

View File

@@ -27,9 +27,6 @@ if (Test-Path -LiteralPath $packageRoot) {
} }
New-Item -ItemType Directory -Force -Path $packageRoot | Out-Null New-Item -ItemType Directory -Force -Path $packageRoot | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $packageRoot "scripts") | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $packageRoot "assets\certificates") | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $packageRoot "docs") | Out-Null
if (-not (Test-Path -LiteralPath $iconPath)) { if (-not (Test-Path -LiteralPath $iconPath)) {
& (Join-Path $root "scripts\New-MrTrustIcon.ps1") -OutputPath $iconPath & (Join-Path $root "scripts\New-MrTrustIcon.ps1") -OutputPath $iconPath
@@ -55,16 +52,7 @@ if ($SigningThumbprint) {
} }
Copy-Item -LiteralPath $exePath -Destination $packageRoot Copy-Item -LiteralPath $exePath -Destination $packageRoot
Copy-Item -LiteralPath (Join-Path $root "MrTrust.ps1") -Destination $packageRoot
Copy-Item -LiteralPath (Join-Path $root "README.md") -Destination $packageRoot Copy-Item -LiteralPath (Join-Path $root "README.md") -Destination $packageRoot
Copy-Item -LiteralPath $iconPath -Destination (Join-Path $packageRoot "assets")
Copy-Item -LiteralPath (Join-Path $root "scripts\Install-MrTrust.ps1") -Destination (Join-Path $packageRoot "scripts")
Copy-Item -LiteralPath (Join-Path $root "scripts\Uninstall-MrTrust.ps1") -Destination (Join-Path $packageRoot "scripts")
Copy-Item -LiteralPath (Join-Path $root "scripts\Start-MrTrustGui.ps1") -Destination (Join-Path $packageRoot "scripts")
Copy-Item -LiteralPath (Join-Path $root "assets\certificates\MrSphay-LocalTrust-Root.cer") -Destination (Join-Path $packageRoot "assets\certificates")
Copy-Item -LiteralPath (Join-Path $root "assets\certificates\MrSphay-CodeSigning.cer") -Destination (Join-Path $packageRoot "assets\certificates")
Copy-Item -LiteralPath (Join-Path $root "assets\certificates\thumbprints.txt") -Destination (Join-Path $packageRoot "assets\certificates")
Copy-Item -LiteralPath (Join-Path $root "docs\security-model.md") -Destination (Join-Path $packageRoot "docs")
if (Test-Path -LiteralPath $zipPath) { if (Test-Path -LiteralPath $zipPath) {
Remove-Item -LiteralPath $zipPath -Force Remove-Item -LiteralPath $zipPath -Force

View File

@@ -0,0 +1,71 @@
#!/usr/bin/env bash
set -euo pipefail
if [ "$#" -lt 1 ]; then
echo "Usage: Sign-MrTrustProjectLinux.sh <artifact> [artifact...]" >&2
exit 2
fi
if [ -z "${MRTRUST_CODESIGN_PFX_BASE64:-}" ]; then
echo "MRTRUST_CODESIGN_PFX_BASE64 is required." >&2
exit 2
fi
if [ -z "${MRTRUST_CODESIGN_PFX_PASSWORD:-}" ]; then
echo "MRTRUST_CODESIGN_PFX_PASSWORD is required." >&2
exit 2
fi
if ! command -v osslsigncode >/dev/null 2>&1; then
if command -v apt-get >/dev/null 2>&1; then
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y osslsigncode
else
echo "osslsigncode is not installed and apt-get is unavailable." >&2
exit 2
fi
fi
work_dir="$(mktemp -d)"
trap 'rm -rf "$work_dir"' EXIT
pfx_path="$work_dir/mrtrust-codesign.pfx"
printf '%s' "$MRTRUST_CODESIGN_PFX_BASE64" | base64 -d > "$pfx_path"
timestamp_url="${MRTRUST_TIMESTAMP_URL:-http://timestamp.digicert.com}"
for artifact in "$@"; do
if [ ! -f "$artifact" ]; then
echo "Artifact not found: $artifact" >&2
exit 2
fi
case "${artifact##*.}" in
exe|EXE|msi|MSI|dll|DLL|cat|CAT)
;;
*)
echo "Unsupported artifact for osslsigncode: $artifact" >&2
exit 2
;;
esac
signed_path="$work_dir/$(basename "$artifact").signed"
args=(
sign
-pkcs12 "$pfx_path"
-pass "$MRTRUST_CODESIGN_PFX_PASSWORD"
-n "MrSphay"
-i "https://git.wilkensxl.de/MrSphay"
-in "$artifact"
-out "$signed_path"
)
if [ -n "$timestamp_url" ]; then
args+=( -t "$timestamp_url" )
fi
osslsigncode "${args[@]}"
mv "$signed_path" "$artifact"
echo "Signed $artifact"
done

View File

@@ -10,6 +10,25 @@ $script:RootPath = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyComman
$script:RootCertificatePath = Join-Path $script:RootPath "assets\certificates\MrSphay-LocalTrust-Root.cer" $script:RootCertificatePath = Join-Path $script:RootPath "assets\certificates\MrSphay-LocalTrust-Root.cer"
$script:PublisherCertificatePath = Join-Path $script:RootPath "assets\certificates\MrSphay-CodeSigning.cer" $script:PublisherCertificatePath = Join-Path $script:RootPath "assets\certificates\MrSphay-CodeSigning.cer"
$script:IconPath = Join-Path $script:RootPath "assets\MrTrust.ico" $script:IconPath = Join-Path $script:RootPath "assets\MrTrust.ico"
$script:SelectedFilePath = $null
$script:CurrentAccent = 0
$colors = @{
Background = [Drawing.Color]::FromArgb(15, 20, 20)
Shell = [Drawing.Color]::FromArgb(20, 27, 27)
Panel = [Drawing.Color]::FromArgb(27, 36, 35)
PanelAlt = [Drawing.Color]::FromArgb(34, 45, 43)
PanelSelected = [Drawing.Color]::FromArgb(31, 64, 49)
Border = [Drawing.Color]::FromArgb(53, 66, 64)
Text = [Drawing.Color]::FromArgb(239, 244, 241)
Muted = [Drawing.Color]::FromArgb(157, 172, 166)
Green = [Drawing.Color]::FromArgb(0, 175, 91)
GreenHover = [Drawing.Color]::FromArgb(0, 199, 104)
GreenSoft = [Drawing.Color]::FromArgb(34, 76, 52)
Orange = [Drawing.Color]::FromArgb(242, 153, 74)
Red = [Drawing.Color]::FromArgb(235, 87, 87)
Blue = [Drawing.Color]::FromArgb(82, 166, 255)
}
function Test-IsAdministrator { function Test-IsAdministrator {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent() $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
@@ -28,12 +47,7 @@ function Get-MrTrustCertificate {
} }
function Get-TrustScope { function Get-TrustScope {
if ($script:AllUsersCheckBox.Checked) { if ($script:AllUsersCheckBox.Checked) { "LocalMachine" } else { "CurrentUser" }
"LocalMachine"
}
else {
"CurrentUser"
}
} }
function Get-StorePath { function Get-StorePath {
@@ -56,14 +70,153 @@ function Test-CertificateInstalled {
@(Get-ChildItem -Path $storePath | Where-Object Thumbprint -eq $Certificate.Thumbprint).Count -gt 0 @(Get-ChildItem -Path $storePath | Where-Object Thumbprint -eq $Certificate.Thumbprint).Count -gt 0
} }
function Set-StatusText { function Set-Busy {
param([Parameter(Mandatory)][string]$Text) param([bool]$Busy)
$script:StatusLabel.Text = $Text $script:ProgressBar.Visible = $Busy
if ($Busy) {
$script:ProgressBar.Style = "Marquee"
}
} }
function Refresh-MrTrustStatus { function Set-StatusText {
param(
[Parameter(Mandatory)][string]$Text,
[Parameter(Mandatory)][Drawing.Color]$Color
)
$script:StatusLabel.Text = $Text
$script:StatusPill.BackColor = $Color
}
function Add-AnimatedButton {
param(
[Parameter(Mandatory)][Windows.Forms.Button]$Button,
[Parameter(Mandatory)][Drawing.Color]$Normal,
[Parameter(Mandatory)][Drawing.Color]$Hover
)
$normalColor = $Normal
$hoverColor = $Hover
$textColor = $colors.Text
$borderColor = $colors.Border
$Button.FlatStyle = "Flat"
$Button.FlatAppearance.BorderColor = $borderColor
$Button.FlatAppearance.BorderSize = 1
$Button.BackColor = $normalColor
$Button.ForeColor = $textColor
$Button.Cursor = [Windows.Forms.Cursors]::Hand
$Button.Add_MouseEnter({ param($sender, $eventArgs) $sender.BackColor = $hoverColor }.GetNewClosure())
$Button.Add_MouseLeave({ param($sender, $eventArgs) $sender.BackColor = $normalColor }.GetNewClosure())
}
function New-Label {
param(
[string]$Text,
[int]$X,
[int]$Y,
[int]$Width = 220,
[int]$Height = 24,
[Drawing.Color]$Color = $colors.Muted,
[Drawing.Font]$Font = $null
)
$label = [Windows.Forms.Label]::new()
$label.Text = $Text
$label.Location = [Drawing.Point]::new($X, $Y)
$label.Size = [Drawing.Size]::new($Width, $Height)
$label.ForeColor = $Color
if ($Font) { $label.Font = $Font }
$label
}
function New-Card {
param(
[int]$X,
[int]$Y,
[int]$Width,
[int]$Height,
[Drawing.Color]$BackColor = $colors.Panel
)
$panel = [Windows.Forms.Panel]::new()
$panel.Location = [Drawing.Point]::new($X, $Y)
$panel.Size = [Drawing.Size]::new($Width, $Height)
$panel.BackColor = $BackColor
$panel.BorderStyle = "FixedSingle"
$panel.Anchor = "Top,Left,Right"
$panel
}
function New-PageTitle {
param(
[string]$Title,
[string]$Description
)
$titleLabel = New-Label -Text $Title -X 0 -Y 0 -Width 760 -Height 34 -Color $colors.Text -Font ([Drawing.Font]::new("Segoe UI", 15, [Drawing.FontStyle]::Bold))
$descriptionLabel = New-Label -Text $Description -X 1 -Y 38 -Width 850 -Height 24 -Color $colors.Muted
[pscustomobject]@{
Title = $titleLabel
Description = $descriptionLabel
}
}
function New-NavButton {
param(
[Parameter(Mandatory)][string]$Key,
[Parameter(Mandatory)][string]$Text,
[Parameter(Mandatory)][int]$Y
)
$button = [Windows.Forms.Button]::new()
$button.Text = $Text
$button.Tag = $Key
$button.Size = [Drawing.Size]::new(174, 44)
$button.Location = [Drawing.Point]::new(22, $Y)
$button.TextAlign = "MiddleLeft"
$button.Padding = [Windows.Forms.Padding]::new(14, 0, 0, 0)
$button.Font = [Drawing.Font]::new("Segoe UI", 10, [Drawing.FontStyle]::Bold)
$button.FlatStyle = "Flat"
$button.FlatAppearance.BorderSize = 1
$button.FlatAppearance.BorderColor = $colors.Shell
$button.BackColor = $colors.Shell
$button.ForeColor = $colors.Muted
$button.Cursor = [Windows.Forms.Cursors]::Hand
$button.Add_MouseEnter({ param($sender, $eventArgs) if ($script:ActivePage -ne $sender.Tag) { $sender.BackColor = $colors.PanelAlt } }.GetNewClosure())
$button.Add_MouseLeave({ param($sender, $eventArgs) if ($script:ActivePage -ne $sender.Tag) { $sender.BackColor = $colors.Shell } }.GetNewClosure())
$button.Add_Click({ param($sender, $eventArgs) Show-MrTrustPage -Key $sender.Tag })
$button
}
function Show-MrTrustPage {
param([Parameter(Mandatory)][string]$Key)
$script:ActivePage = $Key
foreach ($pageKey in $script:Pages.Keys) {
$script:Pages[$pageKey].Visible = $pageKey -eq $Key
}
foreach ($buttonKey in $script:NavButtons.Keys) {
$button = $script:NavButtons[$buttonKey]
if ($buttonKey -eq $Key) {
$button.BackColor = $colors.PanelSelected
$button.FlatAppearance.BorderColor = $colors.Green
$button.ForeColor = $colors.Text
}
else {
$button.BackColor = $colors.Shell
$button.FlatAppearance.BorderColor = $colors.Shell
$button.ForeColor = $colors.Muted
}
}
}
function Update-TrustStatus {
try { try {
Set-Busy $true
$rootCertificate = Get-MrTrustCertificate -Path $script:RootCertificatePath $rootCertificate = Get-MrTrustCertificate -Path $script:RootCertificatePath
$publisherCertificate = Get-MrTrustCertificate -Path $script:PublisherCertificatePath $publisherCertificate = Get-MrTrustCertificate -Path $script:PublisherCertificatePath
$scope = Get-TrustScope $scope = Get-TrustScope
@@ -74,79 +227,58 @@ function Refresh-MrTrustStatus {
$script:RootThumbprintLabel.Text = $rootCertificate.Thumbprint $script:RootThumbprintLabel.Text = $rootCertificate.Thumbprint
$script:PublisherThumbprintLabel.Text = $publisherCertificate.Thumbprint $script:PublisherThumbprintLabel.Text = $publisherCertificate.Thumbprint
$script:ExpiryLabel.Text = $rootCertificate.NotAfter.ToString("yyyy-MM-dd") $script:ExpiryLabel.Text = $rootCertificate.NotAfter.ToString("yyyy-MM-dd")
$script:ScopeValueLabel.Text = $scope
if ($rootInstalled -and $publisherInstalled) { if ($rootInstalled -and $publisherInstalled) {
Set-StatusText "Trusted" Set-StatusText -Text "Trusted" -Color $colors.Green
$script:StatusPill.BackColor = [Drawing.Color]::FromArgb(28, 185, 111) $script:TrustSummaryLabel.Text = "MrSphay public trust is installed for $scope."
} }
else { else {
Set-StatusText "Not installed" Set-StatusText -Text "Not installed" -Color $colors.Orange
$script:StatusPill.BackColor = [Drawing.Color]::FromArgb(242, 153, 74) $script:TrustSummaryLabel.Text = "Trust is not fully installed for $scope."
} }
} }
catch { catch {
Set-StatusText $_.Exception.Message Set-StatusText -Text "Error" -Color $colors.Red
$script:StatusPill.BackColor = [Drawing.Color]::FromArgb(235, 87, 87) $script:TrustSummaryLabel.Text = $_.Exception.Message
}
finally {
Set-Busy $false
} }
} }
function Install-MrTrustCertificates { function Install-MrTrustCertificates {
$scope = Get-TrustScope $scope = Get-TrustScope
if ($scope -eq "LocalMachine" -and -not (Test-IsAdministrator)) { if ($scope -eq "LocalMachine" -and -not (Test-IsAdministrator)) {
[Windows.Forms.MessageBox]::Show( [Windows.Forms.MessageBox]::Show("All-users trust requires running MrTrust as Administrator.", "MrTrust", "OK", "Warning") | Out-Null
"All-users trust requires running PowerShell as Administrator.",
"MrTrust",
[Windows.Forms.MessageBoxButtons]::OK,
[Windows.Forms.MessageBoxIcon]::Warning
) | Out-Null
return return
} }
$rootCertificate = Get-MrTrustCertificate -Path $script:RootCertificatePath $rootCertificate = Get-MrTrustCertificate -Path $script:RootCertificatePath
$publisherCertificate = Get-MrTrustCertificate -Path $script:PublisherCertificatePath $publisherCertificate = Get-MrTrustCertificate -Path $script:PublisherCertificatePath
$message = "Install MrSphay trust for $scope?`r`n`r`nRoot:`r`n$($rootCertificate.Thumbprint)`r`n`r`nPublisher:`r`n$($publisherCertificate.Thumbprint)`r`n`r`nThis does not disable Defender or SmartScreen."
$result = [Windows.Forms.MessageBox]::Show($message, "Install MrTrust", "YesNo", "Warning")
if ($result -ne [Windows.Forms.DialogResult]::Yes) { return }
$message = "Install MrSphay trust for $scope?`r`n`r`nRoot:`r`n$($rootCertificate.Thumbprint)`r`n`r`nPublisher:`r`n$($publisherCertificate.Thumbprint)`r`n`r`nOnly continue if you trust software signed by MrSphay." Set-Busy $true
$result = [Windows.Forms.MessageBox]::Show(
$message,
"Install MrTrust",
[Windows.Forms.MessageBoxButtons]::YesNo,
[Windows.Forms.MessageBoxIcon]::Warning
)
if ($result -ne [Windows.Forms.DialogResult]::Yes) {
return
}
Import-Certificate -FilePath $script:RootCertificatePath -CertStoreLocation (Get-StorePath -Scope $scope -Store "Root") | Out-Null Import-Certificate -FilePath $script:RootCertificatePath -CertStoreLocation (Get-StorePath -Scope $scope -Store "Root") | Out-Null
Import-Certificate -FilePath $script:PublisherCertificatePath -CertStoreLocation (Get-StorePath -Scope $scope -Store "TrustedPublisher") | Out-Null Import-Certificate -FilePath $script:PublisherCertificatePath -CertStoreLocation (Get-StorePath -Scope $scope -Store "TrustedPublisher") | Out-Null
Refresh-MrTrustStatus Update-TrustStatus
} }
function Remove-MrTrustCertificates { function Remove-MrTrustCertificates {
$scope = Get-TrustScope $scope = Get-TrustScope
if ($scope -eq "LocalMachine" -and -not (Test-IsAdministrator)) { if ($scope -eq "LocalMachine" -and -not (Test-IsAdministrator)) {
[Windows.Forms.MessageBox]::Show( [Windows.Forms.MessageBox]::Show("All-users removal requires running MrTrust as Administrator.", "MrTrust", "OK", "Warning") | Out-Null
"All-users removal requires running PowerShell as Administrator.",
"MrTrust",
[Windows.Forms.MessageBoxButtons]::OK,
[Windows.Forms.MessageBoxIcon]::Warning
) | Out-Null
return return
} }
$result = [Windows.Forms.MessageBox]::Show("Remove MrSphay trust for $scope?", "Remove MrTrust", "YesNo", "Question")
if ($result -ne [Windows.Forms.DialogResult]::Yes) { return }
Set-Busy $true
$rootCertificate = Get-MrTrustCertificate -Path $script:RootCertificatePath $rootCertificate = Get-MrTrustCertificate -Path $script:RootCertificatePath
$publisherCertificate = Get-MrTrustCertificate -Path $script:PublisherCertificatePath $publisherCertificate = Get-MrTrustCertificate -Path $script:PublisherCertificatePath
$result = [Windows.Forms.MessageBox]::Show(
"Remove MrSphay trust for $scope?",
"Remove MrTrust",
[Windows.Forms.MessageBoxButtons]::YesNo,
[Windows.Forms.MessageBoxIcon]::Question
)
if ($result -ne [Windows.Forms.DialogResult]::Yes) {
return
}
$targets = @( $targets = @(
[pscustomobject]@{ Store = "Root"; Thumbprint = $rootCertificate.Thumbprint }, [pscustomobject]@{ Store = "Root"; Thumbprint = $rootCertificate.Thumbprint },
[pscustomobject]@{ Store = "TrustedPublisher"; Thumbprint = $publisherCertificate.Thumbprint } [pscustomobject]@{ Store = "TrustedPublisher"; Thumbprint = $publisherCertificate.Thumbprint }
@@ -159,7 +291,86 @@ function Remove-MrTrustCertificates {
Remove-Item Remove-Item
} }
Refresh-MrTrustStatus Update-TrustStatus
}
function Test-MarkOfTheWeb {
param([Parameter(Mandatory)][string]$Path)
try {
$stream = Get-Content -LiteralPath $Path -Stream Zone.Identifier -ErrorAction Stop
($stream -join "`n") -match "ZoneId\s*=\s*[3-4]"
}
catch {
$false
}
}
function Get-SmartScreenExplanation {
param(
[bool]$SignedByMrSphay,
[bool]$HasMotw
)
if ($SignedByMrSphay -and $HasMotw) {
"Publisher trust can be valid while SmartScreen still warns because the downloaded file has Internet origin and low Microsoft reputation."
}
elseif ($SignedByMrSphay) {
"The publisher matches MrSphay. SmartScreen may still warn until Microsoft reputation builds for this exact app and publisher."
}
elseif ($HasMotw) {
"This file came from the Internet and is not signed by MrSphay. SmartScreen warnings are expected."
}
else {
"MrTrust can only help with MrSphay-signed files. SmartScreen reputation is separate from local certificate trust."
}
}
function Test-SelectedFile {
param([Parameter(Mandatory)][string]$Path)
Set-Busy $true
try {
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
throw "File not found: $Path"
}
$signature = Get-AuthenticodeSignature -LiteralPath $Path
$hasMotw = Test-MarkOfTheWeb -Path $Path
$signer = $signature.SignerCertificate
$signedByMrSphay = $false
if ($signer) {
$signedByMrSphay = $signer.Thumbprint -eq "A024A89200469F099EC4A172B4F96F6428AFD41B" -or $signer.Subject -like "*MrSphay*"
}
$script:FileNameLabel.Text = [IO.Path]::GetFileName($Path)
$script:FilePathLabel.Text = $Path
$script:SignatureStatusLabel.Text = "$($signature.Status)"
$script:SignerLabel.Text = if ($signer) { $signer.Subject } else { "No signer certificate" }
$script:MrSphayMatchLabel.Text = if ($signedByMrSphay) { "Yes" } else { "No" }
$script:MotwLabel.Text = if ($hasMotw) { "Yes" } else { "No" }
$script:SmartScreenLabel.Text = Get-SmartScreenExplanation -SignedByMrSphay $signedByMrSphay -HasMotw $hasMotw
if ($signedByMrSphay -and $signature.Status -eq "Valid") {
$script:FileVerdictLabel.Text = "Looks good: signed by MrSphay and locally valid."
$script:FileVerdictLabel.ForeColor = $colors.Green
}
elseif ($signedByMrSphay) {
$script:FileVerdictLabel.Text = "Signed by MrSphay, but local validation is not fully valid: $($signature.Status)"
$script:FileVerdictLabel.ForeColor = $colors.Orange
}
else {
$script:FileVerdictLabel.Text = "Not a MrSphay-signed file."
$script:FileVerdictLabel.ForeColor = $colors.Red
}
}
catch {
$script:FileVerdictLabel.Text = $_.Exception.Message
$script:FileVerdictLabel.ForeColor = $colors.Red
}
finally {
Set-Busy $false
}
} }
[Windows.Forms.Application]::EnableVisualStyles() [Windows.Forms.Application]::EnableVisualStyles()
@@ -167,9 +378,9 @@ function Remove-MrTrustCertificates {
$form = [Windows.Forms.Form]::new() $form = [Windows.Forms.Form]::new()
$form.Text = "MrTrust" $form.Text = "MrTrust"
$form.StartPosition = "CenterScreen" $form.StartPosition = "CenterScreen"
$form.ClientSize = [Drawing.Size]::new(900, 560) $form.ClientSize = [Drawing.Size]::new(1120, 720)
$form.MinimumSize = [Drawing.Size]::new(860, 540) $form.MinimumSize = [Drawing.Size]::new(1040, 680)
$form.BackColor = [Drawing.Color]::FromArgb(22, 26, 29) $form.BackColor = $colors.Background
$form.Font = [Drawing.Font]::new("Segoe UI", 10) $form.Font = [Drawing.Font]::new("Segoe UI", 10)
if (Test-Path -LiteralPath $script:IconPath) { if (Test-Path -LiteralPath $script:IconPath) {
$form.Icon = [Drawing.Icon]::new($script:IconPath) $form.Icon = [Drawing.Icon]::new($script:IconPath)
@@ -177,170 +388,285 @@ if (Test-Path -LiteralPath $script:IconPath) {
$header = [Windows.Forms.Panel]::new() $header = [Windows.Forms.Panel]::new()
$header.Dock = "Top" $header.Dock = "Top"
$header.Height = 124 $header.Height = 96
$header.BackColor = [Drawing.Color]::FromArgb(27, 32, 35) $header.BackColor = $colors.Background
$form.Controls.Add($header) $form.Controls.Add($header)
$accent = [Windows.Forms.Panel]::new() $brandLine = [Windows.Forms.Panel]::new()
$accent.Dock = "Left" $brandLine.Dock = "Bottom"
$accent.Width = 8 $brandLine.Height = 1
$accent.BackColor = [Drawing.Color]::FromArgb(28, 185, 111) $brandLine.BackColor = $colors.Border
$header.Controls.Add($accent) $header.Controls.Add($brandLine)
$logoBox = [Windows.Forms.PictureBox]::new() $logoBox = [Windows.Forms.PictureBox]::new()
$logoBox.Size = [Drawing.Size]::new(44, 44) $logoBox.Size = [Drawing.Size]::new(42, 42)
$logoBox.Location = [Drawing.Point]::new(34, 30) $logoBox.Location = [Drawing.Point]::new(28, 26)
$logoBox.SizeMode = "StretchImage" $logoBox.SizeMode = "StretchImage"
if (Test-Path -LiteralPath $script:IconPath) { if (Test-Path -LiteralPath $script:IconPath) {
$logoBox.Image = [Drawing.Icon]::new($script:IconPath).ToBitmap() $logoBox.Image = [Drawing.Icon]::new($script:IconPath).ToBitmap()
} }
$header.Controls.Add($logoBox) $header.Controls.Add($logoBox)
$title = [Windows.Forms.Label]::new() $title = New-Label -Text "MrTrust" -X 86 -Y 20 -Width 260 -Height 36 -Color $colors.Text -Font ([Drawing.Font]::new("Segoe UI", 18, [Drawing.FontStyle]::Bold))
$title.Text = "MrTrust"
$title.ForeColor = [Drawing.Color]::White
$title.Font = [Drawing.Font]::new("Segoe UI", 24, [Drawing.FontStyle]::Bold)
$title.AutoSize = $true
$title.Location = [Drawing.Point]::new(92, 24)
$header.Controls.Add($title) $header.Controls.Add($title)
$subtitle = [Windows.Forms.Label]::new() $subtitle = New-Label -Text "Local certificate trust for MrSphay signed Windows apps" -X 88 -Y 56 -Width 520 -Height 24 -Color $colors.Muted
$subtitle.Text = "Trust setup for MrSphay signed Windows apps"
$subtitle.ForeColor = [Drawing.Color]::FromArgb(177, 190, 183)
$subtitle.AutoSize = $true
$subtitle.Location = [Drawing.Point]::new(96, 74)
$header.Controls.Add($subtitle) $header.Controls.Add($subtitle)
$statusText = [Windows.Forms.Label]::new() $statusCard = [Windows.Forms.Panel]::new()
$statusText.Text = "Status" $statusCard.Anchor = "Top,Right"
$statusText.ForeColor = [Drawing.Color]::FromArgb(177, 190, 183) $statusCard.Location = [Drawing.Point]::new(780, 20)
$statusText.AutoSize = $true $statusCard.Size = [Drawing.Size]::new(300, 56)
$statusText.Location = [Drawing.Point]::new(646, 32) $statusCard.BackColor = $colors.Panel
$header.Controls.Add($statusText) $statusCard.BorderStyle = "FixedSingle"
$header.Controls.Add($statusCard)
$statusText = New-Label -Text "Trust status" -X 18 -Y 7 -Width 130 -Height 20 -Color $colors.Muted -Font ([Drawing.Font]::new("Segoe UI", 8.5))
$statusCard.Controls.Add($statusText)
$script:StatusPill = [Windows.Forms.Panel]::new() $script:StatusPill = [Windows.Forms.Panel]::new()
$script:StatusPill.Size = [Drawing.Size]::new(16, 16) $script:StatusPill.Size = [Drawing.Size]::new(14, 14)
$script:StatusPill.Location = [Drawing.Point]::new(646, 62) $script:StatusPill.Location = [Drawing.Point]::new(18, 31)
$script:StatusPill.BackColor = [Drawing.Color]::FromArgb(242, 153, 74) $script:StatusPill.BackColor = $colors.Orange
$header.Controls.Add($script:StatusPill) $statusCard.Controls.Add($script:StatusPill)
$script:StatusLabel = [Windows.Forms.Label]::new() $script:StatusLabel = New-Label -Text "Checking..." -X 40 -Y 26 -Width 210 -Height 24 -Color $colors.Text -Font ([Drawing.Font]::new("Segoe UI", 10, [Drawing.FontStyle]::Bold))
$script:StatusLabel.Text = "Checking..."
$script:StatusLabel.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227)
$script:StatusLabel.AutoSize = $false
$script:StatusLabel.AutoEllipsis = $true $script:StatusLabel.AutoEllipsis = $true
$script:StatusLabel.Location = [Drawing.Point]::new(674, 57) $statusCard.Controls.Add($script:StatusLabel)
$script:StatusLabel.Size = [Drawing.Size]::new(190, 28)
$header.Controls.Add($script:StatusLabel)
$content = [Windows.Forms.Panel]::new() $script:ProgressBar = [Windows.Forms.ProgressBar]::new()
$content.Dock = "Fill" $script:ProgressBar.Anchor = "Top,Left,Right"
$content.Padding = [Windows.Forms.Padding]::new(30) $script:ProgressBar.Location = [Drawing.Point]::new(28, 86)
$content.BackColor = [Drawing.Color]::FromArgb(22, 26, 29) $script:ProgressBar.Size = [Drawing.Size]::new(1052, 4)
$form.Controls.Add($content) $script:ProgressBar.Visible = $false
$header.Controls.Add($script:ProgressBar)
$infoPanel = [Windows.Forms.Panel]::new() $shell = [Windows.Forms.Panel]::new()
$infoPanel.BackColor = [Drawing.Color]::FromArgb(31, 37, 40) $shell.Dock = "Fill"
$infoPanel.Size = [Drawing.Size]::new(820, 226) $shell.BackColor = $colors.Background
$infoPanel.Location = [Drawing.Point]::new(40, 34) $form.Controls.Add($shell)
$content.Controls.Add($infoPanel)
$scopeLabel = [Windows.Forms.Label]::new() $sidebar = [Windows.Forms.Panel]::new()
$scopeLabel.Text = "Scope" $sidebar.Dock = "Left"
$scopeLabel.ForeColor = [Drawing.Color]::FromArgb(177, 190, 183) $sidebar.Width = 220
$scopeLabel.Location = [Drawing.Point]::new(24, 24) $sidebar.BackColor = $colors.Shell
$scopeLabel.AutoSize = $true $shell.Controls.Add($sidebar)
$infoPanel.Controls.Add($scopeLabel)
$sideAccent = [Windows.Forms.Panel]::new()
$sideAccent.Dock = "Left"
$sideAccent.Width = 4
$sideAccent.BackColor = $colors.Green
$sidebar.Controls.Add($sideAccent)
$navTitle = New-Label -Text "Navigation" -X 24 -Y 26 -Width 160 -Height 22 -Color $colors.Muted -Font ([Drawing.Font]::new("Segoe UI", 8.5, [Drawing.FontStyle]::Bold))
$sidebar.Controls.Add($navTitle)
$script:NavButtons = @{}
$script:NavButtons.Trust = New-NavButton -Key "Trust" -Text "Trust" -Y 58
$script:NavButtons.Diagnostics = New-NavButton -Key "Diagnostics" -Text "File scan" -Y 110
$script:NavButtons.SmartScreen = New-NavButton -Key "SmartScreen" -Text "SmartScreen" -Y 162
foreach ($navButton in $script:NavButtons.Values) {
$sidebar.Controls.Add($navButton)
}
$sideNote = New-Label -Text "Installs public certificates only. No Defender, SmartScreen, UAC, firewall, or policy bypasses." -X 24 -Y 510 -Width 164 -Height 96 -Color $colors.Muted -Font ([Drawing.Font]::new("Segoe UI", 8.5))
$sideNote.Anchor = "Left,Bottom"
$sidebar.Controls.Add($sideNote)
$contentHost = [Windows.Forms.Panel]::new()
$contentHost.Dock = "Fill"
$contentHost.BackColor = $colors.Background
$contentHost.Padding = [Windows.Forms.Padding]::new(34, 30, 34, 30)
$shell.Controls.Add($contentHost)
$script:Pages = @{}
$trustPage = [Windows.Forms.Panel]::new()
$trustPage.Dock = "Fill"
$trustPage.BackColor = $colors.Background
$contentHost.Controls.Add($trustPage)
$script:Pages.Trust = $trustPage
$trustTitle = New-PageTitle -Title "Trust" -Description "Install, remove, and verify the MrSphay public trust certificates."
$trustPage.Controls.Add($trustTitle.Title)
$trustPage.Controls.Add($trustTitle.Description)
$trustPanel = New-Card -X 0 -Y 82 -Width 820 -Height 236
$trustPage.Controls.Add($trustPanel)
$scopeLabel = New-Label -Text "Scope" -X 24 -Y 22 -Width 160 -Height 22 -Color $colors.Muted -Font ([Drawing.Font]::new("Segoe UI", 8.5, [Drawing.FontStyle]::Bold))
$trustPanel.Controls.Add($scopeLabel)
$script:AllUsersCheckBox = [Windows.Forms.CheckBox]::new() $script:AllUsersCheckBox = [Windows.Forms.CheckBox]::new()
$script:AllUsersCheckBox.Text = "Install for all users (requires Administrator)" $script:AllUsersCheckBox.Text = "All users (LocalMachine, requires Administrator)"
$script:AllUsersCheckBox.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227) $script:AllUsersCheckBox.ForeColor = $colors.Text
$script:AllUsersCheckBox.Location = [Drawing.Point]::new(24, 50) $script:AllUsersCheckBox.Location = [Drawing.Point]::new(24, 46)
$script:AllUsersCheckBox.AutoSize = $true $script:AllUsersCheckBox.AutoSize = $true
$script:AllUsersCheckBox.FlatStyle = "Flat" $script:AllUsersCheckBox.FlatStyle = "Flat"
$script:AllUsersCheckBox.Add_CheckedChanged({ Refresh-MrTrustStatus }) $script:AllUsersCheckBox.Add_CheckedChanged({ Update-TrustStatus })
$infoPanel.Controls.Add($script:AllUsersCheckBox) $trustPanel.Controls.Add($script:AllUsersCheckBox)
$rootLabel = [Windows.Forms.Label]::new() $trustPanel.Controls.Add((New-Label -Text "Root thumbprint" -X 24 -Y 90 -Width 170 -Height 24))
$rootLabel.Text = "Root thumbprint" $script:RootThumbprintLabel = New-Label -Text "-" -X 210 -Y 90 -Width 560 -Height 24 -Color $colors.Text -Font ([Drawing.Font]::new("Consolas", 9))
$rootLabel.ForeColor = [Drawing.Color]::FromArgb(177, 190, 183) $script:RootThumbprintLabel.AutoEllipsis = $true
$rootLabel.Location = [Drawing.Point]::new(24, 92) $trustPanel.Controls.Add($script:RootThumbprintLabel)
$rootLabel.AutoSize = $true
$infoPanel.Controls.Add($rootLabel)
$script:RootThumbprintLabel = [Windows.Forms.Label]::new() $trustPanel.Controls.Add((New-Label -Text "Publisher thumbprint" -X 24 -Y 124 -Width 170 -Height 24))
$script:RootThumbprintLabel.Text = "-" $script:PublisherThumbprintLabel = New-Label -Text "-" -X 210 -Y 124 -Width 560 -Height 24 -Color $colors.Text -Font ([Drawing.Font]::new("Consolas", 9))
$script:RootThumbprintLabel.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227) $script:PublisherThumbprintLabel.AutoEllipsis = $true
$script:RootThumbprintLabel.Font = [Drawing.Font]::new("Consolas", 9) $trustPanel.Controls.Add($script:PublisherThumbprintLabel)
$script:RootThumbprintLabel.Location = [Drawing.Point]::new(180, 92)
$script:RootThumbprintLabel.AutoSize = $true
$infoPanel.Controls.Add($script:RootThumbprintLabel)
$publisherLabel = [Windows.Forms.Label]::new() $trustPanel.Controls.Add((New-Label -Text "Expires" -X 24 -Y 158 -Width 170 -Height 24))
$publisherLabel.Text = "Publisher thumbprint" $script:ExpiryLabel = New-Label -Text "-" -X 210 -Y 158 -Width 180 -Height 24 -Color $colors.Text
$publisherLabel.ForeColor = [Drawing.Color]::FromArgb(177, 190, 183) $trustPanel.Controls.Add($script:ExpiryLabel)
$publisherLabel.Location = [Drawing.Point]::new(24, 128)
$publisherLabel.AutoSize = $true
$infoPanel.Controls.Add($publisherLabel)
$script:PublisherThumbprintLabel = [Windows.Forms.Label]::new() $trustPanel.Controls.Add((New-Label -Text "Active scope" -X 24 -Y 192 -Width 170 -Height 24))
$script:PublisherThumbprintLabel.Text = "-" $script:ScopeValueLabel = New-Label -Text "-" -X 210 -Y 192 -Width 200 -Height 24 -Color $colors.Text
$script:PublisherThumbprintLabel.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227) $trustPanel.Controls.Add($script:ScopeValueLabel)
$script:PublisherThumbprintLabel.Font = [Drawing.Font]::new("Consolas", 9)
$script:PublisherThumbprintLabel.Location = [Drawing.Point]::new(180, 128)
$script:PublisherThumbprintLabel.AutoSize = $true
$infoPanel.Controls.Add($script:PublisherThumbprintLabel)
$expiryLabelTitle = [Windows.Forms.Label]::new() $summaryPanel = New-Card -X 0 -Y 338 -Width 820 -Height 76 -BackColor $colors.PanelAlt
$expiryLabelTitle.Text = "Expires" $trustPage.Controls.Add($summaryPanel)
$expiryLabelTitle.ForeColor = [Drawing.Color]::FromArgb(177, 190, 183)
$expiryLabelTitle.Location = [Drawing.Point]::new(24, 164)
$expiryLabelTitle.AutoSize = $true
$infoPanel.Controls.Add($expiryLabelTitle)
$script:ExpiryLabel = [Windows.Forms.Label]::new() $script:TrustSummaryLabel = New-Label -Text "Checking trust state..." -X 22 -Y 18 -Width 760 -Height 34 -Color $colors.Text -Font ([Drawing.Font]::new("Segoe UI", 10, [Drawing.FontStyle]::Bold))
$script:ExpiryLabel.Text = "-" $summaryPanel.Controls.Add($script:TrustSummaryLabel)
$script:ExpiryLabel.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227)
$script:ExpiryLabel.Location = [Drawing.Point]::new(180, 164)
$script:ExpiryLabel.AutoSize = $true
$infoPanel.Controls.Add($script:ExpiryLabel)
$installButton = [Windows.Forms.Button]::new() $installButton = [Windows.Forms.Button]::new()
$installButton.Text = "Install trust" $installButton.Text = "Install trust"
$installButton.BackColor = [Drawing.Color]::FromArgb(28, 185, 111) $installButton.Size = [Drawing.Size]::new(160, 44)
$installButton.ForeColor = [Drawing.Color]::White $installButton.Location = [Drawing.Point]::new(0, 438)
$installButton.FlatStyle = "Flat" Add-AnimatedButton -Button $installButton -Normal $colors.Green -Hover $colors.GreenHover
$installButton.Size = [Drawing.Size]::new(180, 46)
$installButton.Location = [Drawing.Point]::new(40, 292)
$installButton.Add_Click({ Install-MrTrustCertificates }) $installButton.Add_Click({ Install-MrTrustCertificates })
$content.Controls.Add($installButton) $trustPage.Controls.Add($installButton)
$removeButton = [Windows.Forms.Button]::new() $removeButton = [Windows.Forms.Button]::new()
$removeButton.Text = "Remove trust" $removeButton.Text = "Remove trust"
$removeButton.BackColor = [Drawing.Color]::FromArgb(44, 52, 56) $removeButton.Size = [Drawing.Size]::new(160, 44)
$removeButton.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227) $removeButton.Location = [Drawing.Point]::new(176, 438)
$removeButton.FlatStyle = "Flat" Add-AnimatedButton -Button $removeButton -Normal $colors.PanelAlt -Hover $colors.Border
$removeButton.Size = [Drawing.Size]::new(180, 46)
$removeButton.Location = [Drawing.Point]::new(240, 292)
$removeButton.Add_Click({ Remove-MrTrustCertificates }) $removeButton.Add_Click({ Remove-MrTrustCertificates })
$content.Controls.Add($removeButton) $trustPage.Controls.Add($removeButton)
$refreshButton = [Windows.Forms.Button]::new() $refreshButton = [Windows.Forms.Button]::new()
$refreshButton.Text = "Refresh" $refreshButton.Text = "Refresh"
$refreshButton.BackColor = [Drawing.Color]::FromArgb(44, 52, 56) $refreshButton.Size = [Drawing.Size]::new(124, 44)
$refreshButton.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227) $refreshButton.Location = [Drawing.Point]::new(352, 438)
$refreshButton.FlatStyle = "Flat" Add-AnimatedButton -Button $refreshButton -Normal $colors.PanelAlt -Hover $colors.Border
$refreshButton.Size = [Drawing.Size]::new(140, 46) $refreshButton.Add_Click({ Update-TrustStatus })
$refreshButton.Location = [Drawing.Point]::new(440, 292) $trustPage.Controls.Add($refreshButton)
$refreshButton.Add_Click({ Refresh-MrTrustStatus })
$content.Controls.Add($refreshButton)
$note = [Windows.Forms.Label]::new() $note = New-Label -Text "Trust actions are reversible and scoped to the selected Windows certificate store." -X 0 -Y 508 -Width 820 -Height 26 -Color $colors.Muted
$note.Text = "MrTrust installs public certificates only. It does not disable Defender, SmartScreen, UAC, or enterprise policies." $trustPage.Controls.Add($note)
$note.ForeColor = [Drawing.Color]::FromArgb(177, 190, 183)
$note.Location = [Drawing.Point]::new(40, 376)
$note.Size = [Drawing.Size]::new(820, 48)
$content.Controls.Add($note)
$form.Add_Shown({ Refresh-MrTrustStatus }) $diagnosticsPage = [Windows.Forms.Panel]::new()
$diagnosticsPage.Dock = "Fill"
$diagnosticsPage.BackColor = $colors.Background
$diagnosticsPage.Visible = $false
$contentHost.Controls.Add($diagnosticsPage)
$script:Pages.Diagnostics = $diagnosticsPage
$diagnosticsTitle = New-PageTitle -Title "File scan" -Description "Inspect a Windows executable, installer, catalog, or DLL for signature and origin signals."
$diagnosticsPage.Controls.Add($diagnosticsTitle.Title)
$diagnosticsPage.Controls.Add($diagnosticsTitle.Description)
$filePanel = New-Card -X 0 -Y 82 -Width 820 -Height 420
$diagnosticsPage.Controls.Add($filePanel)
$chooseButton = [Windows.Forms.Button]::new()
$chooseButton.Text = "Choose file"
$chooseButton.Size = [Drawing.Size]::new(150, 42)
$chooseButton.Location = [Drawing.Point]::new(24, 24)
Add-AnimatedButton -Button $chooseButton -Normal $colors.Green -Hover $colors.GreenHover
$filePanel.Controls.Add($chooseButton)
$scanButton = [Windows.Forms.Button]::new()
$scanButton.Text = "Scan again"
$scanButton.Size = [Drawing.Size]::new(130, 42)
$scanButton.Location = [Drawing.Point]::new(190, 24)
Add-AnimatedButton -Button $scanButton -Normal $colors.PanelAlt -Hover $colors.Border
$filePanel.Controls.Add($scanButton)
$script:FileVerdictLabel = New-Label -Text "Choose a Windows app or installer to inspect." -X 24 -Y 88 -Width 760 -Height 30 -Color $colors.Muted -Font ([Drawing.Font]::new("Segoe UI", 11, [Drawing.FontStyle]::Bold))
$filePanel.Controls.Add($script:FileVerdictLabel)
$filePanel.Controls.Add((New-Label -Text "File" -X 24 -Y 136 -Width 170 -Height 24))
$script:FileNameLabel = New-Label -Text "-" -X 210 -Y 136 -Width 560 -Height 24 -Color $colors.Text
$script:FileNameLabel.AutoEllipsis = $true
$filePanel.Controls.Add($script:FileNameLabel)
$filePanel.Controls.Add((New-Label -Text "Path" -X 24 -Y 170 -Width 170 -Height 24))
$script:FilePathLabel = New-Label -Text "-" -X 210 -Y 170 -Width 560 -Height 40 -Color $colors.Text
$script:FilePathLabel.AutoEllipsis = $true
$filePanel.Controls.Add($script:FilePathLabel)
$filePanel.Controls.Add((New-Label -Text "Signature status" -X 24 -Y 222 -Width 170 -Height 24))
$script:SignatureStatusLabel = New-Label -Text "-" -X 210 -Y 222 -Width 560 -Height 24 -Color $colors.Text
$filePanel.Controls.Add($script:SignatureStatusLabel)
$filePanel.Controls.Add((New-Label -Text "Signer" -X 24 -Y 256 -Width 170 -Height 24))
$script:SignerLabel = New-Label -Text "-" -X 210 -Y 256 -Width 560 -Height 36 -Color $colors.Text
$script:SignerLabel.AutoEllipsis = $true
$filePanel.Controls.Add($script:SignerLabel)
$filePanel.Controls.Add((New-Label -Text "MrSphay match" -X 24 -Y 306 -Width 170 -Height 24))
$script:MrSphayMatchLabel = New-Label -Text "-" -X 210 -Y 306 -Width 160 -Height 24 -Color $colors.Text
$filePanel.Controls.Add($script:MrSphayMatchLabel)
$filePanel.Controls.Add((New-Label -Text "Mark-of-the-Web" -X 390 -Y 306 -Width 160 -Height 24))
$script:MotwLabel = New-Label -Text "-" -X 560 -Y 306 -Width 160 -Height 24 -Color $colors.Text
$filePanel.Controls.Add($script:MotwLabel)
$filePanel.Controls.Add((New-Label -Text "SmartScreen note" -X 24 -Y 352 -Width 170 -Height 24))
$script:SmartScreenLabel = New-Label -Text "-" -X 210 -Y 352 -Width 560 -Height 48 -Color $colors.Muted
$filePanel.Controls.Add($script:SmartScreenLabel)
$chooseButton.Add_Click({
$dialog = [Windows.Forms.OpenFileDialog]::new()
$dialog.Filter = "Windows apps and installers (*.exe;*.msi;*.dll;*.cat)|*.exe;*.msi;*.dll;*.cat|All files (*.*)|*.*"
if ($dialog.ShowDialog() -eq [Windows.Forms.DialogResult]::OK) {
$script:SelectedFilePath = $dialog.FileName
Test-SelectedFile -Path $script:SelectedFilePath
}
})
$scanButton.Add_Click({
if ($script:SelectedFilePath) {
Test-SelectedFile -Path $script:SelectedFilePath
}
})
$helpPage = [Windows.Forms.Panel]::new()
$helpPage.Dock = "Fill"
$helpPage.BackColor = $colors.Background
$helpPage.Visible = $false
$contentHost.Controls.Add($helpPage)
$script:Pages.SmartScreen = $helpPage
$helpTitle = New-PageTitle -Title "SmartScreen" -Description "Understand what MrTrust can verify and what Windows reputation still controls."
$helpPage.Controls.Add($helpTitle.Title)
$helpPage.Controls.Add($helpTitle.Description)
$helpPanel = New-Card -X 0 -Y 82 -Width 820 -Height 330
$helpPage.Controls.Add($helpPanel)
$helpPanel.Controls.Add((New-Label -Text "Local trust" -X 24 -Y 24 -Width 180 -Height 24 -Color $colors.Text -Font ([Drawing.Font]::new("Segoe UI", 11, [Drawing.FontStyle]::Bold))))
$helpPanel.Controls.Add((New-Label -Text "MrTrust installs the public MrSphay root and publisher certificates into the selected Windows certificate stores." -X 24 -Y 54 -Width 740 -Height 42 -Color $colors.Muted))
$helpPanel.Controls.Add((New-Label -Text "Windows reputation" -X 24 -Y 118 -Width 220 -Height 24 -Color $colors.Text -Font ([Drawing.Font]::new("Segoe UI", 11, [Drawing.FontStyle]::Bold))))
$helpPanel.Controls.Add((New-Label -Text "SmartScreen can still warn for a new or rarely downloaded file, even when the signature is valid and the publisher is recognized." -X 24 -Y 148 -Width 740 -Height 42 -Color $colors.Muted))
$helpPanel.Controls.Add((New-Label -Text "What to check" -X 24 -Y 212 -Width 180 -Height 24 -Color $colors.Text -Font ([Drawing.Font]::new("Segoe UI", 11, [Drawing.FontStyle]::Bold))))
$helpPanel.Controls.Add((New-Label -Text "Use File scan to verify signature status, MrSphay signer matching, and Mark-of-the-Web. MrTrust never disables SmartScreen." -X 24 -Y 242 -Width 740 -Height 46 -Color $colors.Muted))
$pulseTimer = [Windows.Forms.Timer]::new()
$pulseTimer.Interval = 100
$pulseTimer.Add_Tick({
$script:CurrentAccent = ($script:CurrentAccent + 1) % 40
$value = 130 + [Math]::Abs(20 - $script:CurrentAccent) * 4
$sideAccent.BackColor = [Drawing.Color]::FromArgb(0, [Math]::Min(205, $value), 91)
})
$pulseTimer.Start()
$form.Add_Shown({
Show-MrTrustPage -Key "Trust"
Update-TrustStatus
})
[Windows.Forms.Application]::Run($form) [Windows.Forms.Application]::Run($form)

View File

@@ -1,34 +1,50 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
namespace MrTrust namespace MrTrust
{ {
internal static class MrTrustLauncher internal static class MrTrustLauncher
{ {
[STAThread] private const string PayloadResourcePrefix = "MrTrust.Payload.";
private static int Main()
{
string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
string scriptPath = Path.Combine(baseDirectory, "MrTrust.ps1");
if (!File.Exists(scriptPath)) private static readonly PayloadFile[] PayloadFiles =
{ {
MessageBox.Show( new PayloadFile("MrTrust.ps1", "MrTrust.ps1"),
"MrTrust.ps1 was not found next to MrTrust.exe.", new PayloadFile("scripts.Build-MrTrustExe.ps1", Path.Combine("scripts", "Build-MrTrustExe.ps1")),
"MrTrust", new PayloadFile("scripts.Install-MrTrust.ps1", Path.Combine("scripts", "Install-MrTrust.ps1")),
MessageBoxButtons.OK, new PayloadFile("scripts.New-MrTrustCertificate.ps1", Path.Combine("scripts", "New-MrTrustCertificate.ps1")),
MessageBoxIcon.Error); new PayloadFile("scripts.New-MrTrustIcon.ps1", Path.Combine("scripts", "New-MrTrustIcon.ps1")),
return 1; new PayloadFile("scripts.New-MrTrustRelease.ps1", Path.Combine("scripts", "New-MrTrustRelease.ps1")),
} new PayloadFile("scripts.Sign-MrTrustProject.ps1", Path.Combine("scripts", "Sign-MrTrustProject.ps1")),
new PayloadFile("scripts.Sign-MrTrustProjectLinux.sh", Path.Combine("scripts", "Sign-MrTrustProjectLinux.sh")),
new PayloadFile("scripts.Start-MrTrustGui.ps1", Path.Combine("scripts", "Start-MrTrustGui.ps1")),
new PayloadFile("scripts.Uninstall-MrTrust.ps1", Path.Combine("scripts", "Uninstall-MrTrust.ps1")),
new PayloadFile("assets.MrTrust.ico", Path.Combine("assets", "MrTrust.ico")),
new PayloadFile("assets.certificates.MrSphay-LocalTrust-Root.cer", Path.Combine("assets", "certificates", "MrSphay-LocalTrust-Root.cer")),
new PayloadFile("assets.certificates.MrSphay-CodeSigning.cer", Path.Combine("assets", "certificates", "MrSphay-CodeSigning.cer")),
new PayloadFile("assets.certificates.thumbprints.txt", Path.Combine("assets", "certificates", "thumbprints.txt"))
};
[STAThread]
private static int Main(string[] args)
{
string baseDirectory = string.Empty;
try try
{ {
baseDirectory = ExtractPayload();
string scriptPath = Path.Combine(baseDirectory, "MrTrust.ps1");
string commandArguments = BuildCommandArguments(args);
ProcessStartInfo startInfo = new ProcessStartInfo ProcessStartInfo startInfo = new ProcessStartInfo
{ {
FileName = "powershell.exe", FileName = "powershell.exe",
Arguments = "-NoProfile -ExecutionPolicy Bypass -File \"" + scriptPath + "\" gui", Arguments = "-NoProfile -ExecutionPolicy Bypass -File " + QuoteArgument(scriptPath) + " " + commandArguments,
UseShellExecute = false, UseShellExecute = false,
CreateNoWindow = true, CreateNoWindow = true,
WorkingDirectory = baseDirectory WorkingDirectory = baseDirectory
@@ -40,9 +56,10 @@ namespace MrTrust
{ {
throw new InvalidOperationException("PowerShell could not be started."); throw new InvalidOperationException("PowerShell could not be started.");
} }
}
return 0; process.WaitForExit();
return process.ExitCode;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -53,6 +70,123 @@ namespace MrTrust
MessageBoxIcon.Error); MessageBoxIcon.Error);
return 1; return 1;
} }
finally
{
TryDeleteDirectory(baseDirectory);
}
}
private static string ExtractPayload()
{
Assembly assembly = Assembly.GetExecutingAssembly();
string versionKey = GetPayloadVersionKey(assembly);
string targetDirectory = Path.Combine(
Path.GetTempPath(),
"MrTrust",
"standalone",
versionKey,
Guid.NewGuid().ToString("N"));
foreach (PayloadFile payloadFile in PayloadFiles)
{
string targetPath = Path.Combine(targetDirectory, payloadFile.RelativePath);
string targetParent = Path.GetDirectoryName(targetPath);
if (!string.IsNullOrEmpty(targetParent))
{
Directory.CreateDirectory(targetParent);
}
using (Stream stream = assembly.GetManifestResourceStream(PayloadResourcePrefix + payloadFile.ResourceName))
{
if (stream == null)
{
throw new FileNotFoundException("Embedded MrTrust payload file was not found.", payloadFile.RelativePath);
}
using (FileStream file = File.Create(targetPath))
{
stream.CopyTo(file);
}
}
}
return targetDirectory;
}
private static void TryDeleteDirectory(string directory)
{
if (string.IsNullOrEmpty(directory) || !Directory.Exists(directory))
{
return;
}
try
{
Directory.Delete(directory, true);
}
catch
{
// Best-effort cleanup only. A locked icon or antivirus scan should not mask the command result.
}
}
private static string GetPayloadVersionKey(Assembly assembly)
{
string location = Application.ExecutablePath;
if (File.Exists(location))
{
FileInfo fileInfo = new FileInfo(location);
return fileInfo.Length.ToString("x") + "-" + fileInfo.LastWriteTimeUtc.Ticks.ToString("x");
}
return assembly.GetName().Version.ToString();
}
private static string BuildCommandArguments(string[] args)
{
string[] effectiveArgs = args.Length == 0 ? new[] { "gui" } : args;
return string.Join(" ", effectiveArgs.Select(QuoteArgument).ToArray());
}
private static string QuoteArgument(string value)
{
if (string.IsNullOrEmpty(value))
{
return "\"\"";
}
if (value.IndexOfAny(new[] { ' ', '\t', '\n', '\r', '"' }) < 0)
{
return value;
}
StringBuilder builder = new StringBuilder();
builder.Append('"');
foreach (char character in value)
{
if (character == '"')
{
builder.Append('\\');
}
builder.Append(character);
}
builder.Append('"');
return builder.ToString();
}
private sealed class PayloadFile
{
public PayloadFile(string resourceName, string relativePath)
{
ResourceName = resourceName;
RelativePath = relativePath;
}
public string ResourceName { get; private set; }
public string RelativePath { get; private set; }
} }
} }
} }

View File

@@ -9,7 +9,24 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>
<SelfContained>false</SelfContained> <SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<ApplicationIcon>..\assets\MrTrust.ico</ApplicationIcon> <ApplicationIcon>..\assets\MrTrust.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="..\MrTrust.ps1" LogicalName="MrTrust.Payload.MrTrust.ps1" />
<EmbeddedResource Include="..\scripts\Build-MrTrustExe.ps1" LogicalName="MrTrust.Payload.scripts.Build-MrTrustExe.ps1" />
<EmbeddedResource Include="..\scripts\Install-MrTrust.ps1" LogicalName="MrTrust.Payload.scripts.Install-MrTrust.ps1" />
<EmbeddedResource Include="..\scripts\New-MrTrustCertificate.ps1" LogicalName="MrTrust.Payload.scripts.New-MrTrustCertificate.ps1" />
<EmbeddedResource Include="..\scripts\New-MrTrustIcon.ps1" LogicalName="MrTrust.Payload.scripts.New-MrTrustIcon.ps1" />
<EmbeddedResource Include="..\scripts\New-MrTrustRelease.ps1" LogicalName="MrTrust.Payload.scripts.New-MrTrustRelease.ps1" />
<EmbeddedResource Include="..\scripts\Sign-MrTrustProject.ps1" LogicalName="MrTrust.Payload.scripts.Sign-MrTrustProject.ps1" />
<EmbeddedResource Include="..\scripts\Sign-MrTrustProjectLinux.sh" LogicalName="MrTrust.Payload.scripts.Sign-MrTrustProjectLinux.sh" />
<EmbeddedResource Include="..\scripts\Start-MrTrustGui.ps1" LogicalName="MrTrust.Payload.scripts.Start-MrTrustGui.ps1" />
<EmbeddedResource Include="..\scripts\Uninstall-MrTrust.ps1" LogicalName="MrTrust.Payload.scripts.Uninstall-MrTrust.ps1" />
<EmbeddedResource Include="..\assets\MrTrust.ico" LogicalName="MrTrust.Payload.assets.MrTrust.ico" />
<EmbeddedResource Include="..\assets\certificates\MrSphay-LocalTrust-Root.cer" LogicalName="MrTrust.Payload.assets.certificates.MrSphay-LocalTrust-Root.cer" />
<EmbeddedResource Include="..\assets\certificates\MrSphay-CodeSigning.cer" LogicalName="MrTrust.Payload.assets.certificates.MrSphay-CodeSigning.cer" />
<EmbeddedResource Include="..\assets\certificates\thumbprints.txt" LogicalName="MrTrust.Payload.assets.certificates.thumbprints.txt" />
</ItemGroup>
</Project> </Project>