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
41 changes: 41 additions & 0 deletions .bestpractices.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "https://bestpractices.coreinfrastructure.org/projects.schema.json",
"_comment": "OpenSSF Best Practices self-assessment for RandomCodeSpace/snipIT (RAN-54). Project is registered at https://www.bestpractices.dev/en/projects/12647 — `passing` answers are reflected here in-repo and updated in lockstep with PRs that touch a relevant surface (build, test, vulnerability reporting, release, license, contribution docs, crypto, access control). Marking each criterion `Met` on the project page itself requires a board admin OAuth login.",
"project_id": 12647,
"name": "snipIT",
"description": "A professional snipping tool for Windows 11 written in pure PowerShell 7.5+ on .NET 9. Hover-to-highlight smart capture, magnifier loupe, floating widget, system tray, chromeless Fluent preview with a full annotation editor — single script, zero external dependencies, no admin elevation.",
"homepage_url": "https://github.com/RandomCodeSpace/snipIT",
"repo_url": "https://github.com/RandomCodeSpace/snipIT",
"license": "MIT",
"level": "passing",
"status": {
"basics": "self-assessed-passing",
"change_control": "self-assessed-passing",
"reporting": "self-assessed-passing",
"quality": "self-assessed-passing",
"security": "self-assessed-passing",
"analysis": "self-assessed-passing"
},
"evidence": {
"vulnerability_report_process": "SECURITY.md",
"engineering_standards": "shared/runbooks/engineering-standards.md",
"license_file": "LICENSE",
"build_reproducible": "pwsh -NoProfile -File ./Test-SnipIT.ps1 (single-file script; no compile/build step — the .ps1 is the deliverable)",
"ci_workflow": ".github/workflows/test.yml",
"code_scanning": "GitHub repo setting (secret scanning + push protection enabled). Code-scanning SAST is provided by Semgrep in `.github/workflows/security.yml` (CodeQL is not enabled — there is no CodeQL pack for PowerShell today; Semgrep is the OSS-native equivalent per `shared/runbooks/engineering-standards.md` §9b).",
"supply_chain_scorecard": ".github/workflows/scorecard.yml",
"dependency_updates": ".github/dependabot.yml",
"signed_commits": "scripts/setup-git-signed.sh",
"secret_scanning": "GitHub repo setting (secret_scanning + push_protection enabled)",
"static_analysis": "PSScriptAnalyzer (Error severity gate) + Semgrep (p/security-audit + p/owasp-top-ten) — `.github/workflows/security.yml`",
"vulnerability_scanning": "Trivy filesystem scan (HIGH/CRITICAL gating) + Dependabot security updates — `.github/workflows/security.yml` + repo settings",
"duplication_check": "jscpd 4 (--threshold 3 --min-tokens 100, format powershell) — `.github/workflows/security.yml`",
"secret_scan_history": "Gitleaks (full git history) — `.github/workflows/security.yml`",
"sbom": "anchore/sbom-action (SPDX + CycloneDX) — `.github/workflows/security.yml`"
},
"audit": {
"self_assessment_date": "2026-04-26",
"self_assessment_author": "TechLead (RAN-54)",
"registration_status": "https://www.bestpractices.dev/en/projects/12647 — in_progress; board admin OAuth login required to flip remaining criteria to Met."
}
}
34 changes: 34 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Dependabot configuration for snipIT.
# Docs: https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
#
# Strategy:
# * weekly cadence — keeps the noise floor low while still catching CVEs early
# * grouped updates per ecosystem so PR fan-out stays manageable
# * security updates fire whenever needed regardless of the weekly slot
#
# RAN-54 AC #4 (Dependabot, weekly, grouped). snipIT has no Maven / npm / pip
# manifests today — it is a single .ps1 with zero external runtime deps. The
# only ecosystem with managed dependencies is GitHub Actions (CI workflows).
# Repo-level "Dependabot security updates" is enabled separately via gh api;
# the version-updates below cover routine bumps for the actions we pin.

