Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Release and trust policy changes require maintainer review.
* @OneDeadMachine-Dev
/.github/workflows/ @OneDeadMachine-Dev
/.signpath/ @OneDeadMachine-Dev
/CODE_SIGNING_POLICY.md @OneDeadMachine-Dev
/PRIVACY.md @OneDeadMachine-Dev
/SECURITY.md @OneDeadMachine-Dev
65 changes: 65 additions & 0 deletions .github/ISSUE_TEMPLATE/beta-report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Beta report / Отчёт beta-тестирования
description: Report a W-Fix beta result without publishing secrets or organizational data.
title: "[Beta]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Спасибо за тестирование W-Fix. Не публикуйте пароли, реальные доменные имена, IP-адреса и необработанные логи. Прикладывайте только обезличенный support bundle.

Thank you for testing W-Fix. Do not publish passwords, real domain names, IP addresses, or raw logs. Attach only a sanitized support bundle.
- type: input
id: version
attributes:
label: W-Fix version / Версия W-Fix
placeholder: v3.0.0-beta.1
validations:
required: true
- type: dropdown
id: windows
attributes:
label: Windows
options:
- Windows 10
- Windows 11 23H2 or older
- Windows 11 24H2 or newer
- Mixed Windows 10/11
validations:
required: true
- type: dropdown
id: topology
attributes:
label: Network / Сеть
options:
- Local repair / Локальный ремонт
- Active Directory domain
- Workgroup / Одноранговая сеть
validations:
required: true
- type: textarea
id: scenario
attributes:
label: Scenario / Сценарий
description: What was broken and what repair was selected?
validations:
required: true
- type: textarea
id: result
attributes:
label: Result / Результат
description: What succeeded, failed, rolled back, or required a reboot?
validations:
required: true
- type: textarea
id: bundle
attributes:
label: Sanitized support bundle / Обезличенный support bundle
description: Drag the ZIP created by W-Fix here. This is optional.
- type: checkboxes
id: privacy
attributes:
label: Privacy confirmation / Проверка конфиденциальности
options:
- label: I verified that this report contains no passwords, document contents, real domain names, or other sensitive organizational data.
required: true
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Security vulnerability / Уязвимость
url: https://github.com/OneDeadMachine-Dev/W-FIX/security/advisories/new
about: Report security issues privately. Сообщайте об уязвимостях приватно.
197 changes: 197 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
name: Release

on:
push:
tags:
- "v*"

permissions:
actions: read
contents: write

jobs:
build-sign-release:
runs-on: windows-latest
environment: release
timeout-minutes: 45

steps:
- name: Checkout tagged source
uses: actions/checkout@v7

- name: Setup .NET 8
uses: actions/setup-dotnet@v6
with:
dotnet-version: 8.0.x

- name: Resolve release metadata
id: metadata
shell: pwsh
run: |
$tag = '${{ github.ref_name }}'
if ($tag -notmatch '^v(?<version>\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$') {
throw "Unsupported release tag: $tag"
}
$notes = "docs/releases/$tag.md"
if (-not (Test-Path -LiteralPath $notes)) {
throw "Bilingual release notes are required: $notes"
}
"tag=$tag" >> $env:GITHUB_OUTPUT
"version=$($Matches.version)" >> $env:GITHUB_OUTPUT
"notes=$notes" >> $env:GITHUB_OUTPUT
"prerelease=$($Matches.version.Contains('-').ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT

- name: Restore
run: dotnet restore W-Fix.sln

- name: Strict Release build
shell: pwsh
run: |
dotnet build W-Fix.sln --configuration Release --no-restore -warnaserror
dotnet build tools/W-Fix.CatalogSigner/W-Fix.CatalogSigner.csproj --configuration Release -warnaserror

- name: Test
run: dotnet test W-Fix.sln --configuration Release --no-build --logger "console;verbosity=minimal"

- name: NuGet vulnerability report
run: dotnet list W-Fix.sln package --vulnerable --include-transitive

- name: Publish portable win-x64 executable
run: >-
dotnet publish src/W-Fix.App/W-Fix.App.csproj
--configuration Release
--runtime win-x64
--self-contained true
--no-restore
-p:Version=${{ steps.metadata.outputs.version }}
-p:PublishSingleFile=true
-p:IncludeAllContentForSelfExtract=true
-p:EnableCompressionInSingleFile=true
-o artifacts/publish/win-x64

