Claw-Gripper.ps1

<#PSScriptInfo

.VERSION 1.1.1

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

.AUTHOR Grip Security

.COMPANYNAME Grip Security

.COPYRIGHT (c) Grip Security

.TAGS GripSecurity OpenClaw Discovery Audit MDM WSL

.LICENSEURI https://grip.security/

.PROJECTURI https://grip.security/

.ICONURI

.EXTERNALMODULEDEPENDENCIES

.REQUIREDSCRIPTS

.EXTERNALSCRIPTDEPENDENCIES

.RELEASENOTES
1.1.1 - Metadata refresh (description/tags). No functional changes.

.PRIVATEDATA

#>
 



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

.DESCRIPTION
Claw Gripper helps security teams quickly identify **where OpenClaw is running** — and what it can do on each endpoint.

Built by **Grip Security** for fast, reliable discovery.

Scans **Windows + WSL** (when available).

Captures:
- Device + Platform / Distro
- OpenClaw Version + Installed Skills (best-effort)
- Installation State Directory Path (best-effort)

Clean table output by default.

**MDM-ready** JSON export to stdout and/or file for fleet reporting and automation.

.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()
}