chore(tooling): add AI security review exporter

This commit is contained in:
Ochenstarik 2026-08-18 14:46:09 +07:00
parent bc2b658570
commit 1495067501
4 changed files with 538 additions and 0 deletions

View file

@ -0,0 +1,19 @@
# TASK-2026-08-18-консолидация-рабочего-пространства
**Дата:** 2026-08-18
**От:** владелец проекта (через Claude)
**Статус:** in-progress
## Цель
Склонировать 17 репозиториев ochenstarik-ui в E:\Agent projects и проверить структуру agents/ в каждом.
## Репозитории
business-platform, kagent, hermes-config-backup, server-monitor-manager,
randomayzer, trade-signal-platform, trading-knowledge-base,
finance-telegram-bot, agent-control-center, agent-control-center-server,
lightweight-server, safetest-platform, agent-engineering-quality-system,
hermes-lossless-context-layer, hermes-task-inbox, singbox-ai-router,
trade-signal-bot-legacy
## Отчёт
E:\Agent projects\done\2026-08-18-консолидация-рабочего-пространства.md

View file

@ -0,0 +1,18 @@
# Task Done: Developer Tooling — Add Security Review Exporter
**Status:** DONE
**Assigned to:** Antigravity (@orchestrator)
**Date:** 2026-08-18
**Base Commit:** `bc2b658`
## Accomplished
1. Created `tools/export-review.ps1` supporting:
- Full snapshot mode (`git archive HEAD`) with `REVIEW_CONTEXT.md` injection.
- Diff mode (`-Diff`, `-Base <SHA>`) with `REVIEW_DIFF.patch`, `REVIEW_CHANGED_FILES.txt`, changed source files in tree structure, all tests in `tests/*`, `prisma/schema.prisma`, `package.json`, `docs/*`.
- Security safety check against tracked secrets (`.env`, `*.pem`, `*.key`, `credentials.json`, `secrets.json`).
- Dirty worktree handling with warning and `-RequireClean` strict guard.
- Output summary table with file count, size, commit SHA, mode.
- `-CopyPrompt` to automatically put Russian reviewer prompt into Windows clipboard.
- Compatible with Windows PowerShell 5.1 and PowerShell 7+, handles paths with spaces.
2. Created user documentation in `docs/AI_REVIEW_EXPORT.md`.
3. Verified both FULL and DIFF exports, tested `-RequireClean`, tested Windows PowerShell 5.1 and PowerShell 7.

91
docs/AI_REVIEW_EXPORT.md Normal file
View file