version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "08:00"
timezone: "Etc/UTC"
open-pull-requests-limit: 5
labels:
- "type:dependencies"
- "area:ci"
commit-message:
prefix: "chore(actions)"
include: "scope"
groups:
actions:
patterns:
- "*"
66 changes: 66 additions & 0 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# OpenSSF Scorecard supply-chain analysis.
# RAN-54 AC #6. Best-effort target — no hard numeric floor; Scorecard does not gate merge.
# Docs: https://github.com/ossf/scorecard-action

name: Scorecard supply-chain security

on:
push:
branches: [main]
schedule:
# Mondays 06:00 UTC
- cron: "0 6 * * 1"
workflow_dispatch:

# Restrict the default GITHUB_TOKEN to read-only; the steps below request the
# narrow scopes they actually need.
permissions: read-all

jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Required for upload to the code-scanning Security tab.
security-events: write
# Required to read OIDC token for publish_results.
id-token: write
# Default scopes for actions/checkout.
contents: read
actions: read

steps:
- name: Harden runner egress
# step-security/harden-runner v2.19.0
uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40
with:
egress-policy: audit

- name: Checkout code
# actions/checkout v6.0.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
with:
persist-credentials: false

- name: Run Scorecard analysis
# ossf/scorecard-action v2.4.3
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a
with:
results_file: results.sarif
results_format: sarif
# Publish the results so they appear on the public Scorecard dashboard.
publish_results: true

- name: Upload Scorecard SARIF (artifact)
# actions/upload-artifact v7.0.1
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: scorecard-sarif
path: results.sarif
retention-days: 5

- name: Upload SARIF to GitHub code-scanning
# github/codeql-action/upload-sarif v3.35.2
uses: github/codeql-action/upload-sarif@ce64ddcb0d8d890d2df4a9d1c04ff297367dea2a
with:
sarif_file: results.sarif
191 changes: 191 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
name: Security (OSS-CLI)
# OSS-CLI security stack per RAN-54 AC §3 — replicates codeiq RAN-46 path B
# (Sonar / CodeQL / OWASP-DC excluded by board ruling), language-adapted for
# this single-file PowerShell project.
#
# Six independent jobs — fail-fast off so all signals surface on a single run.
# All actions SHA-pinned per Scorecard `Pinned-Dependencies`. Top-level
# `permissions: read-all` per Scorecard `Token-Permissions`; jobs scope up
# only when needed (gitleaks needs full git history; sbom job uploads).
#
# PowerShell-variant deltas vs the codeiq Java stack:
# - OSV-Scanner job is omitted: snipIT is a single .ps1 with zero external
# dependencies (no npm / Maven / pip lockfile). Trivy filesystem scan
# remains the SCA channel for any future deps; Dependabot covers the
# GitHub Actions ecosystem.
# - Semgrep packs: `p/security-audit` + `p/owasp-top-ten` only. Semgrep
# has no first-party PowerShell pack today; the language-specific gate
# is PSScriptAnalyzer (added below) — codeiq-equivalent of `p/java`.
# - jscpd format set to `powershell`, scoped to the three .ps1 files.
# - Added `psscriptanalyzer` job — language lint per
# `shared/runbooks/engineering-standards.md` (PowerShell variant).
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '21 4 * * 1' # Mondays 04:21 UTC — catch newly-disclosed CVEs

permissions: read-all

jobs:
trivy:
name: Trivy (filesystem + container scan)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: fs
scan-ref: .
severity: HIGH,CRITICAL
exit-code: '1'
ignore-unfixed: true

semgrep:
name: Semgrep (SAST)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.12'
- name: Install semgrep
run: python -m pip install --quiet --upgrade pip semgrep
- name: Run semgrep (security-audit + owasp-top-ten)
# No `p/powershell` pack ships in Semgrep registry — language-level
# findings come from PSScriptAnalyzer below. The two packs run here
# are language-agnostic (path traversal, dangerous deserialization,
# OWASP top-ten patterns) and still flag issues in .ps1 source.
run: |
semgrep scan \
--error \
--config p/security-audit \
--config p/owasp-top-ten \
--severity ERROR \
--metrics off

