diff --git a/README.md b/README.md index ae3b530..00ee62e 100644 --- a/README.md +++ b/README.md @@ -12,22 +12,34 @@ Use $skill-installer to install https://github.com/andydixon/securityaudit-skill Restart Codex after it installs. -Or paste this into Terminal: +Or paste this into Terminal on macOS/Linux: ```bash curl -fsSL https://raw.githubusercontent.com/andydixon/securityaudit-skill/main/install.sh | bash ``` +Or paste this into PowerShell on Windows: + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -Command "[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; Invoke-RestMethod https://raw.githubusercontent.com/andydixon/securityaudit-skill/main/install.ps1 | Invoke-Expression" +``` + Restart Codex after it installs. ## Claude Code install -Paste this into Terminal: +Paste this into Terminal on macOS/Linux: ```bash curl -fsSL https://raw.githubusercontent.com/andydixon/securityaudit-skill/main/install-claude.sh | bash ``` +Or paste this into PowerShell on Windows: + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -Command "[Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; Invoke-RestMethod https://raw.githubusercontent.com/andydixon/securityaudit-skill/main/install-claude.ps1 | Invoke-Expression" +``` + Restart Claude Code if it was already open. ## Codex usage diff --git a/install-claude.ps1 b/install-claude.ps1 new file mode 100644 index 0000000..b389edf --- /dev/null +++ b/install-claude.ps1 @@ -0,0 +1,111 @@ +param( + [string]$RepoSlug = $(if ($env:SECURITYAUDIT_REPO) { $env:SECURITYAUDIT_REPO } else { "andydixon/securityaudit-skill" }), + [string]$Ref = $(if ($env:SECURITYAUDIT_REF) { $env:SECURITYAUDIT_REF } else { "main" }), + [string]$SkillPath = $(if ($env:SECURITYAUDIT_SKILL_PATH) { $env:SECURITYAUDIT_SKILL_PATH } else { "securityaudit" }) +) + +$ErrorActionPreference = "Stop" + +function Say { + param([string]$Message) + Write-Host $Message +} + +function Fail { + param([string]$Message) + Write-Error $Message + exit 1 +} + +function Copy-ClaudeSecurityAuditSkill { + param([string]$SourcePath) + + $skillFile = Join-Path $SourcePath "SKILL.md" + if (-not (Test-Path -LiteralPath $skillFile)) { + Fail "Could not find SKILL.md at $SourcePath" + } + + $skillText = Get-Content -LiteralPath $skillFile -Raw + if ($skillText -notmatch "(?m)^name:\s*securityaudit\s*$") { + Fail "SKILL.md does not look like the securityaudit skill" + } + + $claudeHome = if ($env:CLAUDE_HOME) { $env:CLAUDE_HOME } else { Join-Path $HOME ".claude" } + $destParent = Join-Path $claudeHome "skills" + $dest = Join-Path $destParent "securityaudit" + + New-Item -ItemType Directory -Force -Path $destParent | Out-Null + + $stage = Join-Path $destParent (".securityaudit-claude-install-" + [Guid]::NewGuid().ToString("N")) + $stageSkill = Join-Path $stage "securityaudit" + New-Item -ItemType Directory -Force -Path $stageSkill | Out-Null + + Copy-Item -LiteralPath $skillFile -Destination (Join-Path $stageSkill "SKILL.md") -Force + foreach ($dir in @("scripts", "references", "assets")) { + $sourceDir = Join-Path $SourcePath $dir + if (Test-Path -LiteralPath $sourceDir) { + Copy-Item -LiteralPath $sourceDir -Destination (Join-Path $stageSkill $dir) -Recurse -Force + } + } + + if (-not (Test-Path -LiteralPath (Join-Path $stageSkill "SKILL.md"))) { + Fail "Install staging failed" + } + + if (Test-Path -LiteralPath $dest) { + $backup = "$dest.backup.$(Get-Date -Format 'yyyyMMdd-HHmmss')" + Say "Existing Claude securityaudit skill found. Backing it up to:" + Say $backup + Move-Item -LiteralPath $dest -Destination $backup + } + + Move-Item -LiteralPath $stageSkill -Destination $dest + Remove-Item -LiteralPath $stage -Force -ErrorAction SilentlyContinue + + Say "" + Say "Installed Claude Code securityaudit skill to:" + Say $dest + Say "" + Say "Restart Claude Code if it was already open, then use:" + Say " /securityaudit" + Say " /securityaudit --fix" +} + +if ($PSScriptRoot -and (Test-Path -LiteralPath (Join-Path $PSScriptRoot $SkillPath "SKILL.md"))) { + Say "Installing Claude Code securityaudit skill from local checkout..." + Copy-ClaudeSecurityAuditSkill -SourcePath (Join-Path $PSScriptRoot $SkillPath) + exit 0 +} + +if (-not (Get-Command Expand-Archive -ErrorAction SilentlyContinue)) { + Fail "PowerShell Expand-Archive is required. Please use PowerShell 5 or newer." +} + +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + +$temp = Join-Path ([IO.Path]::GetTempPath()) ("securityaudit-claude-install-" + [Guid]::NewGuid().ToString("N")) +New-Item -ItemType Directory -Force -Path $temp | Out-Null + +try { + $archive = Join-Path $temp "repo.zip" + $url = "https://github.com/$RepoSlug/archive/refs/heads/$Ref.zip" + + Say "Downloading Claude Code securityaudit skill from $RepoSlug ($Ref)..." + $client = New-Object Net.WebClient + $client.DownloadFile($url, $archive) + + Expand-Archive -LiteralPath $archive -DestinationPath $temp -Force + + $skillFile = Get-ChildItem -Path $temp -Filter "SKILL.md" -Recurse | + Where-Object { Split-Path -Leaf (Split-Path -Parent $_.FullName) -eq $SkillPath } | + Select-Object -First 1 + + if (-not $skillFile) { + Fail "Could not find $SkillPath/SKILL.md in the downloaded repo" + } + + Copy-ClaudeSecurityAuditSkill -SourcePath (Split-Path -Parent $skillFile.FullName) +} +finally { + Remove-Item -LiteralPath $temp -Recurse -Force -ErrorAction SilentlyContinue +} diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 0000000..282fdac --- /dev/null +++ b/install.ps1 @@ -0,0 +1,105 @@ +param( + [string]$RepoSlug = $(if ($env:SECURITYAUDIT_REPO) { $env:SECURITYAUDIT_REPO } else { "andydixon/securityaudit-skill" }), + [string]$Ref = $(if ($env:SECURITYAUDIT_REF) { $env:SECURITYAUDIT_REF } else { "main" }), + [string]$SkillPath = $(if ($env:SECURITYAUDIT_SKILL_PATH) { $env:SECURITYAUDIT_SKILL_PATH } else { "securityaudit" }) +) + +$ErrorActionPreference = "Stop" + +function Say { + param([string]$Message) + Write-Host $Message +} + +function Fail { + param([string]$Message) + Write-Error $Message + exit 1 +} + +function Copy-SecurityAuditSkill { + param([string]$SourcePath) + + $skillFile = Join-Path $SourcePath "SKILL.md" + if (-not (Test-Path -LiteralPath $skillFile)) { + Fail "Could not find SKILL.md at $SourcePath" + } + + $skillText = Get-Content -LiteralPath $skillFile -Raw + if ($skillText -notmatch "(?m)^name:\s*securityaudit\s*$") { + Fail "SKILL.md does not look like the securityaudit skill" + } + + $codexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { Join-Path $HOME ".codex" } + $destParent = Join-Path $codexHome "skills" + $dest = Join-Path $destParent "securityaudit" + + New-Item -ItemType Directory -Force -Path $destParent | Out-Null + + $stage = Join-Path $destParent (".securityaudit-install-" + [Guid]::NewGuid().ToString("N")) + $stageSkill = Join-Path $stage "securityaudit" + New-Item -ItemType Directory -Force -Path $stageSkill | Out-Null + + Copy-Item -Path (Join-Path $SourcePath "*") -Destination $stageSkill -Recurse -Force + + if (-not (Test-Path -LiteralPath (Join-Path $stageSkill "SKILL.md"))) { + Fail "Install staging failed" + } + + if (Test-Path -LiteralPath $dest) { + $backup = "$dest.backup.$(Get-Date -Format 'yyyyMMdd-HHmmss')" + Say "Existing securityaudit skill found. Backing it up to:" + Say $backup + Move-Item -LiteralPath $dest -Destination $backup + } + + Move-Item -LiteralPath $stageSkill -Destination $dest + Remove-Item -LiteralPath $stage -Force -ErrorAction SilentlyContinue + + Say "" + Say "Installed securityaudit to:" + Say $dest + Say "" + Say "Restart Codex, then use:" + Say " `$securityaudit" + Say " `$securityaudit --fix" +} + +if ($PSScriptRoot -and (Test-Path -LiteralPath (Join-Path $PSScriptRoot $SkillPath "SKILL.md"))) { + Say "Installing securityaudit from local checkout..." + Copy-SecurityAuditSkill -SourcePath (Join-Path $PSScriptRoot $SkillPath) + exit 0 +} + +if (-not (Get-Command Expand-Archive -ErrorAction SilentlyContinue)) { + Fail "PowerShell Expand-Archive is required. Please use PowerShell 5 or newer." +} + +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + +$temp = Join-Path ([IO.Path]::GetTempPath()) ("securityaudit-install-" + [Guid]::NewGuid().ToString("N")) +New-Item -ItemType Directory -Force -Path $temp | Out-Null + +try { + $archive = Join-Path $temp "repo.zip" + $url = "https://github.com/$RepoSlug/archive/refs/heads/$Ref.zip" + + Say "Downloading securityaudit from $RepoSlug ($Ref)..." + $client = New-Object Net.WebClient + $client.DownloadFile($url, $archive) + + Expand-Archive -LiteralPath $archive -DestinationPath $temp -Force + + $skillFile = Get-ChildItem -Path $temp -Filter "SKILL.md" -Recurse | + Where-Object { Split-Path -Leaf (Split-Path -Parent $_.FullName) -eq $SkillPath } | + Select-Object -First 1 + + if (-not $skillFile) { + Fail "Could not find $SkillPath/SKILL.md in the downloaded repo" + } + + Copy-SecurityAuditSkill -SourcePath (Split-Path -Parent $skillFile.FullName) +} +finally { + Remove-Item -LiteralPath $temp -Recurse -Force -ErrorAction SilentlyContinue +} diff --git a/install.sh b/install.sh index 8deeba4..5ca318a 100755 --- a/install.sh +++ b/install.sh @@ -82,5 +82,5 @@ say "Installed securityaudit to:" say "$DEST" say "" say "Restart Codex, then use:" -say " securityaudit" -say " securityaudit --fix" +say ' $securityaudit' +say ' $securityaudit --fix'