- name: Upload unsigned artifact for origin verification
id: upload-unsigned
uses: actions/upload-artifact@v7
with:
name: unsigned-w-fix-${{ steps.metadata.outputs.tag }}
path: artifacts/publish/win-x64/W-Fix.exe
if-no-files-found: error
retention-days: 14

- name: Detect SignPath configuration
id: signpath-config
shell: pwsh
env:
SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }}
SIGNPATH_ORGANIZATION_ID: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
SIGNPATH_PROJECT_SLUG: ${{ vars.SIGNPATH_PROJECT_SLUG }}
SIGNPATH_SIGNING_POLICY_SLUG: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}
SIGNPATH_ARTIFACT_CONFIGURATION_SLUG: ${{ vars.SIGNPATH_ARTIFACT_CONFIGURATION_SLUG }}
run: |
$values = @(
$env:SIGNPATH_API_TOKEN,
$env:SIGNPATH_ORGANIZATION_ID,
$env:SIGNPATH_PROJECT_SLUG,
$env:SIGNPATH_SIGNING_POLICY_SLUG,
$env:SIGNPATH_ARTIFACT_CONFIGURATION_SLUG
)
$enabled = -not ($values | Where-Object { [string]::IsNullOrWhiteSpace($_) })
"enabled=$($enabled.ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT

- name: Submit release-signing request to SignPath
if: steps.signpath-config.outputs.enabled == 'true'
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}
artifact-configuration-slug: ${{ vars.SIGNPATH_ARTIFACT_CONFIGURATION_SLUG }}
github-artifact-id: ${{ steps.upload-unsigned.outputs.artifact-id }}
wait-for-completion: true
output-artifact-directory: artifacts/signed
parameters: |
version: "${{ steps.metadata.outputs.version }}"

- name: Prepare signed or explicitly unsigned executable
id: executable
shell: pwsh
run: |
$signingEnabled = '${{ steps.signpath-config.outputs.enabled }}' -eq 'true'
$source = 'artifacts/publish/win-x64/W-Fix.exe'
if ($signingEnabled) {
$signed = @(Get-ChildItem -LiteralPath 'artifacts/signed' -Recurse -File -Filter 'W-Fix.exe')
if ($signed.Count -ne 1) {
throw "Expected exactly one signed W-Fix.exe, found $($signed.Count)."
}
$source = $signed[0].FullName
$signature = Get-AuthenticodeSignature -LiteralPath $source
if ($signature.Status -ne 'Valid' -or $null -eq $signature.TimeStamperCertificate) {
throw "Authenticode or timestamp verification failed: $($signature.Status)"
}
} else {
Write-Warning 'SignPath is not configured. This release will be explicitly published as unsigned.'
}
New-Item -ItemType Directory -Force -Path 'artifacts/release' | Out-Null
$name = "W-Fix-${{ steps.metadata.outputs.tag }}-win-x64.exe"
Copy-Item -LiteralPath $source -Destination "artifacts/release/$name"
"name=$name" >> $env:GITHUB_OUTPUT
"signed=$($signingEnabled.ToString().ToLowerInvariant())" >> $env:GITHUB_OUTPUT

- name: Sign and verify known-issues catalog
shell: pwsh
env:
KNOWN_ISSUES_PRIVATE_KEY_PEM: ${{ secrets.KNOWN_ISSUES_PRIVATE_KEY_PEM }}
run: |
if ([string]::IsNullOrWhiteSpace($env:KNOWN_ISSUES_PRIVATE_KEY_PEM)) {
throw 'KNOWN_ISSUES_PRIVATE_KEY_PEM is required for a release.'
}
$keyPath = Join-Path $env:RUNNER_TEMP 'known-issues-private.pem'
[IO.File]::WriteAllText($keyPath, $env:KNOWN_ISSUES_PRIVATE_KEY_PEM, [Text.UTF8Encoding]::new($false))
Copy-Item 'src/W-Fix.Core/Catalog/known-issues.json' 'artifacts/release/known-issues.json'
dotnet run --project tools/W-Fix.CatalogSigner --configuration Release --no-build -- sign `
artifacts/release/known-issues.json $keyPath artifacts/release/known-issues.json.sig
if ($LASTEXITCODE -ne 0) { throw 'Catalog signing failed.' }
dotnet run --project tools/W-Fix.CatalogSigner --configuration Release --no-build -- verify `
artifacts/release/known-issues.json src/W-Fix.Core/Catalog/known-issues-public.pem artifacts/release/known-issues.json.sig
if ($LASTEXITCODE -ne 0) { throw 'Catalog signature verification failed.' }
Remove-Item -LiteralPath $keyPath -Force

