Claw-Gripper.ps1

<#PSScriptInfo

.VERSION 1.1.0

.GUID 9b7d86ef-6b5c-40b9-853a-ffa88368831b

.AUTHOR Grip Security

.COMPANYNAME Grip Security

.COPYRIGHT (c) Grip Security

.TAGS GripSecurity,OpenClaw,Discovery,Audit,Forensics,WSL

.LICENSEURI https://grip.security

.PROJECTURI https://grip.security

.ICONURI

.EXTERNALMODULEDEPENDENCIES

.REQUIREDSCRIPTS

.EXTERNALSCRIPTDEPENDENCIES

.RELEASENOTES
1.1.0 - Documentation and packaging polish. -Json now outputs JSON to stdout (in addition to optional -JsonPath file output). Core scan logic unchanged.

.PRIVATEDATA

#>


<#
.SYNOPSIS
Claw Gripper (v1.1) - Deep Discovery.

.DESCRIPTION
Performs a deep discovery scan for OpenClaw installations on the local machine.

Specifically captures:
- ComputerName
- Platform (Windows / WSL distro name)
- OpenClaw Version (best-effort)
- Installed Skills (best-effort)
- State directory path (best-effort)

Output is printed as a table by default.
Optionally outputs JSON to stdout and/or writes JSON results to a file path.

.PARAMETER Json
If specified, outputs JSON to stdout (in addition to the standard table output).

.PARAMETER JsonPath
If specified, writes JSON output to the provided path.

.PARAMETER Mdm
If specified, suppresses the "Press any key to close..." prompt (MDM / non-interactive friendly).

.PARAMETER NoPause
If specified, suppresses the "Press any key to close..." prompt.

.EXAMPLE
.\Claw-Gripper.ps1
Runs the scan and prints a table.

.EXAMPLE
.\Claw-Gripper.ps1 -Json
Runs the scan, prints a table, and also outputs JSON to stdout.

.EXAMPLE
.\Claw-Gripper.ps1 -JsonPath "C:\Temp\claw_gripper.json"
Runs the scan, prints a table, and writes results to the specified JSON file.

.NOTES
- Windows-focused: uses Win32_UserProfile and optionally queries WSL distros via wsl.exe.
- If WSL is not available, the script continues and reports Windows findings only.
#>


[CmdletBinding()]
param(
    [switch]$Json,
    [string]$JsonPath,
    [switch]$Mdm,
    [switch]$NoPause
)

$ErrorActionPreference = "Continue"

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

function Get-ClawGripperSkills {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$StatePath
    )

    $skills = @()

    # Check both the standard 'skills' folder and the workspace 'skills'
    $skillPaths = @(
        (Join-Path $StatePath "skills"),
        (Join-Path $StatePath "workspace/skills")
    )

    foreach ($sp in $skillPaths) {
        if (Test-Path $sp) {
            $skills += (Get-ChildItem $sp -Directory -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name)
        }
    }

    return ($skills | Sort-Object -Unique) -join ", "
}

# ---------------------------------------------------------------------------
# Core audit logic
# ---------------------------------------------------------------------------

function Invoke-ClawGripperAudit {
    [CmdletBinding()]
    param()

    $findings = New-Object System.Collections.Generic.List[object]
    $stateDirs = New-Object System.Collections.Generic.List[string]
    $compName = $env:COMPUTERNAME

    Write-Host "... Scanning for installations on $compName" -ForegroundColor Gray

    # 1) WINDOWS SCAN
    try {
        $profiles = Get-CimInstance Win32_UserProfile -ErrorAction Stop | Where-Object { $_.LocalPath }
        foreach ($p in $profiles) {
            $d = Join-Path $p.LocalPath ".openclaw"
            if (Test-Path $d) { $stateDirs.Add("Windows|$d") }
        }
    } catch {
        # Keep going; this may fail on environments without CIM/WMI access.
    }

    # 2) WSL SCAN (Ubuntu/Linux)
    try {
        $distros = wsl.exe --list --quiet 2>$null | Where-Object { $_ -match "\w" }
        foreach ($d in $distros) {
            $distroName = $d.Trim().Replace("`0", "")
            $wslPath = "\\wsl.localhost\$distroName\home"
            if (Test-Path $wslPath) {
                $linuxUsers = Get-ChildItem $wslPath -Directory -ErrorAction SilentlyContinue
                foreach ($lUser in $linuxUsers) {
                    $target = Join-Path $lUser.FullName ".openclaw"
                    if (Test-Path $target) { $stateDirs.Add("$distroName|$target") }
                }
            }
        }
    } catch {
        # WSL not present / not accessible; continue.
    }

    foreach ($entry in ($stateDirs | Sort-Object -Unique)) {
        $parts = $entry -split "\|", 2
        $platform = $parts[0]
        $finalPath = $parts[1]

        $version = "Unknown"
        $skills = Get-ClawGripperSkills -StatePath $finalPath

        if ($platform -ne "Windows") {
            # Linux version check via WSL
            try {
                $rawVer = wsl.exe -d $platform -e openclaw --version 2>$null
                $version = ($rawVer -join " ").Trim()
            } catch {
                $version = "Found (CLI unreachable)"
            }
        } else {
            # Windows version check
            try {
                $cmd = Get-Command openclaw -ErrorAction SilentlyContinue
                if ($cmd) {
                    $version = (& $cmd.Source --version 2>$null) -join " "
                }
            } catch {
                # Keep defaults
            }
        }

        if ([string]::IsNullOrWhiteSpace($version)) { $version = "Detected (No Version info)" }
        if ([string]::IsNullOrWhiteSpace($skills))  { $skills  = "None Found" }

        $findings.Add([pscustomobject]@{
            ComputerName = $compName
            Platform     = $platform
            Version      = $version
            Skills       = $skills
            Path         = $finalPath
        })
    }

    return $findings
}

# ---------------------------------------------------------------------------
# Execution
# ---------------------------------------------------------------------------

$results = Invoke-ClawGripperAudit

if ($results.Count -eq 0) {
    Write-Host "RESULT: No OpenClaw installations found on $env:COMPUTERNAME" -ForegroundColor Green
} else {
    Write-Host "RESULT: Found $($results.Count) installations:`n" -ForegroundColor Yellow
    $results | Format-Table ComputerName, Platform, Version, Skills -AutoSize
}

# JSON output (stdout and/or file)
if ($Json -or $JsonPath) {
    $jsonText = $results | ConvertTo-Json -Depth 5

    if ($Json) {
        $jsonText
    }

    if ($JsonPath) {
        try {
            $jsonText | Set-Content -Path $JsonPath -Encoding utf8
        } catch {
            Write-Warning "Failed to write JSON to path '$JsonPath'. Error: $($_.Exception.Message)"
        }
    }
}

if (-not $Mdm -and -not $NoPause) {
    Write-Host "`nScan Finished. Press any key to close..."
    $null = [Console]::ReadKey()
}