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
73 changes: 73 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Bug report
description: Report reproducible incorrect behavior, a crash, or an evidence-quality problem.
title: "[Bug]: "
labels:
- bug
body:
- type: markdown
attributes:
value: |
Remove secrets, account names, private addresses, target evidence, and full local paths before submitting. Report security vulnerabilities through the private security-policy link instead.
- type: input
id: version
attributes:
label: PortCVE version
description: Paste the complete output of `portcve version`.
placeholder: portcve 0.2.0-alpha.1+...
validations:
required: true
- type: dropdown
id: install
attributes:
label: Installation method
options:
- Signed managed installer
- Signed portable ZIP
- Built from source
- Other
validations:
required: true
- type: input
id: windows
attributes:
label: Windows version and architecture
placeholder: Windows 11 24H2, x64
validations:
required: true
- type: textarea
id: command
attributes:
label: Exact command
description: Redact targets and paths where needed. Never include API keys or environment values.
render: powershell
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
validations:
required: true
- type: textarea
id: actual
attributes:
label: Actual behavior and exit code
description: Include the exit code and sanitized stderr. Attach default-redacted JSON only after reviewing it.
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Minimal reproduction
description: Explain the smallest authorized/local setup that reproduces the issue.
validations:
required: true
- type: checkboxes
id: safety
attributes:
label: Safety confirmation
options:
- label: I removed credentials, secrets, private target evidence, account names, and unnecessary local paths.
required: true
- label: This is not a confidential security vulnerability requiring private disclosure.
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: false
contact_links:
- name: Report a security vulnerability privately
url: https://github.com/Labeeb2339/PortCVE/security/policy
about: Do not disclose exploitable vulnerabilities, secrets, or sensitive target evidence in a public issue.
35 changes: 35 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Feature request
description: Propose a bounded defensive workflow or evidence improvement.
title: "[Feature]: "
labels:
- enhancement
body:
- type: textarea
id: problem
attributes:
label: Operational problem
description: Describe the analyst or administrator task, not only the proposed implementation.
validations:
required: true
- type: textarea
id: workflow
attributes:
label: Desired command and workflow
description: Include an example command, expected evidence, and automation exit behavior.
render: powershell
validations:
required: true
- type: textarea
id: boundaries
attributes:
label: Safety, privacy, and authorization boundaries
description: Explain network activity, privileges, sensitive data, destructive actions, and likely false claims.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Existing tools or alternatives
description: Name current tools and explain the specific gap PortCVE would fill.
validations:
required: true
21 changes: 21 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## Purpose

Describe the user-visible problem and the evidence contract this change affects.

## Safety and compatibility

- [ ] No command gained implicit network access, privilege escalation, destructive behavior, or a weaker authorization gate.
- [ ] Privacy-reduced output and `--include-private` behavior were reviewed.
- [ ] Incomplete evidence still fails closed for `--strict` and finding gates.
- [ ] Versioned JSON/schema or CLI compatibility changes are documented and tested.
- [ ] New third-party actions and dependencies are pinned and justified.

## Verification

- [ ] `dotnet restore PortCVE.sln --locked-mode`
- [ ] `dotnet format PortCVE.sln --verify-no-changes --no-restore`
- [ ] `dotnet build PortCVE.sln -c Release --no-restore`
- [ ] `dotnet test PortCVE.sln -c Release --no-build --no-restore`
- [ ] Relevant PowerShell/live harnesses passed, or the omission is explained below.

Sanitized evidence and omitted gates:
19 changes: 16 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ permissions:

jobs:
windows:
runs-on: windows-latest
name: windows-${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
os: [windows-2022, windows-2025]

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.0
Expand Down Expand Up @@ -50,15 +55,23 @@ jobs:
$json = ./artifacts/win-x64/portcve.exe snapshot --no-firewall 2>$null | ConvertFrom-Json
if ($json.schema_version -ne 1) { throw 'Unexpected snapshot schema.' }

- name: Verify authorized remote loopback workflow
shell: powershell
run: ./scripts/Test-RemoteHostIntegration.ps1 -SkipBuild -PortCVEPath ./artifacts/win-x64/portcve.exe

- name: Verify daily-workflow performance budgets
shell: powershell
run: ./scripts/Test-Performance.ps1 -PortCVEPath ./artifacts/win-x64/portcve.exe -LocalIterations 5 -RemotePortCount 256 -EnforceBudgets

- name: Upload test results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0
with:
name: test-results
name: test-results-${{ matrix.os }}
path: TestResults/

- name: Upload smoke artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0
with:
name: portcve-win-x64
name: portcve-win-x64-${{ matrix.os }}
path: artifacts/win-x64/portcve.exe
47 changes: 47 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: CodeQL

on:
push:
branches: [main]
pull_request:
schedule:
- cron: '23 3 * * 1'
workflow_dispatch:

permissions:
contents: read
security-events: write

concurrency:
group: codeql-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
csharp:
name: csharp-windows-2025
runs-on: windows-2025
timeout-minutes: 20

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.0

- uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: 10.0.101

- name: Initialize CodeQL
uses: github/codeql-action/init@c4dd10e44af883a891fe31ced449bcb4a6728b9b # v3.37.6
with:
languages: csharp
build-mode: manual

- name: Restore locked dependencies
run: dotnet restore PortCVE.sln --locked-mode

- name: Build for analysis
run: dotnet build PortCVE.sln -c Release --no-restore

- name: Analyze
uses: github/codeql-action/analyze@c4dd10e44af883a891fe31ced449bcb4a6728b9b # v3.37.6
with:
category: /language:csharp
47 changes: 42 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ jobs:
- name: Test
run: dotnet test PortCVE.sln -c Release --no-build --logger "trx;LogFileName=release-tests.trx" --results-directory TestResults

- name: Test installer lifecycle under Windows PowerShell 5.1
shell: powershell
run: .\scripts\tests\Test-Installer.ps1

- name: Publish unsigned candidate
run: dotnet publish src/PortCVE/PortCVE.csproj -c Release -r win-x64 --self-contained true --no-build --no-restore -o artifacts/unsigned

Expand Down Expand Up @@ -137,10 +141,6 @@ jobs:
permissions:
contents: read
env:
ES_USERNAME: ${{ secrets.ES_USERNAME }}
ES_PASSWORD: ${{ secrets.ES_PASSWORD }}
CREDENTIAL_ID: ${{ secrets.CREDENTIAL_ID }}
ES_TOTP_SECRET: ${{ secrets.ES_TOTP_SECRET }}
EXPECTED_SIGNER_SUBJECT: ${{ vars.EXPECTED_SIGNER_SUBJECT }}

steps:
Expand All @@ -158,6 +158,11 @@ jobs:

- name: Fail closed on missing signing configuration
shell: pwsh
env:
ES_USERNAME: ${{ secrets.ES_USERNAME }}
ES_PASSWORD: ${{ secrets.ES_PASSWORD }}
CREDENTIAL_ID: ${{ secrets.CREDENTIAL_ID }}
ES_TOTP_SECRET: ${{ secrets.ES_TOTP_SECRET }}
run: |
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
Expand Down Expand Up @@ -433,11 +438,43 @@ jobs:
Copy-Item -LiteralPath schema -Destination (Join-Path $packageRoot 'schema') -Recurse

$zipName = "portcve-$($env:RELEASE_TAG)-win-x64.zip"
Compress-Archive -Path (Join-Path $packageRoot '*') -DestinationPath (Join-Path $releaseRoot $zipName)
$zipPath = Join-Path $releaseRoot $zipName
Compress-Archive -Path (Join-Path $packageRoot '*') -DestinationPath $zipPath
Copy-Item -LiteralPath artifacts/signed/portcve.exe -Destination $releaseRoot
Copy-Item -LiteralPath artifacts/signed/install.ps1 -Destination $releaseRoot
Copy-Item -LiteralPath artifacts/signed/SIGNING-METADATA.json -Destination $releaseRoot

Add-Type -AssemblyName System.IO.Compression
Add-Type -AssemblyName System.IO.Compression.FileSystem
$archive = [IO.Compression.ZipFile]::OpenRead($zipPath)
try {
$zipExecutables = @($archive.Entries | Where-Object {
[StringComparer]::Ordinal.Equals($_.FullName.Replace('\', '/'), 'portcve.exe')
})
if ($zipExecutables.Count -ne 1) {
throw "Portable ZIP must contain exactly one root portcve.exe; found $($zipExecutables.Count)."
}
$zipStream = $zipExecutables[0].Open()
$sha256 = [Security.Cryptography.SHA256]::Create()
try {
$zipExecutableHash = ([BitConverter]::ToString($sha256.ComputeHash($zipStream))).Replace('-', '').ToLowerInvariant()
}
finally {
$sha256.Dispose()
$zipStream.Dispose()
}
}
finally {
$archive.Dispose()
}

$signedExecutableHash = (Get-FileHash -LiteralPath artifacts/signed/portcve.exe -Algorithm SHA256).Hash.ToLowerInvariant()
$signingMetadata = Get-Content -LiteralPath artifacts/signed/SIGNING-METADATA.json -Raw | ConvertFrom-Json
if (-not [StringComparer]::Ordinal.Equals($zipExecutableHash, $signedExecutableHash) -or
-not [StringComparer]::Ordinal.Equals([string]$signingMetadata.artifact.sha256, $signedExecutableHash)) {
throw 'Portable ZIP, standalone executable, and signing metadata do not contain the exact same signed executable.'
}

$checksumTargets = @(Get-ChildItem -LiteralPath $releaseRoot -File | Sort-Object Name)
$checksumLines = @($checksumTargets | ForEach-Object {
$hash = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
Expand Down
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@ All notable changes will be documented here. The project follows semantic versio

- Renamed the project, executable, namespaces, schemas, scripts, and release artifacts from BindWitness (`bindwitness`) to PortCVE (`portcve`); no behavior changed as part of the rename.
- Added `scan` for offline known-advisory matching against immutable local Docker image IDs and explicit local SBOMs, with a versioned JSON schema, redaction, database-freshness evidence, and `--strict`/`--fail-on` exit gates.
- Added explicit `db status` and `db update` commands for an externally installed, locally validated Trivy executable; scans remain offline and never install or update the engine or advisory database implicitly.
- Hardened the Trivy boundary with local non-reparse cache/SBOM/temp validation, inherited environment scrubbing, strict result parsing, bounded process termination, and guarded cleanup.
- Added `scan-host` for explicitly authorized, rate-limited TCP host/CIDR discovery, protocol-bound greeting/HTTP/TLS fingerprinting, privacy-reduced remote JSON, and bounded safe-active HTTP/TLS posture checks.
- Added exact catalog-backed banner identities for Dropbear SSH, ProFTPD, vsftpd, and Exim, while retaining unresolved results for headers, ports, ambiguous banners, and unsupported versions.
- Added explicit-online, catalog-backed NVD correlation with provenance-bound identities, preserved applicability conditions and enrichment status, candidate-only wording, and process-wide rate limiting.
- Added import-only Nmap XML and Nuclei JSONL normalization with local non-reparse inputs, bounded parsers, source hashing, versioned JSON, and no scanner/template execution.
- Added a file-backed, self-verifying PowerShell installer template and a fail-closed release workflow that signs and independently verifies both `portcve.exe` and `install.ps1`.
- Added receipt-bound managed update, exact-version rollback, offline uninstall, guarded user-`PATH` changes, and transactional restoration tests; portable release ZIPs remain side-effect free.
- Added cryptographic RFC 3161 token decoding, signer-info imprint binding, trusted TSA matching, full-SHA GitHub Actions pinning, release checksums, metadata, and provenance attestation.
- Live-validated Docker TCP/UDP correlation and the offline vulnerability path; see `docs/validation.md` for dated evidence and claim boundaries.
- Expanded CI to fresh Windows Server 2022 and 2025 runners with live loopback remote-assessment and enforceable performance budgets, plus local socket-churn and Docker-forwarding validation.
- Added a daily-use runbook and structured privacy-aware issue and pull-request templates for expert feedback.
- Live-validated Docker TCP/UDP correlation, the offline vulnerability path, and authorized adaptive HTTP discovery on a random loopback port; see `docs/validation.md` and `docs/remote-live-validation.md` for dated evidence and claim boundaries.

## 0.1.0-alpha.1 - 2026-08-09

Expand Down
11 changes: 8 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,28 @@ dotnet restore PortCVE.sln --locked-mode
dotnet format PortCVE.sln --verify-no-changes --no-restore
dotnet build PortCVE.sln -c Release --no-restore
dotnet test PortCVE.sln -c Release --no-build
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\tests\Test-Installer.ps1
```

NuGet lockfiles are committed. Keep them synchronized with intentional package changes; `--locked-mode` should fail unexpected dependency-resolution drift.

When changing native collection, also compare a live fixture against structured `Get-NetTCPConnection -State Listen` and `Get-NetUDPEndpoint` output. Do not use localized `netstat` text as a parser or test oracle.

For Docker correlation changes, build Release and run `scripts\Test-DockerIntegration.ps1 -ValidateLockCheck`. The script may pull `alpine:3.22` and creates/removes a labeled test container. Its default publications are loopback-only; `-AllowWildcardUdp` intentionally exposes the UDP echo fixture on `0.0.0.0` for the duration of the test.
For Docker correlation changes, build Release and run `scripts\Test-DockerIntegration.ps1 -ValidateLockCheck -ValidateRemoteScan`. The script may pull `alpine:3.22` and creates/removes a labeled test container. Its default publications are loopback-only; `-AllowWildcardUdp` intentionally exposes the UDP echo fixture on `0.0.0.0` for the duration of the test.

For remote-scanner changes, run the loopback-only `scripts\Test-RemoteHostIntegration.ps1` harness. Never use a public, third-party, or local-network target as a release test without explicit authorization from its owner. Run `scripts\Test-Performance.ps1 -EnforceBudgets` when changing collection, planning, concurrency, or serialization paths.

When changing firewall reasoning, add tests for both the intended match and a near-miss. An unavailable or unsupported predicate must reduce confidence; it must not silently become `allow` or `block`.

## Design rules

- Keep v1 read-only.
- Keep local collection and all assessment workflows non-destructive.
- Require explicit authorization for active network assessment, preserve rate/concurrency/time/evidence caps, and never add an unlimited mode.
- Preserve JSON stdout; diagnostics belong on stderr.
- Never collect process environment variables.
- Do not add command-line collection without a separate privacy design and explicit opt-in.
- Keep external reachability `unknown` unless a future external verifier actually tests it.
- Keep local listener reachability conservative. A successful `scan-host` TCP connection proves only that exact tested path and observation time, not Internet-wide reachability or exploitability.
- Do not infer CPEs or vulnerability matches from a port number, filename, or untrusted HTTP header.
- A collector failure is evidence degradation, not proof that an endpoint disappeared.
- Add or update schema fixtures for compatibility changes.
- Isolate Windows interop from correlation and diff logic.
Expand Down
Loading
Loading