- name: Package and calculate checksums
shell: pwsh
run: |
$exe = 'artifacts/release/${{ steps.executable.outputs.name }}'
$zip = "artifacts/release/W-Fix-${{ steps.metadata.outputs.tag }}-win-x64.zip"
Compress-Archive -LiteralPath $exe -DestinationPath $zip -CompressionLevel Optimal
$files = @($exe, $zip, 'artifacts/release/known-issues.json', 'artifacts/release/known-issues.json.sig')
$lines = foreach ($file in $files) {
$hash = Get-FileHash -Algorithm SHA256 -LiteralPath $file
"$($hash.Hash.ToLowerInvariant()) $([IO.Path]::GetFileName($file))"
}
[IO.File]::WriteAllLines('artifacts/release/SHA256SUMS.txt', $lines, [Text.UTF8Encoding]::new($false))

- name: Upload final release artifact
uses: actions/upload-artifact@v7
with:
name: release-${{ steps.metadata.outputs.tag }}
path: artifacts/release/*
if-no-files-found: error
retention-days: 90

- name: Publish GitHub Release
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: |
$arguments = @(
'release', 'create', '${{ steps.metadata.outputs.tag }}',
'--verify-tag',
'--title', "W-Fix ${{ steps.metadata.outputs.version }}",
'--notes-file', '${{ steps.metadata.outputs.notes }}'
)
if ('${{ steps.metadata.outputs.prerelease }}' -eq 'true') {
$arguments += '--prerelease'
}
$arguments += @(Get-ChildItem -LiteralPath 'artifacts/release' -File | Select-Object -ExpandProperty FullName)
& gh @arguments
if ($LASTEXITCODE -ne 0) { throw 'GitHub Release publication failed.' }
30 changes: 30 additions & 0 deletions CODE_SIGNING_POLICY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Code signing policy / Политика подписи кода

Free code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/) — после одобрения проекта SignPath Foundation.

## Roles / Роли

- Committer and reviewer: [OneDeadMachine-Dev](https://github.com/OneDeadMachine-Dev)
- Signing approver: [OneDeadMachine-Dev](https://github.com/OneDeadMachine-Dev)

## Release policy / Политика выпуска

- Публичные binaries собираются только GitHub Actions из защищённой ветки `main` и опубликованного тега `v*`.
- Release workflow повторно выполняет restore, строгую Release-сборку, тесты и проверку зависимостей.
- Неподписанный artifact загружается в GitHub Actions до передачи в SignPath; SignPath проверяет происхождение сборки.
- Release signing требует ручного подтверждения. После подписи workflow проверяет Authenticode и timestamp, затем рассчитывает SHA-256.
- ZIP всегда создаётся из уже подписанного EXE.
- Detached ECDSA signature каталога известных проблем не заменяет Authenticode и управляется отдельным ключом.
- Закрытые ключи и API tokens находятся только в защищённых secret stores и не записываются в GitHub Release или логи.

- Public binaries are built only by GitHub Actions from protected `main` and a published `v*` tag.
- The release workflow repeats restore, strict Release build, tests, and dependency checks.
- The unsigned artifact is uploaded to GitHub Actions before submission so SignPath can verify build origin.
- Release signing requires manual approval. The workflow then verifies Authenticode and timestamp before calculating SHA-256.
- ZIP archives are always created from the signed EXE.
- The known-issues catalog ECDSA signature is independent from Authenticode and uses a separate key.
- Private keys and API tokens exist only in protected secret stores and are never written to releases or logs.

Until SignPath approval, public beta releases remain explicitly marked unsigned and include SHA-256 checksums. For internal testing, a separately documented self-signed certificate may be trusted manually on managed test computers; it is never presented as publicly trusted.

See also [Privacy Policy](PRIVACY.md) and [Security Policy](SECURITY.md).
27 changes: 27 additions & 0 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Политика конфиденциальности / Privacy Policy

## Русский

W-Fix не содержит телеметрии, рекламы и автоматической отправки отчётов. Приложение работает локально или с компьютерами, которые явно выбрал оператор.

- Логи хранятся локально в `%LocalAppData%\W-Fix\Logs` и могут содержать имя локального компьютера и пользователя.
- Отчёты о ремонте хранятся в `%ProgramData%\W-Fix\Runs` и могут содержать имена выбранных компьютеров, принтеров, драйверов и результаты действий.
- Обезличенный support bundle создаётся только по команде пользователя. Имена компьютеров и принтеров заменяются псевдонимами; пароли, ссылки Credential Manager, пути снимков и содержимое документов печати не экспортируются.
- Альтернативные учётные данные сохраняются только после явного согласия в Windows Credential Manager. Пароли не записываются в конфигурацию, аргументы процессов, логи и отчёты.
- При диагностике W-Fix может загрузить декларативный каталог известных проблем с официального GitHub Release проекта. Загруженный каталог проверяется цифровой подписью и не может содержать исполняемый код.
- W-Fix не передаёт сведения третьим лицам, если пользователь сам не экспортировал и не отправил отчёт или support bundle.

Сообщить о проблеме конфиденциальности можно через [Private vulnerability reporting](https://github.com/OneDeadMachine-Dev/W-FIX/security/advisories/new).

## English

W-Fix contains no telemetry, advertising, or automatic report uploads. It operates locally or against computers explicitly selected by the operator.

- Logs are stored locally in `%LocalAppData%\W-Fix\Logs` and may contain the local computer and user names.
- Repair reports are stored in `%ProgramData%\W-Fix\Runs` and may contain selected computer, printer, and driver names plus action results.
- A sanitized support bundle is created only on explicit request. Computer and printer names are replaced with aliases; passwords, Credential Manager references, snapshot paths, and print-document contents are excluded.
- Alternate credentials are saved only with explicit consent in Windows Credential Manager. Passwords are never written to configuration, process arguments, logs, or reports.
- During diagnostics W-Fix may download a declarative known-issues catalog from the project's official GitHub Release. The catalog is signature-verified and cannot contain executable code.
- W-Fix sends no information to third parties unless the user explicitly exports and shares a report or support bundle.

Report a privacy concern through [Private vulnerability reporting](https://github.com/OneDeadMachine-Dev/W-FIX/security/advisories/new).
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

[![Platform](https://img.shields.io/badge/platform-Windows%2010%2F11-blue?logo=windows)](https://www.microsoft.com/windows)
[![.NET](https://img.shields.io/badge/.NET-8.0-purple?logo=dotnet)](https://dotnet.microsoft.com/)
[![Release](https://img.shields.io/badge/version-3.0.0--beta.1-blue)](https://github.com/OneDeadMachine/W-Fix/releases)
[![Release](https://img.shields.io/badge/version-3.0.0--beta.1-blue)](https://github.com/OneDeadMachine-Dev/W-FIX/releases)
[![License](https://img.shields.io/badge/license-MIT-orange)](LICENSE)
[![Author](https://img.shields.io/badge/author-OneDeadMachine-red)](https://github.com/OneDeadMachine)

Expand Down Expand Up @@ -58,7 +58,7 @@

## 🚀 Быстрый старт

1. Скачай `W-Fix.exe` из раздела [Releases](https://github.com/OneDeadMachine/W-Fix/releases)
1. Скачай `W-Fix.exe` из раздела [Releases](https://github.com/OneDeadMachine-Dev/W-FIX/releases)
2. Запусти **от имени администратора** (правой кнопкой → «Запуск от имени администратора»)
3. Выбери принтер в левой панели
4. Выбери фиксер в правой панели → нажми **«Применить»**
Expand All @@ -82,7 +82,7 @@

### Debug-запуск
```powershell
git clone https://github.com/OneDeadMachine/W-Fix.git
git clone https://github.com/OneDeadMachine-Dev/W-FIX.git
cd W-Fix
dotnet run --project src/W-Fix.App
```
Expand Down
Loading