9 Commits

Author SHA1 Message Date
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
MrSphay
cf32e3b20e Polish GUI and add app icon
All checks were successful
Build MrTrust / build (push) Successful in 1m18s
2026-05-16 00:14:46 +02:00
MrSphay
46026cb62c Build Windows EXE on Ubuntu runner
All checks were successful
Build MrTrust / build (push) Successful in 1m37s
2026-05-16 00:03:58 +02:00
16 changed files with 712 additions and 106 deletions

View File

@@ -8,42 +8,72 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
build-windows: build:
runs-on: windows-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Verify PowerShell scripts - name: Setup .NET
shell: powershell uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Build Windows EXE
shell: bash
run: | run: |
$scripts = @() set -euo pipefail
$scripts += @(Get-ChildItem . -Filter *.ps1) version="0.1.2"
$scripts += @(Get-ChildItem .\scripts -Filter *.ps1) dotnet publish src/MrTrustLauncher.csproj \
foreach ($script in $scripts) { --configuration Release \
$tokens = $null --runtime win-x64 \
$errors = $null --output dist/build \
[System.Management.Automation.Language.Parser]::ParseFile($script.FullName, [ref]$tokens, [ref]$errors) | Out-Null -p:EnableWindowsTargeting=true \
if ($errors) { throw $errors } -p:PublishSingleFile=true \
} -p:SelfContained=true
cp dist/build/MrTrust.exe dist/MrTrust.exe
- name: Build release ZIP - name: Build release ZIP
shell: powershell shell: bash
run: | run: |
$arguments = @("-ExecutionPolicy", "Bypass", "-File", ".\scripts\New-MrTrustRelease.ps1", "-Version", "0.1.0") set -euo pipefail
if ($env:MRTRUST_SIGNING_THUMBPRINT) { version="0.1.2"
$arguments += @("-SigningThumbprint", $env:MRTRUST_SIGNING_THUMBPRINT) package_root="dist/MrTrust-${version}"
} rm -rf "$package_root" "dist/MrTrust-${version}.zip"
powershell @arguments mkdir -p "$package_root"
cp dist/MrTrust.exe "$package_root/"
cp README.md "$package_root/"
(cd dist && zip -r "MrTrust-${version}.zip" "MrTrust-${version}")
- name: Show package contents - name: Show package contents
shell: powershell shell: bash
run: | run: |
Get-ChildItem .\dist -Recurse | Select-Object FullName, Length find dist -maxdepth 4 -type f -printf '%p %s bytes\n'
- name: Upload release artifact - name: Upload release artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: MrTrust-0.1.0 name: MrTrust-0.1.2
path: dist/MrTrust-0.1.0.zip path: dist/MrTrust-0.1.2.zip
- name: Attach ZIP to Gitea release
if: github.ref == 'refs/heads/main'
shell: bash
env:
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
version="0.1.2"
api="https://git.wilkensxl.de/api/v1/repos/MrSphay/MrTrust"
release_json="$(curl -fsS -H "Authorization: token ${GITEA_TOKEN}" "${api}/releases/tags/v${version}")"
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
curl -fsS \
-X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-F "attachment=@dist/MrTrust-${version}.zip" \
"${api}/releases/${release_id}/assets?name=MrTrust-${version}.zip"

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,9 +1,22 @@
# Changelog # Changelog
## 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
- Added a custom MrTrust application icon and embedded it into the launcher.
- Improved the GUI layout so status text is not clipped.
- Updated the Gitea artifact version to `0.1.1`.
## 0.1.0 ## 0.1.0
- Added MrTrust certificate generation, installation, removal, and signing scripts. - Added MrTrust certificate generation, installation, removal, and signing scripts.
- Added a simple Windows GUI for installing and removing MrTrust. - Added a simple Windows GUI for installing and removing MrTrust.
- Added a Windows launcher EXE and Gitea Runner workflow for release ZIP builds. - Added a Windows launcher EXE and Ubuntu Gitea Runner workflow for release ZIP builds.
- Added integration prompt for other Windows projects. - Added integration prompt for other Windows projects.
- Added security model documentation. - Added security model documentation.

View File