@ -0,0 +1,91 @@
# Randomayzer AI Security Review Exporter
`tools/export-review.ps1` — утилита для создания автономных snapshot и diff пакетов репозитория для внешних AI security reviewers, у которых нет прямого доступа к GitHub репозиторию или локальной файловой системе.
---
## 1. Возможности
- **Full Snapshot Mode (по умолчанию)**: Создает полный ZIP-архив репозитория на основе `git archive HEAD`.
- **Diff Mode (`-Diff`)**: Создает компактный пакет изменений между базовым коммитом (`-Base`) и `HEAD`, включая патч, список измененных файлов, измененные исходники, полный набор тестов, схему базы данных и документацию.
- **Review Context (`REVIEW_CONTEXT.md`)**: В каждый архив автоматически встраивается файл метаданных с полным SHA, веткой, временной меткой, состоянием working tree и статистикой `git log`.
- **Secret Safety Check**: Проверяет tracked-файлы на наличие потенциальных секретов (`.env`, `*.pem`, `*.key`, `credentials.json`, `secrets.json`) и блокирует экспорт при обнаружении.
- **Dirty Worktree Warning / Guard**: Предупреждает о наличии незакоммиченных изменений (снапшот собирается строго из `HEAD`) или прерывает выполнение при флаге `-RequireClean`.
- **Zero External Dependencies**: Работает на Windows PowerShell 5.1 и PowerShell 7+ с использованием стандартных API .NET (`System.IO.Compression`). Сторонние архиваторы (7-Zip, WinRAR) не требуются.
- **Корректная работа с пробелами в путях** (например, `E:\Agent projects\Randomayzer`).
---
## 2. Быстрый старт
Запуск из корня проекта:
### Полный снимок репозитория (Full Snapshot)
```powershell
.\tools\export-review.ps1
```
Архив сохраняется по умолчанию в папку `Desktop\Randomayzer Reviews\randomayzer-review-<shortSHA>.zip`.
### Diff последнего коммита (`HEAD^` vs `HEAD`)
```powershell
.\tools\export-review.ps1 -Diff
```
Создает архив `randomayzer-diff-review-<shortSHA>.zip`, содержащий:
- `REVIEW_CONTEXT.md`
- `REVIEW_DIFF.patch`
- `REVIEW_CHANGED_FILES.txt`
- Измененные файлы в структуре каталогов проекта
- Полный набор тестов `tests/*`
- Схему `prisma/schema.prisma`, `package.json`, `.env.example`, документацию `docs/*`
### Diff от определенного коммита
```powershell
.\tools\export-review.ps1 -Diff -Base b5467f6
```
---
## 3. Параметры
| Параметр | Тип | Описание |
|---|---|---|
| `-Diff` | Switch | Включает режим формирования diff-пакета вместо полного снимка. |
| `-Base <SHA>` | String | Базовый коммит для сравнения в режиме `-Diff` (по умолчанию `HEAD^`). |
| `-OutputDir <Path>` | String | Пользовательский путь для сохранения ZIP-файлов (по умолчанию `Desktop\Randomayzer Reviews\`). |
| `-RequireClean` | Switch | Завершает работу с ошибкой, если в working tree есть незакоммиченные файлы. |
| `-AllowSensitiveTrackedFiles` | Switch | Отключает блокировку экспорта при обнаружении подозрительных tracked-файлов. |
| `-CopyPrompt` | Switch | Автоматически копирует в буфер обмена Windows текст задания для security reviewer. |
---
## 4. Примеры использования
### Экспорт со строгой проверкой чистоты рабочей директории
```powershell
.\tools\export-review.ps1 -RequireClean
```
### Экспорт в пользовательскую директорию с копированием промпта
```powershell
.\tools\export-review.ps1 -Diff -Base bc2b658 -OutputDir "D:\Audits" -CopyPrompt
```
После выполнения в буфере обмена будет готов стандартизированный промпт:
> «В приложенном архиве snapshot Randomayzer на commit `<SHA>`.
> Используй архив как source of truth.
> Не используй GitHub HEAD вместо него.
> Выполни ранее выданное security review задание.»
---
## 5. Гарантии безопасности архива
1. **Изоляция от локального мусора**: В ZIP-архив **никогда не попадают**:
- `.git`
- `node_modules`
- `.next` сборки и кэш
- Локальные незакоммиченные файлы
- Локальные `.env`, `.env.local`
- IDE-файлы (`.vscode`, `.idea`)
2. **Точность снимка**: Файлы извлекаются напрямую из Git-объектов коммита (`git archive` для Full или `git show HEAD:<path>` для Diff), поэтому локальные изменения в рабочей директории не искажают снимок.
3. **Защита от утечки секретов**: Сканирование имен файлов блокирует экспорт при случайном попадании приватных ключей или файлов конфигурации в индекс Git.

410
tools/export-review.ps1 Normal file
View file

@ -0,0 +1,410 @@
<#
.SYNOPSIS
Exports Randomayzer repository snapshots and diff packages for AI Security Reviewers.
.DESCRIPTION
Creates clean, deterministic ZIP archives of tracked repository code at a specific commit.
Guarantees that node_modules, local .env files, untracked artifacts, and IDE caches are never included.
Includes automated REVIEW_CONTEXT.md, diffs, and verification metrics.
.PARAMETER Diff
Generate a diff review package between Base commit and HEAD instead of a full snapshot.
.PARAMETER Base
Base commit for diff mode (defaults to HEAD^).
.PARAMETER OutputDir
Output directory for the generated ZIP file (defaults to Desktop\Randomayzer Reviews\).
.PARAMETER RequireClean
If specified, aborts the export if the git working tree has uncommitted changes.
.PARAMETER AllowSensitiveTrackedFiles
Bypasses the safety abort when potentially sensitive file patterns are detected in tracked git files.
.PARAMETER CopyPrompt
Copies the standardized security reviewer instructions to the Windows clipboard upon completion.
.EXAMPLE
.\tools\export-review.ps1
.EXAMPLE
.\tools\export-review.ps1 -Diff
.EXAMPLE
.\tools\export-review.ps1 -Diff -Base bc2b658 -CopyPrompt
.EXAMPLE
.\tools\export-review.ps1 -OutputDir "D:\Audits" -RequireClean
#>
[CmdletBinding()]
param(
[switch]$Diff,
[string]$Base,
[string]$OutputDir,
[switch]$RequireClean,
[switch]$AllowSensitiveTrackedFiles,
[switch]$CopyPrompt
)
$ErrorActionPreference = "Stop"
# 1. Resolve Git repository root and metadata
$gitRootRaw = git rev-parse --show-toplevel 2>$null
if (-not $gitRootRaw -or $LASTEXITCODE -ne 0) {
Write-Error "Not inside a git repository or git is unavailable."
exit 1
}
$gitRoot = [System.IO.Path]::GetFullPath($gitRootRaw.Trim())
$headSha = (git rev-parse HEAD 2>$null).Trim()
$shortSha = (git rev-parse --short HEAD 2>$null).Trim()
$branch = (git rev-parse --abbrev-ref HEAD 2>$null).Trim()
$remoteUrl = (git config --get remote.origin.url 2>$null)
if (-not $remoteUrl) {
$remoteUrl = "https://github.com/ochenstarik-ui/randomayzer"
} else {
$remoteUrl = $remoteUrl.Trim()
}
# 2. Output directory resolution (relative to current PowerShell location)
if (-not $OutputDir) {
$desktopPath = [Environment]::GetFolderPath([Environment+SpecialFolder]::Desktop)
$OutputDir = [System.IO.Path]::Combine($desktopPath, "Randomayzer Reviews")
} else {
if (-not [System.IO.Path]::IsPathRooted($OutputDir)) {
$OutputDir = [System.IO.Path]::Combine((Get-Location).Path, $OutputDir)
}
$OutputDir = [System.IO.Path]::GetFullPath($OutputDir)
}
if (-not (Test-Path -LiteralPath $OutputDir)) {
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
}
# 3. Dirty worktree check
$statusOutput = git status --porcelain
$isDirty = [bool]($statusOutput -and ($statusOutput.Trim().Length -gt 0))
if ($isDirty) {
if ($RequireClean) {
Write-Error "Working tree contains uncommitted changes. -RequireClean was specified. Aborting export."
exit 1
} else {
Write-Warning "Working tree contains uncommitted changes. Snapshot represents committed HEAD only."
}
}
$dirtyText = if ($isDirty) { "YES" } else { "NO" }
# 4. Secret safety check on tracked files
$trackedFiles = git ls-tree -r --name-only HEAD
$sensitiveMatches = @()
$sensitiveRegex = '(?i)(^|/|\\)(\.env(\.(?!example$).*)?|\.env$|.*\.pem$|.*\.key$|credentials\.json$|secrets\.json$|.*\.p12$|.*\.pfx$)'
foreach ($file in $trackedFiles) {
if ($file -match $sensitiveRegex -and $file -notmatch '(?i)\.env\.example') {
$sensitiveMatches += $file
}
}
if ($sensitiveMatches.Count -gt 0) {
Write-Warning "=================================================="
Write-Warning "POTENTIALLY SENSITIVE FILES DETECTED IN TRACKED GIT:"
foreach ($sf in $sensitiveMatches) {
Write-Warning " - $sf"
}
Write-Warning "=================================================="
if (-not $AllowSensitiveTrackedFiles) {
Write-Error "Export aborted to prevent secret leakage. Pass -AllowSensitiveTrackedFiles to override."
exit 1
}
}
# Ensure .NET compression assemblies are loaded
Add-Type -AssemblyName System.IO.Compression
Add-Type -AssemblyName System.IO.Compression.FileSystem
$timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss zzz")
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
# 5. Export generation
if (-not $Diff) {
# ── FULL SNAPSHOT MODE ──
$modeName = "FULL"
$zipFileName = "randomayzer-review-$shortSha.zip"
$zipFilePath = [System.IO.Path]::Combine($OutputDir, $zipFileName)
if (Test-Path -LiteralPath $zipFilePath) {
Remove-Item -LiteralPath $zipFilePath -Force
}
# Build clean archive directly from git HEAD
Push-Location $gitRoot
try {
git archive HEAD --format=zip --output="$zipFilePath"
if ($LASTEXITCODE -ne 0) {
Write-Error "git archive failed with exit code $LASTEXITCODE"
exit 1
}
} finally {
Pop-Location
}
# Generate metadata context
$gitLogStat = (git log -1 --stat HEAD) -join "`n"
$reviewContextLines = @(
"# Randomayzer Review Snapshot",
"",
"- **Full commit SHA:** $headSha",
"- **Short SHA:** $shortSha",
"- **Branch:** $branch",
"- **Generated at:** $timestamp",
"- **Repository:** $remoteUrl",
"- **Snapshot mode:** FULL",
"- **Working tree dirty at export time:** $dirtyText",
"",
"## Source of Truth",
"The files in this archive are the exact tracked repository snapshot for the commit shown above.",
"",
"## Reviewer Instructions",
"Do not use GitHub HEAD as source of truth.",
"Review the attached snapshot.",
"",
"## Recent Commit Log",
'```',
$gitLogStat,
'```'
)
$reviewContext = $reviewContextLines -join "`n"
# Inject REVIEW_CONTEXT.md into ZIP
$archive = [System.IO.Compression.ZipFile]::Open($zipFilePath, [System.IO.Compression.ZipArchiveMode]::Update)
try {
$existingEntry = $archive.GetEntry("REVIEW_CONTEXT.md")
if ($existingEntry) {
$existingEntry.Delete()
}
$entry = $archive.CreateEntry("REVIEW_CONTEXT.md", [System.IO.Compression.CompressionLevel]::Optimal)
$stream = $entry.Open()
try {
$bytes = $utf8NoBom.GetBytes($reviewContext)
$stream.Write($bytes, 0, $bytes.Length)
} finally {
$stream.Dispose()
}
} finally {
$archive.Dispose()
}
} else {
# ── DIFF MODE ──
$modeName = "DIFF"
$zipFileName = "randomayzer-diff-review-$shortSha.zip"
$zipFilePath = [System.IO.Path]::Combine($OutputDir, $zipFileName)
if (Test-Path -LiteralPath $zipFilePath) {
Remove-Item -LiteralPath $zipFilePath -Force
}
if (-not $Base) {
$Base = "HEAD^"
}
$baseShaRaw = git rev-parse --verify "$Base" 2>$null
if (-not $baseShaRaw -or $LASTEXITCODE -ne 0) {
Write-Error "Invalid base commit: '$Base'"
exit 1
}
$baseSha = $baseShaRaw.Trim()
$baseShortSha = (git rev-parse --short $baseSha 2>$null).Trim()
# Generate diff and list of changed files
$diffPatch = (git diff $baseSha HEAD) -join "`n"
$changedFiles = git diff --name-only $baseSha HEAD
$changedFilesList = @()
if ($changedFiles) {
$changedFilesList = @($changedFiles) | Where-Object { $_ -and $_.Trim().Length -gt 0 }
}
$changedFilesText = ($changedFilesList -join "`n")
# Determine files to include in Diff package:
# 1. Changed files present in HEAD (exclude deleted files)
# 2. All tracked test files (tests/*)
# 3. Security, config, and documentation context files
$trackedHeadFiles = git ls-tree -r --name-only HEAD
$filesToInclude = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
foreach ($cf in $changedFilesList) {
if ($trackedHeadFiles -contains $cf) {
$filesToInclude.Add($cf) | Out-Null
}
}
# Add all tracked test files for test integrity review
foreach ($tf in $trackedHeadFiles) {
if ($tf -like "tests/*" -or $tf -like "test/*") {
$filesToInclude.Add($tf) | Out-Null
}
}
# Add schema, dependencies, env.example, and documentation
$contextPatterns = @(
"package.json",
"package-lock.json",
"prisma/schema.prisma",
".env.example",
"README.md",
"AGENTS.md",
"GEMINI.md",
"docs/*"
)
foreach ($pattern in $contextPatterns) {
foreach ($hf in $trackedHeadFiles) {
if ($hf -like $pattern) {
# Security filter
if ($hf -match $sensitiveRegex -and $hf -notmatch '(?i)\.env\.example') {
continue
}
$filesToInclude.Add($hf) | Out-Null
}
}
}
# Stage files in temporary directory
$tempDir = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), ("randomayzer_export_" + [System.Guid]::NewGuid().ToString("N")))
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
try {
Push-Location $gitRoot
try {
foreach ($relPath in $filesToInclude) {
$gitRelPath = $relPath -replace '\\', '/'
$destPath = [System.IO.Path]::Combine($tempDir, ($relPath -replace '/', [System.IO.Path]::DirectorySeparatorChar))
$destParent = [System.IO.Path]::GetDirectoryName($destPath)
if (-not (Test-Path -LiteralPath $destParent)) {
New-Item -ItemType Directory -Path $destParent -Force | Out-Null
}
# Extract exact binary content from HEAD
$processInfo = New-Object System.Diagnostics.ProcessStartInfo
$processInfo.FileName = "git"
$processInfo.Arguments = "show `"HEAD:$gitRelPath`""
$processInfo.WorkingDirectory = $gitRoot
$processInfo.UseShellExecute = $false
$processInfo.RedirectStandardOutput = $true
$processInfo.RedirectStandardError = $true
$processInfo.CreateNoWindow = $true
$process = [System.Diagnostics.Process]::Start($processInfo)
$outputStream = [System.IO.File]::Create($destPath)
try {
$process.StandardOutput.BaseStream.CopyTo($outputStream)
} finally {
$outputStream.Dispose()
}
$process.WaitForExit()
}
} finally {
Pop-Location
}
# Write diff artifacts
[System.IO.File]::WriteAllText([System.IO.Path]::Combine($tempDir, "REVIEW_DIFF.patch"), $diffPatch, $utf8NoBom)
[System.IO.File]::WriteAllText([System.IO.Path]::Combine($tempDir, "REVIEW_CHANGED_FILES.txt"), $changedFilesText, $utf8NoBom)
# Generate REVIEW_CONTEXT.md
$gitLogStat = (git log --stat "$baseSha..HEAD") -join "`n"
$reviewContextLines = @(
"# Randomayzer Review Snapshot (Diff Mode)",
"",
"- **Full commit SHA:** $headSha",
"- **Short SHA:** $shortSha",
"- **Branch:** $branch",
"- **Generated at:** $timestamp",
"- **Repository:** $remoteUrl",
"- **Snapshot mode:** DIFF",
"- **Base commit:** $baseSha ($baseShortSha)",
"- **Target commit:** $headSha ($shortSha)",
"- **Working tree dirty at export time:** $dirtyText",
"",
"## Source of Truth",
"The files in this archive are the exact tracked repository snapshot for the commit shown above.",
"This archive contains the diff against base commit, changed files, relevant test suites, schema definitions, and project documentation.",
"",
"## Reviewer Instructions",
"Do not use GitHub HEAD as source of truth.",
"Review the attached snapshot.",
"- Check `REVIEW_DIFF.patch` for the complete patch.",
"- Check `REVIEW_CHANGED_FILES.txt` for the list of modified files.",
"- Inspect the included source and test files.",
"",
"## Recent Commit Log ($baseShortSha..$shortSha)",
'```',
$gitLogStat,
'```'
)
$reviewContext = $reviewContextLines -join "`n"
[System.IO.File]::WriteAllText([System.IO.Path]::Combine($tempDir, "REVIEW_CONTEXT.md"), $reviewContext, $utf8NoBom)
# Compress staged directory
[System.IO.Compression.ZipFile]::CreateFromDirectory($tempDir, $zipFilePath, [System.IO.Compression.CompressionLevel]::Optimal, $false)
} finally {
if (Test-Path -LiteralPath $tempDir) {
Remove-Item -LiteralPath $tempDir -Recurse -Force -ErrorAction SilentlyContinue
}
}
}
# 6. Verify Archive & Display Summary
$zip = [System.IO.Compression.ZipFile]::OpenRead($zipFilePath)
$fileCount = $zip.Entries.Count
$zip.Dispose()
$fileInfo = Get-Item -LiteralPath $zipFilePath
$sizeBytes = $fileInfo.Length
$sizeFormatted = if ($sizeBytes -ge 1MB) {
"{0:N2} MB ({1:N0} bytes)" -f ($sizeBytes / 1MB), $sizeBytes
} elseif ($sizeBytes -ge 1KB) {
"{0:N2} KB ({1:N0} bytes)" -f ($sizeBytes / 1KB), $sizeBytes
} else {
"{0} bytes" -f $sizeBytes
}
Write-Host ""
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host " Randomayzer Review Package Exported Successfully " -ForegroundColor Cyan
Write-Host "==================================================" -ForegroundColor Cyan
Write-Host ("Created: " + $zipFilePath) -ForegroundColor Green
Write-Host ("Commit: " + $headSha + " (" + $shortSha + ")")
Write-Host ("Mode: " + $modeName)
Write-Host ("Files: " + $fileCount)
Write-Host ("Size: " + $sizeFormatted)
Write-Host "==================================================" -ForegroundColor Cyan
# 7. Optional Clipboard Prompt
if ($CopyPrompt) {
$clipboardPromptLines = @(
"В приложенном архиве snapshot Randomayzer на commit $headSha.",
"Используй архив как source of truth.",
"Не используй GitHub HEAD вместо него.",
"Выполни ранее выданное security review задание."
)
$clipboardPrompt = $clipboardPromptLines -join "`n"
try {
if (Get-Command Set-Clipboard -ErrorAction SilentlyContinue) {
Set-Clipboard -Value $clipboardPrompt
Write-Host "Reviewer prompt copied to clipboard." -ForegroundColor Yellow
} else {
Write-Warning "Set-Clipboard is not available in this environment."
}
} catch {
Write-Warning "Could not copy prompt to clipboard: $_"
}
}