Skill: msrovani/skill-evidenceops (version 1.0.0)

--- name: evidenceops description: Forensic media triage with chain of custody. Use when receiving images, videos, audio, PDFs, or documents that need evidence-grade handling, integrity verification, and audit trails. version: 1.0.0 author: OpenClaw Community metadata: openclaw: emoji: "🔬" tools: - evidence.ingest - evidence.verify - evidence.manifest - evidence.export - evidence.access_log - Read - Write - Bash install: npm: "@openclaw/evidence-vault" os: - darwin - linux - win32 categories: - forensics - security - compliance tags: - evidence - chain-of-custody - forensic - integrity - audit --- # EvidenceOps - Forensic Media Triage ## What It Does EvidenceOps provides forensic-grade handling of media files with complete chain of custody: 1. **Media Intake** - Accept images, videos, audio, PDFs, and documents from any channel 2. **Immutable Storage** - Store originals in append-only vault with cryptographic hashes 3. **Metadata Extraction** - Extract EXIF, file properties, and media information without altering originals 4. **Derivative Generation** - Create thumbnails, transcripts, previews in separate folders 5. **Chain of Custody** - Maintain tamper-evident audit trail with hash chain 6. **Integrity Verification** - Verify evidence hasn't been modified post-ingest 7. **Audit Trail** - Complete JSONL audit log for compliance ## What It NEVER Does - **NEVER** modifies original evidence files after ingest - **NEVER** stores secrets, API keys, or credentials in manifests or logs - **NEVER** accepts unsanitized paths from user input - **NEVER** executes untrusted code or downloads remote scripts - **NEVER** exfiltrates data to external services without explicit configuration - **NEVER** bypasses channel allowlists or pairing requirements - **NEVER** stores real personal data in example files ## Prerequisites Before using this skill, ensure: 1. Plugin `@openclaw/evidence-vault` is installed and initialized 2. Vault storage directory is configured with appropriate permissions 3. Channel allowlist is configured for trusted sources only 4. Retention policies comply with your legal requirements ## Workflow ### Step 1: Receive Media When media is received via any channel: ``` User sends: [image/video/document] ``` **Required Information:** - File content (from attachment) - Original filename - Source channel (whatsapp, telegram, email, etc.) - Sender identifier - Message ID (if available) ### Step 2: Create or Select Case ``` IF user specifies existing caseId: USE that caseId ELSE IF user requests new case: CREATE case with format: case-{YYYY}-{NNN} EXAMPLE: case-2026-001 ELSE: ASK user: "Should I create a new case or add to existing case [case-2026-XXX]?" ``` **Case ID Format:** `case-{year}-{sequence}` - Must match pattern: `^case-[a-zA-Z0-9_-]+$` - Examples: `case-2026-001`, `case-incident-alpha`, `case-legal-2026-q1` ### Step 3: Stage Original (Read-Only) Before ingest: 1. **Save received file to temporary staging area** 2. **Calculate SHA-256 hash immediately** 3. **Record file size and MIME type** 4. **DO NOT modify the file** ``` # Staging directory structure /tmp/evidence-staging/ ├── {caseId}/ │ └── {timestamp}-{filename} ``` ### Step 4: Extract Metadata Extract metadata WITHOUT modifying original: **For Images:** - EXIF data (camera, GPS, timestamps) - Dimensions - Color profile **For Videos:** - Duration - Codec information - Resolution **For Audio:** - Duration - Sample rate - Codec **For PDFs:** - Page count - Author (if embedded) - Creation date ``` # Use Read tool or appropriate extraction commands # NEVER write back to original file ``` ### Step 5: Generate Derivatives (Optional) Create derivative artifacts in SEPARATE folder: ``` derivatives/ ├── thumbnails/ │ └── {evidenceId}-thumb.jpg ├── transcripts/ │ └── {evidenceId}-transcript.txt └── previews/ └── {evidenceId}-preview.pdf ``` **Derivative Types:** - `thumbnail` - Reduced resolution image/video preview - `transcript` - Speech-to-text for audio/video - `preview` - PDF or text representation - `ocr` - Extracted text from images ### Step 6: Ingest to Vault Call the evidence.ingest tool: ```json { "filePath": "/path/to/staged/file", "filename": "original-filename.jpg", "caseId": "case-2026-001", "channel": "whatsapp", "sender": "[REDACTED_SECRET]", "messageId": "msg-abc123", "retentionDays": 2555, "metadata": { "exif": { ... }, "extracted": { ... } } } ``` **Response:** ```json { "success": true, "evidenceId": "ev-abc123...", "sha256": "a1b2c3...", "vaultUrl": "file:///vault/cases/case-2026-001/originals/ev-abc123.jpg", "timestamp": "2026-02-17T10:30:00.000Z" } ``` ### Step 7: Update Manifest The manifest is automatically updated by the ingest operation. **Manifest Location:** `{vault}/cases/{caseId}/manifest.json` **Manifest Contents:** - Case metadata - Evidence items with hashes - Derivatives - Chain of custody entries - Retention policy ### Step 8: Return Receipt Provide user with evidence receipt: ``` 📋 EVIDENCE RECEIPT Case ID: case-2026-001 Evidence ID: ev-abc123... File: original-filename.jpg SHA-256: a1b2c3d4e5f6... Size: 1.2 MB Received: 2026-02-17 10:30:00 UTC Vault: file:///vault/cases/case-2026-001/originals/ev-abc123.jpg ✅ Chain of custody established ✅ Original preserved immutably ✅ Audit trail active ``` ## Tool Reference ### evidence.ingest Ingest a file into the evidence vault. **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | filePath | string | Yes | Path to the staged file | | filename | string | Yes | Original filename | | caseId | string | Yes | Case identifier | | channel | string | No | Source channel | | sender | string | No | Sender identifier | | messageId | string | No | Message ID from source | | retentionDays | number | No | Retention period | | metadata | object | No | Additional metadata | **Returns:** `{ evidenceId, sha256, vaultUrl, timestamp }` ### evidence.verify Verify evidence integrity. **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | evidenceId | string | Yes | Evidence to verify | | caseId | string | No | Limit search to case | **Returns:** `{ verified, details: { originalIntact, hashMatch, lastVerifiedAt } }` ### evidence.manifest Get case manifest. **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | caseId | string | Yes | Case identifier | **Returns:** Complete case manifest with all items ### evidence.export Export case as archive. **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | caseId | string | Yes | Case identifier | | format | string | No | 'zip' or 'tar' (default: zip) | **Returns:** `{ exportPath, sha256, size, itemCount }` ### evidence.access_log Get audit trail. **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | caseId | string | Yes | Case identifier | | limit | number | No | Max events (default: 100) | **Returns:** `{ events: [ ... ], count }` ## SECURITY POSTURE ### Channel Allowlist Configuration Configure which channels can submit evidence: ```yaml # ~/.openclaw/openclaw.json { "plugins": { "evidence-vault": { "channelAllowlist": ["whatsapp", "telegram", "email"], "channelDenylist": ["public-discord"], "requirePairing": true } } } ``` ### Pairing Requirements For Direct Messages (DMs): - **REQUIRE** pairing before accepting evidence - **BLOCK** unpaired DMs by default - **LOG** rejected ingestion attempts For Group Channels: - **VERIFY** channel is in allowlist - **REJECT** evidence from untrusted channels - **NEVER** auto-ingest from public channels ### Path Sanitization Rules ALL paths are validated: 1. **No path traversal** - `../` sequences rejected 2. **No null bytes** - `\0` rejected 3. **No home directory** - `~/` rejected 4. **Canonicalization** - Paths resolved before validation 5. **Base path check** - Must be within vault directory **Violations result in:** `E_PATH_TRAVERSAL` error + audit log entry ### Destructive Action Confirmation Before any potentially destructive operation: 1. **EXPORT** - Confirm export destination 2. **RETENTION DELETE** - Require explicit confirmation (if enabled) 3. **LEGAL HOLD RELEASE** - Require explicit confirmation **Confirmation format:** ``` ⚠️ DESTRUCTIVE ACTION You are about to: [describe action] Case: [caseId] Items affected: [count] Type "CONFIRM" to proceed: ``` ### Secrets Handling **NEVER include in logs or manifests:** - API keys - Passwords - Tokens - Credit card numbers - Email addresses (redacted) - Phone numbers (redacted) - SSN (redacted) **Redaction is automatic** via regex patterns. ## Troubleshooting ### Error: E_PATH_TRAVERSAL **Cause:** Filename or path contains disallowed characters **Solution:** - Filenames are automatically sanitized - If error persists, check for unusual characters - Original filename is preserved in manifest metadata ### Error: E_INVALID_MIME **Cause:** File type not in allowed list **Solution:** - Check `allowedMimeTypes` configuration - Add MIME type to allowlist if appropriate - Verify file is not corrupted ### Error: E_SIZE_LIMIT **Cause:** File exceeds maximum size **Solution:** - Check `maxFileSizeBytes` configuration - Split large files if appropriate - Compress before ingest (note in metadata) ### Error: E_CHANNEL_BLOCKED **Cause:** Evidence from blocked or non-allowlisted channel **Solution:** - Verify channel in allowlist - Check if pairing is required - Review security configuration ### Error: E_HASH_MISMATCH **Cause:** Evidence file modified after ingest **Solution:** - This indicates potential tampering - Review access logs for case - Escalate per incident response procedures ### Verification Fails **Symptoms:** `verified: false` in verify response **Diagnostic steps:** 1. Check if original file exists 2. Compare current hash with manifest hash 3. Review audit log for modifications 4. Check filesystem permissions ## Configuration Example ```yaml # openclaw.yaml plugins: evidence-vault: driver: filesystem # or s3 basePath: /var/evidence-vault maxFileSizeBytes: 524288000 # 500MB defaultRetentionDays: 2555 # 7 years allowedMimeTypes: - image/jpeg - image/png - video/mp4 - audio/mpeg - application/pdf channelAllowlist: - whatsapp - telegram - slack requirePairing: true # S3 driver options (if driver: s3) s3: endpoint: https://s3.example.com bucket: evidence-vault region: us-east-1 objectLock: true ``` ## Legal and Compliance Notes ### LGPD/GDPR Considerations - Tag sensitive data with `sensitivityTag` - Configure appropriate retention periods - Never store real personal data in examples - Implement data subject access request procedures ### Retention Policies | Sensitivity | Default Retention | |-------------|-------------------| | public | 365 days | | internal | 1825 days (5 years) | | confidential | 2555 days (7 years) | | restricted | Legal hold | ### Audit Requirements All operations logged to JSONL audit file: - Case creation/deletion - Evidence ingest - Verification attempts - Export operations - Access requests **Audit log retention:** Same as case retention


Security scan verdicts — ClawScan: suspicious | static analysis:  | VirusTotal: clean | SkillSpector: suspicious (HIGH)