@@ -13,13 +13,17 @@ MrTrust does not bypass Microsoft Defender or SmartScreen. Windows can still sca
## What It Contains ## What It Contains
- `MrTrust.ps1 gui` opens a simple Windows interface for installing or removing trust. - `MrTrust.exe` opens a standalone Windows interface for installing or removing trust.
- `MrTrust.ps1` and `scripts/` are source and maintainer tools for building, signing, and local development.
- `scripts/New-MrTrustCertificate.ps1` creates a local root certificate and a code-signing certificate for the publisher. - `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/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/Uninstall-MrTrust.ps1` removes the MrTrust certificate again.
- `scripts/Sign-MrTrustProject.ps1` signs `.exe`, `.msi`, `.ps1`, and other Authenticode-compatible files. - `scripts/Sign-MrTrustProject.ps1` signs `.exe`, `.msi`, `.ps1`, and other Authenticode-compatible files.
- `scripts/New-MrTrustRelease.ps1` builds a distributable ZIP package. - `scripts/New-MrTrustRelease.ps1` builds a distributable ZIP package.
- `docs/integration-prompt.md` is a prompt you can paste into other Windows projects. - `docs/integration-prompt.md` is a prompt you can paste into other Windows projects.
- `docs/agent-target-integration.md` is the autonomous target-project integration runbook for agents.
- `mrtrust.integration.json` is the machine-readable integration contract.
- `MrTrust.exe` is standalone for normal users. It embeds the public certificates and runtime scripts.
## Quick Start For MrSphay ## Quick Start For MrSphay
@@ -65,26 +69,14 @@ Remove the trust certificate:
Build a user-facing ZIP release: Build a user-facing ZIP release:
```powershell ```powershell
.\scripts\New-MrTrustRelease.ps1 -Version 0.1.0 .\scripts\New-MrTrustRelease.ps1 -Version 0.1.2
``` ```
The Gitea workflow `.gitea/workflows/build.yml` builds the same ZIP on a Windows runner and uploads it as an artifact. 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.
If the Windows runner has the private signing certificate installed, set `MRTRUST_SIGNING_THUMBPRINT` to sign the launcher during the build.
## User Installation ## User Installation
For normal users, distribute MrTrust with the public certificate file: For normal users, distribute `MrTrust.exe`. The executable embeds the public certificate files and opens the GUI by default.
```text
assets\certificates\MrSphay-LocalTrust-Root.cer
assets\certificates\MrSphay-CodeSigning.cer
```
The user runs:
```powershell
.\MrTrust.ps1 gui
```
By default, MrTrust installs trust only for the current Windows user: By default, MrTrust installs trust only for the current Windows user:
@@ -96,18 +88,34 @@ Code-signing certificate -> Cert:\CurrentUser\TrustedPublisher
For all users on the machine, run PowerShell as Administrator: For all users on the machine, run PowerShell as Administrator:
```powershell ```powershell
.\MrTrust.ps1 install -Scope LocalMachine .\MrTrust.exe
``` ```
Then choose the all-users option in the GUI.
## Using This Repo With Other Agents ## Using This Repo With Other Agents
Yes. Give another agent this repository URL and the target Windows project, then paste `docs/integration-prompt.md`. Yes. Give another agent this repository URL, the target Windows project, and `docs/integration-prompt.md`.
For autonomous work, the agent should read these files in order:
1. `mrtrust.integration.json`
2. `docs/agent-target-integration.md`
3. `docs/integration-prompt.md`
The agent's job is to modify the target project, not this repository:
- expose a visible "Open MrTrust" or trust setup path for users
- link to or bundle the standalone `MrTrust.exe`
- sign Windows release artifacts with the MrSphay code-signing certificate
- keep trust installation explicit, reversible, and user-confirmed
- keep private signing material out of the target repository and release artifacts
Both sides have to be wired: Both sides have to be wired:
- MrTrust side: users install the public trust certificates once. - MrTrust side: users install the public trust certificates once.
- Target project side: release artifacts are signed with the MrSphay code-signing certificate. - 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. - Installer side, optional: the target app can offer "Open MrTrust" or bundle `MrTrust.exe`, but it must not silently change trust.
If the target project is not signed, MrTrust cannot make it trusted. If the target project is not signed, MrTrust cannot make it trusted.
@@ -120,4 +128,6 @@ If the target project is not signed, MrTrust cannot make it trusted.
## Recommended Project Integration ## Recommended Project Integration
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. 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 the standalone `MrTrust.exe`, and a signing step without hiding security changes from the user.
For agents that can inspect files autonomously, `docs/agent-target-integration.md` gives the decision tree, installer patterns, signing commands, and completion checklist.