psscriptanalyzer:
name: PSScriptAnalyzer (Error severity gate)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Install PSScriptAnalyzer
shell: pwsh
run: Install-Module PSScriptAnalyzer -Scope CurrentUser -Force -SkipPublisherCheck
- name: Surface warnings (non-blocking)
shell: pwsh
run: |
Import-Module PSScriptAnalyzer
$w = Invoke-ScriptAnalyzer -Path ./SnipIT.ps1 -Severity Warning
Write-Host "Warning count: $((@($w)).Count)"
$w | Group-Object RuleName | Sort-Object Count -Descending |
Select-Object Count, Name | Format-Table -AutoSize | Out-String | Write-Host
- name: Fail on Error-severity findings
shell: pwsh
run: |
Import-Module PSScriptAnalyzer
$errs = Invoke-ScriptAnalyzer -Path ./SnipIT.ps1 -Severity Error
$count = (@($errs)).Count
Write-Host "Error count: $count"
if ($count -gt 0) {
$errs | Format-Table -AutoSize Severity, RuleName, Line, Message |
Out-String | Write-Host
exit 1
}

gitleaks:
name: Gitleaks (secret scan)
runs-on: ubuntu-latest
permissions:
contents: read
env:
GITLEAKS_VERSION: 8.30.1
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
with:
fetch-depth: 0
# The official `gitleaks/gitleaks-action` requires a paid license for
# GitHub organisations. The underlying gitleaks CLI is MIT-licensed and
# free; install it directly from the upstream release. Using the
# preinstalled `gh` CLI avoids any external `curl`/`wget`.
- name: Install gitleaks
run: |
gh release download "v${GITLEAKS_VERSION}" \
--repo gitleaks/gitleaks \
--pattern "gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
--output gitleaks.tar.gz
tar -xzf gitleaks.tar.gz gitleaks
chmod +x gitleaks
- name: Run gitleaks (full git history)
run: ./gitleaks detect --source . --redact --no-banner --exit-code 1

jscpd:
name: jscpd (duplication < 3% on touched code)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: '20'
- run: |
# snipIT is three PowerShell files at repo root:
# - SnipIT.ps1 (production)
# - Test-SnipIT.ps1 (headless tests)
# - Test-SnipIT-Interactive.ps1 (interactive tests)
#
# Production-only scope per AC interpretation (matches codeiq
# convention where tests are excluded from the dup gate). Tests
# share fixture / Assert-* shape by design.
#
# `--min-tokens 100` is calibrated to PowerShell's medium verbosity
# floor (Add-Type/[CmdletBinding] blocks, `param()` headers, P/Invoke
# signatures). The Java floor of 200 over-suppresses; the jscpd
# default of 50 surfaces param-block boilerplate as false-positive
# clones. 100 captures real duplicate logic without flagging the
# 8–15 line `[CmdletBinding()]` + `param(...)` openers many
# functions share.
npx --yes jscpd@4 \
--threshold 3 \
--min-tokens 100 \
--reporters consoleFull \
--format "powershell" \
--ignore "**/Test-SnipIT*.ps1,**/docs/**" \
./SnipIT.ps1

sbom:
name: SBOM (SPDX + CycloneDX)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Generate SPDX SBOM
uses: anchore/sbom-action@fc46e51fd3cb168ffb36c6d1915723c47db58abb # v0.17.7
with:
format: spdx-json
output-file: sbom.spdx.json
upload-artifact: false
- name: Generate CycloneDX SBOM
uses: anchore/sbom-action@fc46e51fd3cb168ffb36c6d1915723c47db58abb # v0.17.7
with:
format: cyclonedx-json
output-file: sbom.cdx.json
upload-artifact: false
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v4.6.2
with:
name: sbom
path: |
sbom.spdx.json
sbom.cdx.json
retention-days: 90
Loading
Loading