BIN
assets/MrTrust.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,162 @@
# 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.
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
```
## 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 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,31 @@ 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, .ps1, .psm1, .psd1, or .cat release artifact, sign it with:
powershell.exe -ExecutionPolicy Bypass -File .\MrTrust\MrTrust.ps1 sign -Path <artifact-path> -CertificateThumbprint A024A89200469F099EC4A172B4F96F6428AFD41B .\MrTrust\MrTrust.exe sign -Path <artifact-path> -CertificateThumbprint A024A89200469F099EC4A172B4F96F6428AFD41B
- 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.
- If the target project needs automated signing, call MrTrust's signing script from CI or a secure local release machine where the private certificate is already installed or supplied through secrets. 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": {

59
mrtrust.integration.json Normal file
View File

@@ -0,0 +1,59 @@
{
"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"
],
"preferredCommand": ".\\MrTrust\\MrTrust.exe sign -Path <artifact-path> -CertificateThumbprint A024A89200469F099EC4A172B4F96F6428AFD41B",
"ciGuidance": "Run signing only on a trusted Windows release runner or secure local release machine where the private certificate is already installed or supplied through secrets.",
"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

@@ -13,13 +13,40 @@ function Resolve-FullPath {
$root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path) $root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
$sourcePath = Join-Path $root "src\MrTrustLauncher.cs" $sourcePath = Join-Path $root "src\MrTrustLauncher.cs"
$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\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"
} }
if (-not (Test-Path -LiteralPath $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 = @(
@@ -32,19 +59,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",
/reference:System.Windows.Forms.dll ` "/win32icon:$iconPath",
/reference:System.Drawing.dll ` "/reference:System.Windows.Forms.dll",
$sourcePath "/reference:System.Drawing.dll"
)
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

@@ -0,0 +1,61 @@
[CmdletBinding()]
param(
[string]$OutputPath = ".\assets\MrTrust.ico"
)
$ErrorActionPreference = "Stop"
function Resolve-FullPath {
param([Parameter(Mandatory)][string]$Path)
$executionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
}
Add-Type -AssemblyName System.Drawing
$resolvedOutputPath = Resolve-FullPath $OutputPath
$outputDirectory = Split-Path -Parent $resolvedOutputPath
New-Item -ItemType Directory -Force -Path $outputDirectory | Out-Null
$bitmap = [Drawing.Bitmap]::new(64, 64)
$graphics = [Drawing.Graphics]::FromImage($bitmap)
$graphics.SmoothingMode = [Drawing.Drawing2D.SmoothingMode]::AntiAlias
$graphics.Clear([Drawing.Color]::Transparent)
$backgroundBrush = [Drawing.SolidBrush]::new([Drawing.Color]::FromArgb(27, 32, 35))
$accentBrush = [Drawing.SolidBrush]::new([Drawing.Color]::FromArgb(28, 185, 111))
$lightBrush = [Drawing.SolidBrush]::new([Drawing.Color]::FromArgb(225, 231, 227))
$shadowBrush = [Drawing.SolidBrush]::new([Drawing.Color]::FromArgb(70, 0, 0, 0))
$outlinePen = [Drawing.Pen]::new([Drawing.Color]::FromArgb(71, 84, 90), 3)
$graphics.FillRectangle($shadowBrush, 9, 10, 48, 48)
$graphics.FillRectangle($backgroundBrush, 7, 7, 48, 48)
$graphics.DrawRectangle($outlinePen, 7, 7, 48, 48)
$graphics.FillRectangle($accentBrush, 13, 13, 12, 36)
$graphics.FillRectangle($accentBrush, 13, 13, 30, 12)
$graphics.FillRectangle($lightBrush, 31, 29, 14, 20)
$font = [Drawing.Font]::new("Segoe UI", 18, [Drawing.FontStyle]::Bold, [Drawing.GraphicsUnit]::Pixel)
$graphics.DrawString("T", $font, $lightBrush, 32, 27)
$handle = $bitmap.GetHicon()
$icon = [Drawing.Icon]::FromHandle($handle)
$stream = [IO.File]::Open($resolvedOutputPath, [IO.FileMode]::Create)
try {
$icon.Save($stream)
}
finally {
$stream.Dispose()
$icon.Dispose()
$font.Dispose()
$outlinePen.Dispose()
$shadowBrush.Dispose()
$lightBrush.Dispose()
$accentBrush.Dispose()
$backgroundBrush.Dispose()
$graphics.Dispose()
$bitmap.Dispose()
}
Write-Host "Created icon:"
Write-Host " $resolvedOutputPath"

View File

@@ -20,15 +20,17 @@ $output = Resolve-FullPath $OutputDirectory
$packageRoot = Join-Path $output "MrTrust-$Version" $packageRoot = Join-Path $output "MrTrust-$Version"
$zipPath = Join-Path $output "MrTrust-$Version.zip" $zipPath = Join-Path $output "MrTrust-$Version.zip"
$exePath = Join-Path $output "MrTrust.exe" $exePath = Join-Path $output "MrTrust.exe"
$iconPath = Join-Path $root "assets\MrTrust.ico"
if (Test-Path -LiteralPath $packageRoot) { if (Test-Path -LiteralPath $packageRoot) {
Remove-Item -LiteralPath $packageRoot -Recurse -Force Remove-Item -LiteralPath $packageRoot -Recurse -Force
} }
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 if (-not (Test-Path -LiteralPath $iconPath)) {
New-Item -ItemType Directory -Force -Path (Join-Path $packageRoot "docs") | Out-Null & (Join-Path $root "scripts\New-MrTrustIcon.ps1") -OutputPath $iconPath
}
& (Join-Path $root "scripts\Build-MrTrustExe.ps1") -OutputPath $exePath & (Join-Path $root "scripts\Build-MrTrustExe.ps1") -OutputPath $exePath
@@ -50,15 +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 (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

@@ -9,6 +9,7 @@ Add-Type -AssemblyName System.Drawing
$script:RootPath = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path) $script:RootPath = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
$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"
function Test-IsAdministrator { function Test-IsAdministrator {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent() $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
@@ -75,11 +76,11 @@ function Refresh-MrTrustStatus {
$script:ExpiryLabel.Text = $rootCertificate.NotAfter.ToString("yyyy-MM-dd") $script:ExpiryLabel.Text = $rootCertificate.NotAfter.ToString("yyyy-MM-dd")
if ($rootInstalled -and $publisherInstalled) { if ($rootInstalled -and $publisherInstalled) {
Set-StatusText "Trusted for $scope" Set-StatusText "Trusted"
$script:StatusPill.BackColor = [Drawing.Color]::FromArgb(28, 185, 111) $script:StatusPill.BackColor = [Drawing.Color]::FromArgb(28, 185, 111)
} }
else { else {
Set-StatusText "Not installed for $scope" Set-StatusText "Not installed"
$script:StatusPill.BackColor = [Drawing.Color]::FromArgb(242, 153, 74) $script:StatusPill.BackColor = [Drawing.Color]::FromArgb(242, 153, 74)
} }
} }
@@ -166,14 +167,17 @@ 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(760, 520) $form.ClientSize = [Drawing.Size]::new(900, 560)
$form.MinimumSize = [Drawing.Size]::new(720, 500) $form.MinimumSize = [Drawing.Size]::new(860, 540)
$form.BackColor = [Drawing.Color]::FromArgb(22, 26, 29) $form.BackColor = [Drawing.Color]::FromArgb(22, 26, 29)
$form.Font = [Drawing.Font]::new("Segoe UI", 10) $form.Font = [Drawing.Font]::new("Segoe UI", 10)
if (Test-Path -LiteralPath $script:IconPath) {
$form.Icon = [Drawing.Icon]::new($script:IconPath)
}
$header = [Windows.Forms.Panel]::new() $header = [Windows.Forms.Panel]::new()
$header.Dock = "Top" $header.Dock = "Top"
$header.Height = 108 $header.Height = 124
$header.BackColor = [Drawing.Color]::FromArgb(27, 32, 35) $header.BackColor = [Drawing.Color]::FromArgb(27, 32, 35)
$form.Controls.Add($header) $form.Controls.Add($header)
@@ -183,32 +187,50 @@ $accent.Width = 8
$accent.BackColor = [Drawing.Color]::FromArgb(28, 185, 111) $accent.BackColor = [Drawing.Color]::FromArgb(28, 185, 111)
$header.Controls.Add($accent) $header.Controls.Add($accent)
$logoBox = [Windows.Forms.PictureBox]::new()
$logoBox.Size = [Drawing.Size]::new(44, 44)
$logoBox.Location = [Drawing.Point]::new(34, 30)
$logoBox.SizeMode = "StretchImage"
if (Test-Path -LiteralPath $script:IconPath) {
$logoBox.Image = [Drawing.Icon]::new($script:IconPath).ToBitmap()
}
$header.Controls.Add($logoBox)
$title = [Windows.Forms.Label]::new() $title = [Windows.Forms.Label]::new()
$title.Text = "MrTrust" $title.Text = "MrTrust"
$title.ForeColor = [Drawing.Color]::White $title.ForeColor = [Drawing.Color]::White
$title.Font = [Drawing.Font]::new("Segoe UI", 24, [Drawing.FontStyle]::Bold) $title.Font = [Drawing.Font]::new("Segoe UI", 24, [Drawing.FontStyle]::Bold)
$title.AutoSize = $true $title.AutoSize = $true
$title.Location = [Drawing.Point]::new(30, 18) $title.Location = [Drawing.Point]::new(92, 24)
$header.Controls.Add($title) $header.Controls.Add($title)
$subtitle = [Windows.Forms.Label]::new() $subtitle = [Windows.Forms.Label]::new()
$subtitle.Text = "Trust setup for MrSphay signed Windows apps" $subtitle.Text = "Trust setup for MrSphay signed Windows apps"
$subtitle.ForeColor = [Drawing.Color]::FromArgb(177, 190, 183) $subtitle.ForeColor = [Drawing.Color]::FromArgb(177, 190, 183)
$subtitle.AutoSize = $true $subtitle.AutoSize = $true
$subtitle.Location = [Drawing.Point]::new(34, 66) $subtitle.Location = [Drawing.Point]::new(96, 74)
$header.Controls.Add($subtitle) $header.Controls.Add($subtitle)
$statusText = [Windows.Forms.Label]::new()
$statusText.Text = "Status"
$statusText.ForeColor = [Drawing.Color]::FromArgb(177, 190, 183)
$statusText.AutoSize = $true
$statusText.Location = [Drawing.Point]::new(646, 32)
$header.Controls.Add($statusText)
$script:StatusPill = [Windows.Forms.Panel]::new() $script:StatusPill = [Windows.Forms.Panel]::new()
$script:StatusPill.Size = [Drawing.Size]::new(14, 14) $script:StatusPill.Size = [Drawing.Size]::new(16, 16)
$script:StatusPill.Location = [Drawing.Point]::new(610, 42) $script:StatusPill.Location = [Drawing.Point]::new(646, 62)
$script:StatusPill.BackColor = [Drawing.Color]::FromArgb(242, 153, 74) $script:StatusPill.BackColor = [Drawing.Color]::FromArgb(242, 153, 74)
$header.Controls.Add($script:StatusPill) $header.Controls.Add($script:StatusPill)
$script:StatusLabel = [Windows.Forms.Label]::new() $script:StatusLabel = [Windows.Forms.Label]::new()
$script:StatusLabel.Text = "Checking..." $script:StatusLabel.Text = "Checking..."
$script:StatusLabel.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227) $script:StatusLabel.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227)
$script:StatusLabel.AutoSize = $true $script:StatusLabel.AutoSize = $false
$script:StatusLabel.Location = [Drawing.Point]::new(632, 38) $script:StatusLabel.AutoEllipsis = $true
$script:StatusLabel.Location = [Drawing.Point]::new(674, 57)
$script:StatusLabel.Size = [Drawing.Size]::new(190, 28)
$header.Controls.Add($script:StatusLabel) $header.Controls.Add($script:StatusLabel)
$content = [Windows.Forms.Panel]::new() $content = [Windows.Forms.Panel]::new()
@@ -219,8 +241,8 @@ $form.Controls.Add($content)
$infoPanel = [Windows.Forms.Panel]::new() $infoPanel = [Windows.Forms.Panel]::new()
$infoPanel.BackColor = [Drawing.Color]::FromArgb(31, 37, 40) $infoPanel.BackColor = [Drawing.Color]::FromArgb(31, 37, 40)
$infoPanel.Size = [Drawing.Size]::new(700, 210) $infoPanel.Size = [Drawing.Size]::new(820, 226)
$infoPanel.Location = [Drawing.Point]::new(30, 34) $infoPanel.Location = [Drawing.Point]::new(40, 34)
$content.Controls.Add($infoPanel) $content.Controls.Add($infoPanel)
$scopeLabel = [Windows.Forms.Label]::new() $scopeLabel = [Windows.Forms.Label]::new()
@@ -289,7 +311,7 @@ $installButton.BackColor = [Drawing.Color]::FromArgb(28, 185, 111)
$installButton.ForeColor = [Drawing.Color]::White $installButton.ForeColor = [Drawing.Color]::White
$installButton.FlatStyle = "Flat" $installButton.FlatStyle = "Flat"
$installButton.Size = [Drawing.Size]::new(180, 46) $installButton.Size = [Drawing.Size]::new(180, 46)
$installButton.Location = [Drawing.Point]::new(30, 274) $installButton.Location = [Drawing.Point]::new(40, 292)
$installButton.Add_Click({ Install-MrTrustCertificates }) $installButton.Add_Click({ Install-MrTrustCertificates })
$content.Controls.Add($installButton) $content.Controls.Add($installButton)
@@ -299,7 +321,7 @@ $removeButton.BackColor = [Drawing.Color]::FromArgb(44, 52, 56)
$removeButton.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227) $removeButton.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227)
$removeButton.FlatStyle = "Flat" $removeButton.FlatStyle = "Flat"
$removeButton.Size = [Drawing.Size]::new(180, 46) $removeButton.Size = [Drawing.Size]::new(180, 46)
$removeButton.Location = [Drawing.Point]::new(230, 274) $removeButton.Location = [Drawing.Point]::new(240, 292)
$removeButton.Add_Click({ Remove-MrTrustCertificates }) $removeButton.Add_Click({ Remove-MrTrustCertificates })
$content.Controls.Add($removeButton) $content.Controls.Add($removeButton)
@@ -309,15 +331,15 @@ $refreshButton.BackColor = [Drawing.Color]::FromArgb(44, 52, 56)
$refreshButton.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227) $refreshButton.ForeColor = [Drawing.Color]::FromArgb(225, 231, 227)
$refreshButton.FlatStyle = "Flat" $refreshButton.FlatStyle = "Flat"
$refreshButton.Size = [Drawing.Size]::new(140, 46) $refreshButton.Size = [Drawing.Size]::new(140, 46)
$refreshButton.Location = [Drawing.Point]::new(430, 274) $refreshButton.Location = [Drawing.Point]::new(440, 292)
$refreshButton.Add_Click({ Refresh-MrTrustStatus }) $refreshButton.Add_Click({ Refresh-MrTrustStatus })
$content.Controls.Add($refreshButton) $content.Controls.Add($refreshButton)
$note = [Windows.Forms.Label]::new() $note = [Windows.Forms.Label]::new()
$note.Text = "MrTrust installs public certificates only. It does not disable Defender, SmartScreen, UAC, or enterprise policies." $note.Text = "MrTrust installs public certificates only. It does not disable Defender, SmartScreen, UAC, or enterprise policies."
$note.ForeColor = [Drawing.Color]::FromArgb(177, 190, 183) $note.ForeColor = [Drawing.Color]::FromArgb(177, 190, 183)
$note.Location = [Drawing.Point]::new(30, 352) $note.Location = [Drawing.Point]::new(40, 376)
$note.Size = [Drawing.Size]::new(700, 48) $note.Size = [Drawing.Size]::new(820, 48)
$content.Controls.Add($note) $content.Controls.Add($note)
$form.Add_Shown({ Refresh-MrTrustStatus }) $form.Add_Shown({ Refresh-MrTrustStatus })

View File

@@ -1,34 +1,51 @@
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;
#pragma warning disable CS8600, CS8602
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.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 +57,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 +71,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

@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<AssemblyName>MrTrust</AssemblyName>
<RootNamespace>MrTrust</RootNamespace>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<ApplicationIcon>..\assets\MrTrust.ico</ApplicationIcon>
</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\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>