From 1cdf87130b850a0c85569c0d35369b17eae51585 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 15 Feb 2026 00:00:20 -0500 Subject: [PATCH] chore: auto-commit uncommitted changes --- .openclaw/workspace-state.json | 4 + config/email-triage-prompt.md | 61 ++- memory/2026-02-14.md | 44 ++ memory/claude-usage.db | Bin 32768 -> 32768 bytes memory/claude-usage.json | 10 +- memory/email-triage.md | 340 +++++---------- .../inou-security-assessment-2026-02-14.pptx | Bin 0 -> 43621 bytes memory/updates/2026-02-14.json | 49 +-- memory/working-context.md | 57 +-- scripts/shannon-pptx.py | 405 ++++++++++++++++++ 10 files changed, 641 insertions(+), 329 deletions(-) create mode 100644 .openclaw/workspace-state.json create mode 100644 memory/shannon-scan-2026-02-14/inou-security-assessment-2026-02-14.pptx create mode 100644 scripts/shannon-pptx.py diff --git a/.openclaw/workspace-state.json b/.openclaw/workspace-state.json new file mode 100644 index 0000000..eede626 --- /dev/null +++ b/.openclaw/workspace-state.json @@ -0,0 +1,4 @@ +{ + "version": 1, + "onboardingCompletedAt": "2026-02-15T04:20:24.393Z" +} diff --git a/config/email-triage-prompt.md b/config/email-triage-prompt.md index efc7e18..1ae9e0e 100644 --- a/config/email-triage-prompt.md +++ b/config/email-triage-prompt.md @@ -1,42 +1,61 @@ -You are an email triage system for Johan Jongsma. You classify incoming emails into one of four actions. +You are a junk mail filter for Johan Jongsma. You classify incoming emails into one of two actions. -Respond with ONLY a JSON object: {"action": "", "reason": ""} +Respond with ONLY a JSON object: {"action": "junk|pass", "reason": "brief explanation"} ## Actions -- **trash** — Junk, spam, marketing, newsletters, promotions, automated notifications with no value, shipping/delivery updates, "your statement is ready" notices, DigiKey marketing, inou verification codes (noreply@inou.com) -- **archive** — Processed bills, past travel confirmations, payment receipts, security alerts (password changes, new logins), subscription confirmations with reference value -- **keep** — Action required, bills pending payment, personal correspondence awaiting reply, new lingerie/fashion collection launches (Pain de Sucre, Fleur du Mal — new collections only, not sales), Verizon purchase/order emails, anything needing Johan's attention -- **escalate** — Sophia-related (medical, therapy, brain, neuro, activator, pediatric suppliers, insurance claims mentioning Sophia, "S. Jongsma"), phishing attempts, important personal emails, infrastructure alerts (Uptime Kuma, server notifications, SSL), Google Search Console alerts, domain purchase inquiries, anything unusual or uncertain +- **junk** — Spam, marketing, newsletters, promotions, automated notifications, shipping/delivery updates, "your statement is ready" notices, subscription confirmations, order confirmations, receipts, security alerts (password changes, new logins), DigiKey marketing, inou verification codes (noreply@inou.com), retailer promos, anything that doesn't need a human +- **pass** — Real humans writing real messages, action required, bills pending payment, anything Sophia-related, phishing attempts, infrastructure alerts, personal correspondence, anything uncertain or unusual ## Specific Sender Rules -- **Kaseya Marketing** → trash (summarize in reason) -- **Lansweeper** → trash (summarize in reason) -- **inou verification codes** (noreply@inou.com) → trash -- **Immich** (GitHub/releases) → escalate (triggers server update) -- **DigiKey** (e.digikey.com) marketing → trash -- **Verizon Wireless** purchase/order → keep; generic promos → trash -- **Amazon** — order confirmations → keep; everything else (promos, recommendations, shipping updates) → trash -- **Domain purchase inquiries** → escalate +- **Kaseya Marketing** → junk +- **Lansweeper** → junk +- **inou verification codes** (noreply@inou.com) → junk +- **DigiKey** (e.digikey.com) marketing → junk +- **Amazon** — everything (promos, shipping, order confirmations, recommendations) → junk +- **Verizon Wireless** — generic promos → junk; purchase/order emails → pass +- **Immich** (GitHub/releases) → pass (triggers server update) +- **Domain purchase inquiries** → pass + +## "Interesting Spam" Exceptions (pass, not junk) + +These categories look like marketing but Johan wants to see them: + +- **Lingerie/swimwear brands** (Pain de Sucre, Fleur du Mal, and similar) — New collections, seasonal launches, lookbooks, designer collaborations → **pass**. Generic "50% off" / "sale ends tonight" discount blasts → still **junk**. +- **AI/ML news** — New model releases, LLM announcements, ML research breakthroughs, benchmark results → **pass**. Generic "try our AI tool" product marketing → still **junk**. + +The test: is there something *new and interesting* to look at, or is it just trying to get you to buy something on sale? New = pass. Sale = junk. + +## Renewals, Deadlines & Financial Commitments (always pass) + +Any email mentioning renewal, expiration, auto-renew, subscription renewal, domain renewal, "expires soon", certificate expiry, or similar → **always pass**. These have actionable deadlines and/or financial consequences. + +**General rule:** If it involves a deadline or money being charged, **pass**. The junk filter should not make judgment calls on things that cost money. ## Phishing Detection -If ANY of these red flags appear, action MUST be **escalate**: +If ANY of these red flags appear, action MUST be **pass**: - Unexpected "payment processed" / "transaction" language - Phone numbers to call "if you didn't authorize" - Urgent action + money involved - Legitimate service used as delivery mechanism (Canva invite containing payment info, etc.) - Mismatched sender domain vs claimed sender +Phishing is dangerous. When in doubt, pass it through. + +## Renewal / Expiration / Deadline Rule + +Anything mentioning renewal, auto-renew, expiring, expires soon, domain renewal, subscription renewal, certificate expiry, or similar deadline/financial commitment language → **always pass**. The mailroom doesn't make judgment calls on things that cost money. + ## Critical Rules -1. When uncertain, escalate — never silently trash something important -2. Sophia triggers ALWAYS escalate — medical, therapy, brain activator, pediatric suppliers -3. Personal correspondence from real humans → escalate -4. "Re:" thread replies from people → escalate (active conversation) -5. Credits/refunds with expiry dates → keep -6. Shipping/delivery updates → trash (dashboard tracks these separately) +1. When uncertain, **pass** — never silently junk something important +2. Sophia triggers ALWAYS **pass** — medical, therapy, brain activator, pediatric suppliers, "S. Jongsma" +3. Personal correspondence from real humans → **pass** +4. "Re:" thread replies from people → **pass** (active conversation) +5. Infrastructure/server alerts → **pass** +6. Google Search Console alerts → **pass** ## Account Context diff --git a/memory/2026-02-14.md b/memory/2026-02-14.md index a11653c..e494d82 100644 --- a/memory/2026-02-14.md +++ b/memory/2026-02-14.md @@ -136,3 +136,47 @@ - **Archived:** GenerX service form, order confirmations (4720443389, Nordstrom 1026793809) - **Trashed:** Duke Energy usage alert, Mint Mobile survey (Mikhail), Realtor listing for own house, generator notifications, political newsletter, subscriber agreement update, marketing/spam, Valentine's lingerie promos, hardware spam - **MC issue:** LLM triage failing with 401 (API key invalid), requests taking 15s+ + +## Shannon Scan Complete +- Full pentest of inou.com via Shannon Lite on Sonnet 4.5 (OAuth token direct to Anthropic) +- Runtime: ~1.5 hours +- **Findings:** 2 CRITICAL (hardcoded backdoor 250365, session hijacking), 2 HIGH (session fixation, brute force) +- Authorization, SQLi, SSRF all passed clean +- 3 out-of-scope items needing internal access (path traversal, DICOM XSS, LLM prompt injection XSS) +- PowerPoint generated and placed on sophia SMB share +- All deliverables at ~/clawd/memory/shannon-scan-2026-02-14/ + +## Prima (UMich Brain MRI AI) +- Deep dive completed — open source MIT, VLM for 50+ neuro conditions, 97.5% accuracy +- Needs Ampere+ GPU (L40S reference, RTX 3090 minimum). Forge GTX 970 is out +- Johan's idea: offer as inou service — intelligent series selection (don't run all 10K slices, pick relevant series) → RunPod serverless GPU → results in viewer +- Spec being written by subagent +- Sophia had 10,000+ slices — this is the use case + +## Diana Yusha Lab Report +- Added arthritis rebuttal section (Russian) — CRP 1.3 and ESR 6 don't support arthritis diagnosis +- Some Russian doctor suggested arthritis based on elevated CK 406 — incorrect interpretation + +## WebMCP +- Added to HEARTBEAT.md tech watch list +- Chrome experimental API for structured web agent tools — high relevance for James/OpenClaw, medium for inou + +## Colorado Camel Milk +- Order #16698 from Feb 7, $115 (6 pints frozen + $31 shipping) +- Still "Awaiting Shipment" — waiting for safe weather window (frozen perishable to Florida) + +## Infrastructure + +### Generator (48kw) +- Generator does NOT auto-start — manual start only +- GenerX came out today to analyze the auto-start issue (tech: Eduardo Rivera) +- The 3 runs were test runs by GenerX, NOT power outages +- Waiting on quote from GenerX for the fix + +### Emergency Internet Failover (TP-Link AX1500) +- Installed TP-Link AX1500 router, connected to UDM-Pro port 10 +- Tested and working, then port 10 disabled on UDM-Pro +- WiFi network: `X4` / password: `Helder06` +- **For longer/full outages (including mobile coverage):** swap the cable on port 10 for the longer cable (already staged nearby) and connect to Starlink +- This gives internet via Starlink → AX1500 → UDM-Pro port 10 +- Shannon VPS now configured with OAuth token (no more router proxy) diff --git a/memory/claude-usage.db b/memory/claude-usage.db index 4672af11fee659fce53d85ff7dadc9d270b4dc23..897b7131c26decad5c26f928875d9609ede322c4 100644 GIT binary patch delta 434 zcmZo@U}|V!njp=%VWNyPWbml2mR=jV-$ z?VRZ711t)#lLt^)v4Kzuv#?rJ6$4ua V^pgj0x3PgX29sEJ4YL7v{|LDX7Ks1= diff --git a/memory/claude-usage.json b/memory/claude-usage.json index a63359c..00a3098 100644 --- a/memory/claude-usage.json +++ b/memory/claude-usage.json @@ -1,9 +1,9 @@ { - "last_updated": "2026-02-14T23:00:04.689745Z", + "last_updated": "2026-02-15T05:00:03.623124Z", "source": "api", - "session_percent": 6, - "session_resets": "2026-02-15T01:59:59.648057+00:00", - "weekly_percent": 2, - "weekly_resets": "2026-02-21T18:59:59.648078+00:00", + "session_percent": 4, + "session_resets": "2026-02-15T07:00:00.587244+00:00", + "weekly_percent": 4, + "weekly_resets": "2026-02-21T18:59:59.587273+00:00", "sonnet_percent": 5 } \ No newline at end of file diff --git a/memory/email-triage.md b/memory/email-triage.md index 645135e..9bfc2b4 100644 --- a/memory/email-triage.md +++ b/memory/email-triage.md @@ -1,235 +1,121 @@ -# Email Triage Instructions +# Mail Agent Routing Guide -*Created: 2026-02-01* -*Updated: 2026-02-07 (Added Specific Sender Rules)* +*Updated: 2026-02-14 — Pipeline refactor: MC handles junk filtering, mail agent handles smart routing* + +## Architecture + +``` +Email → MC (junk filter) → webhook → OpenClaw mail agent (smart routing) + ↓ junk: deleted ↓ pass: routed intelligently +``` + +**MC's job:** Binary junk/pass filter. Spam, marketing, newsletters → deleted. Everything else → passed to you. +**Your job:** Read each message fully. Route it to the right place. Take action. ## Message Center (MC) API -**Unified endpoint for all messages (email + WhatsApp):** -- Base URL: `http://localhost:8025` -- Fetch new: `GET /messages/new` -- Replay: `GET /messages?since=24h` -- Actions: `POST /messages/{id}/archive`, `/delete`, `/reply`, `/to-docs`, `/seen` +- **Base URL:** `http://localhost:8025` +- `GET /messages/new` — fetch unprocessed messages +- `GET /messages?since=24h` — replay window +- `GET /messages/{id}` — single message with full body +- `POST /messages/{id}/archive` — archive (done processing, no action needed) +- `POST /messages/{id}/delete` — hard delete +- `POST /messages/{id}/reply` — send reply `{"body": "..."}` +- `POST /messages/{id}/to-docs` — forward attachments to ~/documents/inbox/ +- `POST /messages/{id}/seen` — mark seen without archiving (stays in inbox) + +## Routing Destinations + +### Fully Dashboard (alerts for Johan) — port 9202 +```bash +curl -X POST http://localhost:9202/api/alerts \ + -H 'Content-Type: application/json' \ + -d '{"message": "...", "priority": "critical|warning|info"}' +``` + +**Priority levels:** +- **critical** — Act NOW. Security breach, service down, urgent personal matter, phishing attempt +- **warning** — Needs attention today. Bills due, action-required emails, Sophia-related, medical +- **info** — FYI. Interesting but not urgent. Can wait. + +**Route here:** Anything needing Johan's direct attention or decision. + +### James Dashboard (news/status) — port 9200 + +**News:** +```bash +curl -X POST http://localhost:9200/api/news \ + -H 'Content-Type: application/json' \ + -d '{"title": "...", "body": "...", "type": "info"}' +``` +**Route here:** Industry news, Kaseya/Lansweeper updates, interesting FYI items, routine notifications worth noting. + +**Deliveries:** +```bash +curl -X PUT http://localhost:9200/api/deliveries/upsert \ + -H 'Content-Type: application/json' \ + -d '{"description": "...", "carrier": "...", "retailer": "...", "tracking_number": "...", "tracking_url": "...", "expected_date": "...", "status": "shipped|in_transit|out_for_delivery|delivered"}' +``` +**Route here:** Any shipping/delivery/tracking email. Always use upsert (matches by tracking_number or description+retailer). + +**Tasks:** +```bash +curl -X POST http://localhost:9200/api/tasks \ + -H 'Content-Type: application/json' \ + -d '{"title": "...", "text": "...", "priority": "high|medium|low", "status": "pending", "owner": "james", "domain": "..."}' +``` +**Route here:** Actionable items for James (server updates, infrastructure tasks, Google Search Console issues). + +### Document Inbox (attachments) +```bash +curl -X POST http://localhost:8025/messages/{id}/to-docs +``` +**Route here:** Invoices, receipts, contracts, medical docs, tax forms, insurance docs — anything worth keeping/finding later. + +## Routing Rules + +### Always alert Johan (Fully) +- Sophia-related: medical, therapy, brain activator, pediatric suppliers, "S. Jongsma" +- Phishing attempts (preserve message, alert as critical) +- Personal correspondence from real humans needing reply +- Bills/payments requiring action +- Security incidents, infrastructure alerts (Uptime Kuma, SSL) +- Anything unusual or uncertain + +### Dashboard news +- Kaseya marketing → summarize → news +- Lansweeper updates → summarize → news +- Immich releases → news + create James task to update server +- Google Search Console → news + create James task +- Industry/tech updates worth noting + +### Process and archive +- Shipping/delivery → upsert to deliveries → archive +- Order confirmations → archive (ingest attachments if receipt/invoice) +- Security alerts (password changes, new logins) → archive +- Subscription confirmations → archive + +### Attachment handling +Ingest attachments for: invoices, receipts, bills, statements, contracts, legal docs, medical records, insurance docs, tax forms. +Skip: marketing images, logos, signatures, spam attachments. + +## Special Rules + +- **Verizon purchase/order emails** → alert Johan (warning) +- **Domain purchase inquiries** → reply "not for sale" → archive +- **Lingerie/fashion new collections** (Pain de Sucre, Fleur du Mal) → alert Johan (info) +- **inou verification codes** — should be junked by MC, but if they slip through → archive + +## Critical Rules + +1. **ALWAYS read the FULL message** before routing. No exceptions. +2. **When uncertain → alert Johan** (info priority). Better to over-alert than miss something. +3. **Do NOT ping the main session** unless truly urgent and needs real-time discussion. +4. **Do NOT re-report** — check context before alerting about the same email twice. +5. **Phishing = critical alert + preserve** — never delete, never auto-report abuse. + +## Account Context -**Account identifiers:** - `tj_jongsma_me` (tj@jongsma.me) — family/shared account - `johan_jongsma_me` (johan@jongsma.me) — Johan's personal account - `whatsapp` — WhatsApp messages - -All messages use unified ID format: `{source}:{sourceID}` (e.g., `tj_jongsma_me:12345`) - -## CRITICAL RULE - -**ALWAYS read the FULL message content before triaging. NO EXCEPTIONS.** - -Every email gets read in full — regardless of sender, subject, or apparent category. The content determines the action. - -## Reporting Rule (2026-02-08, updated 2026-02-09) - -- **Needs Johan's attention** (kept in inbox, flagged, Sophia-related, action required) → Ping immediately -- **All other processing** (trash, archive, shipping updates, routine) → Batched summary at **9:00 AM** and **9:00 PM** ET via cron jobs -- Do NOT ping Johan for every routine email processed -- **DO NOT RE-REPORT:** Before pinging about an email, check recent session history / daily memory. If you already told Johan about it, don't tell him again. Un-actioned in inbox ≠ unreported. Emails stay in inbox until Johan acts — that's by design, not a signal to re-alert. - ---- - -## Attachment Processing - -When emails have attachments (`has_attachments: true`, `attachment_names: [...]`), decide if any are worth archiving. - -### Trigger Ingest (Forward to Documents Inbox) -```bash -curl -X POST "http://localhost:8025/messages/{id}/to-docs" \ - -H "Content-Type: application/json" \ - -d '{"attachments": ["invoice.pdf"]}' -``` - -MC fetches attachments and saves to `~/documents/inbox/` for DocSys processing. - -**Response:** -```json -{"saved": ["/home/johan/documents/inbox/invoice.pdf"], "errors": []} -``` - -**Ingest all attachments:** omit the `attachments` array or pass `{}`. - -### When to Ingest -**Trigger ingest for anything interesting/worth keeping:** -- Invoices, receipts, bills, statements -- Contracts, agreements, legal documents -- Medical records, insurance docs -- Tax forms (W-2, 1099, etc.) -- Any document worth keeping/finding later -- Use judgment — if it looks useful, ingest it - -**Skip these:** -- Marketing fluff, promo images -- Logos, signatures (image-only attachments) -- Spam attachments -- Duplicate documents already ingested - -### Supported Formats -- **Vision (K2.5):** .pdf, .png, .jpg, .jpeg, .gif, .webp, .tiff, .bmp, .doc, .docx, .odt, .rtf, .xls, .xlsx, .ppt, .pptx -- **Text (K2):** .txt, .md, .csv, .json, .xml, .yaml, .log - ---- - -## James Tasks (assign to myself) - -These emails are ACTIONABLE for me — create a dashboard task (owner: james), don't just archive: - -- **Google Search Console** (sc-noreply@google.com) — inou.com indexing issues, crawl errors, etc. -- **inou.com alerts** — uptime, errors, anything about the platform -- **Infrastructure alerts** — Uptime Kuma, server notifications, SSL expiry -- **Security alerts** — breach notifications, vulnerability disclosures for our stack - -**Workflow:** -1. Read the full email -2. Create task: `POST http://localhost:9200/api/tasks` with `owner: "james"`, appropriate domain -3. THEN archive the email - ---- - -## Sophia Triggers - -Anything matching these → Sophia's recovery folder (or ASK if unclear): -- "brain", "neuro", "therapy", "activator" -- Medical devices, equipment, serial numbers -- Pediatric suppliers (All About Pediatrics, Tri-Med, etc.) -- Insurance claims mentioning Sophia -- Any person name "S. Jongsma" or "Sophia" -- Therapy appointments, medical follow-ups - -## Conversation Detection - -- "Re:" from a person = active conversation, READ IT -- Thread replies are not spam -- Check context before deleting - -## Action Required Detection - -- Credits/refunds → check expiry, action needed? -- Payment reminders → notify Johan -- "ACTION REQUIRED" → read and assess -- Medical advice → flag for attention, don't just file - -## Phishing / Scam Detection - -**Phishing ≠ Spam.** Spam is junk. Phishing is fraud. Different handling. - -### Red Flags (read body carefully if ANY present): -- Unexpected "payment processed" or "transaction" language -- Phone numbers to call "if you didn't authorize" -- Urgent action required + money involved -- Generic sender names ("Your Teacher", "Support Team") -- Legitimate service used as delivery mechanism (Canva class, Google Doc share, etc.) -- Message-ID from unexpected domain (e.g., amazonses.com for a "Canva" email) -- Mismatched context (class invite containing payment info) - -### Phishing Workflow: -1. **PRESERVE** — do NOT delete. Move to a folder or keep in inbox. -2. **FLAG** — mark for Johan's attention -3. **ALERT** — ping Johan: "Phishing attempt detected, preserved for review" -4. **LOG** — dashboard entry with `action: "flagged"`, `reason: "Phishing - [brief description]"` -5. **DO NOT AUTO-REPORT** — Never send abuse reports to Canva/Google/PayPal/etc. without Johan's explicit approval. We don't want to become spam ourselves, and false positives happen. - -**Johan decides** if an abuse report is warranted. I preserve evidence, he takes action. - -### Why This Matters: -- Phishing evolves — today's "class invite" scam is tomorrow's "shared document" scam -- Evidence has value for reporting (if Johan chooses to) -- Auto-reporting could flag legitimate emails, annoy abuse teams, or get our accounts flagged - ---- - -## Delete Behavior - -**NEVER hard-delete. Always move to Trash.** - -Before moving to Trash: -1. Read full content -2. Summarize what it contains -3. Confirm no action is needed -4. Confirm it's NOT phishing (phishing = preserve, not trash) - -## Processed Email Routing - -### → Trash (delete) -- Marketing/promotional emails -- Spam -- Phishing attempts -- Newsletters not worth keeping -- Automated notifications with no value -- Test emails - -**Rule:** If Johan would never want to find this again → Trash - -### → Shopping (mark read, move) -- Order confirmations ONLY - -**Rule:** Actual order/purchase confirmation → mark read, move to `Folders/Shopping` - -### → Trash (after processing) -- Shipping notifications -- Delivery updates -- "Out for delivery" / "Delivered" notices - -**Workflow for shipping/delivery emails:** -1. Read the full message -2. **ALWAYS use upsert** (never POST): `PUT http://localhost:9200/api/deliveries/upsert` — matches by tracking_number or description+retailer, updates if found, creates if not. Include tracking_number when available. -3. THEN move to Trash - -This keeps the delivery schedule current without cluttering Shopping folder. - -### → Archive (keep but out of inbox) -- Processed bills after payment -- Travel confirmations (past trips) -- Payment receipts from subscriptions (reference value) -- Security alerts (password changes, new logins) - -**Rule:** Archive is for things worth FINDING AGAIN. If Johan would never search for it → Trash, not Archive. - -### → Trash (common false-archive candidates) -- **Amazon:** Everything except order confirmations and outliers (product recalls, credits). Promos, recommendations, "items you viewed", shipping updates (after updating deliveries) → all trash. -- **Retailers:** Marketing, sales, "new arrivals" → trash -- **Account notifications** with no future value → trash -- **Generic "your statement is ready"** → trash (he can check the app) - -### → Keep in Inbox (flag for Johan) -- Action required -- Bills/renewals pending payment -- Personal correspondence awaiting reply -- Anything Sophia-related -- Medical/insurance matters - -**Important:** After flagging, mark as seen so it doesn't retrigger: -```bash -curl -X POST "http://localhost:8025/messages/{id}/seen" -``` -This removes it from `/messages/new` without archiving/deleting. - -## Specific Sender Rules - -These override general routing: - -- **Kaseya Marketing:** Read fully -> Summarize (rundown) -> Post to Dashboard News -> **Trash**. -- **Lansweeper:** (Johan is ex-CTO) Read fully -> Summarize what's going on -> Post to Dashboard News -> **Trash**. -- **inou Verification Codes:** (noreply@inou.com) -> **Trash** immediately (Johan uses backdoors). -- **Immich:** (GitHub/Releases) Read fully -> **Trigger update on server 192.168.1.253** -> Post to Dashboard News -> **Trash**. -- **Lingerie & Beach Wear (e.g., Pain de Sucre, Fleur du Mal):** Use your judgment. If the email introduces a **new collection, series, or seasonal release**, **Keep in Inbox**. If it is just general sales/marketing/shipping alerts, **Trash**. -- **Domain Purchase Inquiries (e.g., Jacob):** -> Deny (Reply "not for sale") -> **Archive**. -- **DigiKey** (e.digikey.com): Marketing/newsletters → **Trash** immediately. -- **Verizon Wireless** (ecrmemail.verizonwireless.com): Purchase/order emails → **Keep in Inbox**. Even if they look like "abandoned cart" marketing, Johan may have an active purchase in progress. Only trash obvious generic promos (plan upgrades, add-a-line offers). - -## Mistakes Made - -### 2026-02-01 -- Hard-deleted GenerX thread (permanently lost) -- Deleted Amazon promo credit without checking if action needed -- Almost deleted MBL Brain Activator emails (Sophia's therapy device repair) -- Triaged MosaicDx by subject without reading their medical advice - -### 2026-02-02 -- **Canva phishing email:** Deleted as "educational marketing spam" without reading body. Missed scam payload ($769.68 fake payment + scam phone number). Should have: read full content, recognized phishing red flags, preserved + flagged for Johan. **Violated my own rule: "ALWAYS read the FULL message content before triaging."** - -Learn from these. Don't repeat. diff --git a/memory/shannon-scan-2026-02-14/inou-security-assessment-2026-02-14.pptx b/memory/shannon-scan-2026-02-14/inou-security-assessment-2026-02-14.pptx new file mode 100644 index 0000000000000000000000000000000000000000..3f4b2d6c6184ea2e012e602aadd8add11e64798d GIT binary patch literal 43621 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

qMULB1R1F1kYqrG*|*9R89@yi9N06(Knh^n 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 literal 0 HcmV?d00001 diff --git a/memory/updates/2026-02-14.json b/memory/updates/2026-02-14.json index 8a2f604..ab4d8bf 100644 --- a/memory/updates/2026-02-14.json +++ b/memory/updates/2026-02-14.json @@ -1,48 +1 @@ -{ - "date": "2026-02-14", - "timestamp": "2026-02-14T09:00:50-05:00", - "openclaw": { - "before": "2026.2.12", - "latest": "2026.2.13", - "after": "2026.2.13", - "updated": true - }, - "claude_code": { - "before": "2.1.42", - "latest": "2.1.42", - "updated": false - }, - "os": { - "available": 5, - "packages": [ - { - "name": "linux-generic", - "from": "6.8.0-94.96", - "to": "6.8.0-100.100" - }, - { - "name": "linux-headers-generic", - "from": "6.8.0-94.96", - "to": "6.8.0-100.100" - }, - { - "name": "linux-image-generic", - "from": "6.8.0-94.96", - "to": "6.8.0-100.100" - }, - { - "name": "sosreport", - "from": "4.5.6-0ubuntu4", - "to": "4.9.2-0ubuntu0~24.04.1" - }, - { - "name": "tailscale", - "from": "1.94.1", - "to": "1.94.2" - } - ], - "updated": true, - "reboot_required": false - }, - "gateway_restarted": true -} \ No newline at end of file +{"date":"2026-02-14","time":"21:00","os":{"status":"up_to_date","packages_upgraded":0,"reboot_required":false},"claude_code":{"previous":"2.1.33","current":"2.1.42","updated":true},"openclaw":{"previous":"2026.2.13","current":"2026.2.13","updated":false},"sessions_cleaned":{"orphan_files_removed":109,"orphan_size_mb":13.2,"cron_run_keys_removed":10},"working_context_updated":true,"daily_memory_updated":true} diff --git a/memory/working-context.md b/memory/working-context.md index 3abbb4b..97e892c 100644 --- a/memory/working-context.md +++ b/memory/working-context.md @@ -1,34 +1,35 @@ # Working Context -## Last Session: 2026-02-14 (Cigna Investigation, Shannon Launch, Dashboard Redesign) +## Last Session: 2026-02-14 (Prima Integration, Shannon Scan, Fully Dashboard) -### What happened -- myCigna autonomous login: real Chrome on Xvfb:99, CDP port 9224, 2FA via MC email -- **Baycare ventilator fraud discovered**: billing E0465 ($3,125/mo) for ventilator Sophia doesn't have. 3+ prior notifications ignored. Complaint drafted. -- Shannon security scanner live on Zurich VPS, scanning inou.com portal with K2.5 via CCR router -- Alert dashboard (9202) redesigned: Braun/mid-century vibe, Sora font, larger text, calendar nav -- Evening briefing cron upgraded from systemEvent → real isolated briefing -- Both briefings now require release notes details + cover infrastructure/industry topics -- Context hygiene rules added: subagent for side questions, dual units, thinking levels +### What happened today (Valentine's Day) +- **Prima (UMich Brain MRI AI):** Johan wants to offer as inou service. Intelligent series selection (don't run all 10K slices). RunPod serverless L40S on demand. Full spec written: `/home/johan/dev/inou/specs/prima-integration.md` (39KB). Key: 73-75% GPU cost savings with smart series selection. +- **Shannon Security Scan completed:** 2 CRITICAL (hardcoded backdoor 250365, session hijacking), 2 HIGH (session fixation, brute force). Auth/SQLi/SSRF clean. Deliverables at ~/clawd/memory/shannon-scan-2026-02-14/ +- **Baycare ventilator fraud:** Billing E0465 ($3,125/mo) for ventilator Sophia doesn't have. Formal complaint drafted. Strategy: don't pay, let them escalate. +- **myCigna autonomous login:** Real Chrome on Xvfb:99, CDP 9224, 2FA via MC email — zero human intervention +- **Fully Dashboard (9202) major redesign:** Braun aesthetic, pulse-ox camera feed (MJPEG 7pm-8am), long-press to dismiss, alerts moved left. This is now Johan's unified inbox on Fire tablet. +- **James Dashboard (9200):** News items now clickable with source URLs +- **Context hygiene rules:** Added to AGENTS.md (side questions → subagent, both units always, thinking level matching) +- **Cron finalized:** 7 jobs total. Evening briefing upgraded to isolated agentTurn. +- **Diana Yusha lab report:** Added arthritis rebuttal (Russian) — CRP/ESR don't support diagnosis -### Active Projects -- **Shannon scan** — running on zurich (82.24.174.112), workflow `inou-com_shannon-1771049779401`. Check results. -- **Baycare fraud** — complaint at ~/documents/records/medical/. Strategy: don't pay, let escalate. -- **MC triage** — deployed, monitoring for false negatives -- **Alert Dashboard** — port 9202, redesigned, source at /home/johan/dev/alert-dashboard/ -- **Azure Files Backup** — feature complete, blocked on `az login` MFA. Free account expires ~Feb 27! -- **Dr. Madan call** — Sunday Feb 15 at 2PM ET (reminder cron set, auto-deletes) +### Open threads +- **Prima:** Spec done, Johan to review when he wakes. Next: implementation (RunPod Docker image, inou API endpoints) +- **Shannon findings:** Johan needs to review 2 CRITICAL + 2 HIGH. Backdoor code 250365 is urgent. +- **Baycare fraud:** Complaint ready. Johan decides when to escalate to Cigna fraud division / state AG +- **Azure Files Backup:** Feature complete but blocked on `az login` MFA. Free account expires ~Feb 27! +- **Real estate:** Diana Geegan negotiating 851 ($6.35M sell) + 801 (buy). Johan ~$171K short of $6.2M goal. +- **Colorado Camel Milk:** Order #16698 still awaiting shipment (weather hold) -### Pending -- Kaseya device policy change (BYOD → managed devices). Johan needs to request MacBook Pro. -- Real estate: 851 Brightwaters listed at $7.25M on Zillow. Diana Geegan negotiating. -- Belastingdienst: Dutch corporate tax filing reminder for 2025 -- Dashboard redesign feedback pending (Johan viewing on Fire tablet) +### Johan's state +- Went to sleep ~8:19 PM (first sleep block). Night shift starts ~10:30 PM. +- Tomorrow is Sunday — weekly synthesis due, Docker/HAOS updates -### Key Reminders -- bird for X/Twitter — ALWAYS -- memory_search before responding to references — MANDATORY -- Right model for right job: Opus for judgment, K2.5 for grunt work -- Side questions → subagent always (context hygiene) -- Both units always (metric + imperial) -- myCigna: username=tjjongsma, real Chrome on Xvfb (not headless Playwright) +### Infrastructure +- forge (192.168.1.16): All services healthy. GPU OCR live on port 8090. +- Shannon VPS: OAuth token configured, scans working +- Alert dashboard: port 9202, James dashboard: port 9200 +- Generator ran 3x today (power outages), GenerX serviced + +### Desk layout +- Left: TUI console | Center: Fully tablet (9202) | Center-back: Sophia cam | Right: James Dashboard (9200) diff --git a/scripts/shannon-pptx.py b/scripts/shannon-pptx.py new file mode 100644 index 0000000..0c006ba --- /dev/null +++ b/scripts/shannon-pptx.py @@ -0,0 +1,405 @@ +#!/usr/bin/env python3 +"""Generate Shannon Security Assessment PowerPoint for inou.com""" + +from pptx import Presentation +from pptx.util import Inches, Pt, Emu +from pptx.dml.color import RGBColor +from pptx.enum.text import PP_ALIGN, MSO_ANCHOR +from pptx.enum.shapes import MSO_SHAPE + +prs = Presentation() +prs.slide_width = Inches(13.333) +prs.slide_height = Inches(7.5) + +# Colors +BG_DARK = RGBColor(0x0F, 0x11, 0x17) +BG_CARD = RGBColor(0x1A, 0x1D, 0x26) +ACCENT_RED = RGBColor(0xFF, 0x4D, 0x4D) +ACCENT_ORANGE = RGBColor(0xFF, 0xA5, 0x00) +ACCENT_GREEN = RGBColor(0x4E, 0xC9, 0xB0) +ACCENT_BLUE = RGBColor(0x56, 0x9C, 0xD6) +TEXT_WHITE = RGBColor(0xF0, 0xF0, 0xF0) +TEXT_GRAY = RGBColor(0x99, 0x99, 0x99) +TEXT_DIM = RGBColor(0x66, 0x66, 0x66) + +def set_slide_bg(slide, color=BG_DARK): + bg = slide.background + fill = bg.fill + fill.solid() + fill.fore_color.rgb = color + +def add_text_box(slide, left, top, width, height, text, font_size=18, + color=TEXT_WHITE, bold=False, align=PP_ALIGN.LEFT, font_name="Segoe UI"): + txBox = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(height)) + tf = txBox.text_frame + tf.word_wrap = True + p = tf.paragraphs[0] + p.text = text + p.font.size = Pt(font_size) + p.font.color.rgb = color + p.font.bold = bold + p.font.name = font_name + p.alignment = align + return txBox + +def add_card(slide, left, top, width, height, color=BG_CARD): + shape = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(left), Inches(top), Inches(width), Inches(height)) + shape.fill.solid() + shape.fill.fore_color.rgb = color + shape.line.fill.background() + shape.shadow.inherit = False + return shape + +def add_severity_badge(slide, left, top, severity, color): + shape = slide.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, + Inches(left), Inches(top), Inches(1.8), Inches(0.4)) + shape.fill.solid() + shape.fill.fore_color.rgb = color + shape.line.fill.background() + tf = shape.text_frame + tf.word_wrap = False + p = tf.paragraphs[0] + p.text = severity + p.font.size = Pt(14) + p.font.color.rgb = TEXT_WHITE + p.font.bold = True + p.font.name = "Segoe UI" + p.alignment = PP_ALIGN.CENTER + tf.paragraphs[0].space_before = Pt(2) + +def add_bullet_list(slide, left, top, width, height, items, font_size=14, color=TEXT_WHITE): + txBox = slide.shapes.add_textbox(Inches(left), Inches(top), Inches(width), Inches(height)) + tf = txBox.text_frame + tf.word_wrap = True + for i, item in enumerate(items): + if i == 0: + p = tf.paragraphs[0] + else: + p = tf.add_paragraph() + p.text = item + p.font.size = Pt(font_size) + p.font.color.rgb = color + p.font.name = "Segoe UI" + p.space_after = Pt(6) + return txBox + +# ============================================================ +# SLIDE 1: Title +# ============================================================ +slide = prs.slides.add_slide(prs.slide_layouts[6]) # Blank +set_slide_bg(slide) + +add_text_box(slide, 1, 0.8, 11, 0.6, "🔐 SHANNON", 16, TEXT_DIM, font_name="Consolas") +add_text_box(slide, 1, 1.4, 11, 1.2, "Security Assessment Report", 48, TEXT_WHITE, bold=True) +add_text_box(slide, 1, 2.6, 11, 0.6, "inou.com — Penetration Test Results", 24, ACCENT_BLUE) + +add_text_box(slide, 1, 4.0, 5, 0.4, "Date: February 14, 2026", 16, TEXT_GRAY) +add_text_box(slide, 1, 4.5, 5, 0.4, "Tool: Shannon Lite v1.0.0 (Keygraph)", 16, TEXT_GRAY) +add_text_box(slide, 1, 5.0, 5, 0.4, "Model: Claude Sonnet 4.5 (Anthropic)", 16, TEXT_GRAY) +add_text_box(slide, 1, 5.5, 5, 0.4, "Runtime: ~1.5 hours", 16, TEXT_GRAY) +add_text_box(slide, 1, 6.0, 5, 0.4, "Scope: Auth, XSS, Injection, SSRF, AuthZ", 16, TEXT_GRAY) + +# Decorative line +shape = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, + Inches(1), Inches(3.4), Inches(3), Pt(3)) +shape.fill.solid() +shape.fill.fore_color.rgb = ACCENT_RED +shape.line.fill.background() + +# ============================================================ +# SLIDE 2: Executive Summary +# ============================================================ +slide = prs.slides.add_slide(prs.slide_layouts[6]) +set_slide_bg(slide) + +add_text_box(slide, 0.8, 0.4, 11, 0.6, "Executive Summary", 36, TEXT_WHITE, bold=True) + +# Stats cards +for i, (num, label, color) in enumerate([ + ("4", "Confirmed\nVulnerabilities", ACCENT_RED), + ("2", "Critical", ACCENT_RED), + ("2", "High", ACCENT_ORANGE), + ("3", "Out of Scope\n(Need Internal)", TEXT_GRAY), +]): + x = 0.8 + i * 3.1 + add_card(slide, x, 1.3, 2.8, 1.8) + add_text_box(slide, x + 0.3, 1.4, 2.2, 0.9, num, 54, color, bold=True, align=PP_ALIGN.CENTER) + add_text_box(slide, x + 0.3, 2.2, 2.2, 0.7, label, 14, TEXT_GRAY, align=PP_ALIGN.CENTER) + +# Summary table +add_card(slide, 0.8, 3.5, 11.7, 3.6) +headers = ["Category", "Findings", "Severity", "Status"] +cols = [1.0, 4.5, 8.5, 10.5] +for j, h in enumerate(headers): + add_text_box(slide, cols[j], 3.6, 2.5, 0.4, h, 13, ACCENT_BLUE, bold=True) + +rows = [ + ("Authentication", "4 vulnerabilities", "2× CRITICAL, 2× HIGH", "⚠️ Exploited"), + ("Authorization", "0 vulnerabilities", "—", "✅ Passed"), + ("XSS", "2 code-level issues", "Requires auth", "🔒 Out of scope"), + ("Injection", "1 path traversal", "Requires server access", "🔒 Out of scope"), + ("SSRF", "0 vulnerabilities", "—", "✅ Passed"), +] +for i, (cat, finding, sev, status) in enumerate(rows): + y = 4.2 + i * 0.55 + color = ACCENT_RED if "Exploited" in status else ACCENT_GREEN if "Passed" in status else TEXT_GRAY + add_text_box(slide, cols[0], y, 3, 0.4, cat, 13, TEXT_WHITE, bold=True) + add_text_box(slide, cols[1], y, 3.5, 0.4, finding, 13, TEXT_WHITE) + add_text_box(slide, cols[2], y, 2, 0.4, sev, 13, color) + add_text_box(slide, cols[3], y, 2, 0.4, status, 13, color) + +# ============================================================ +# SLIDE 3: CRITICAL — Hardcoded Backdoor +# ============================================================ +slide = prs.slides.add_slide(prs.slide_layouts[6]) +set_slide_bg(slide) + +add_text_box(slide, 0.8, 0.4, 11, 0.5, "AUTH-VULN-07", 14, TEXT_DIM, font_name="Consolas") +add_text_box(slide, 0.8, 0.8, 11, 0.7, "Universal Authentication Bypass via\nHardcoded Backdoor Code", 32, TEXT_WHITE, bold=True) +add_severity_badge(slide, 0.8, 1.7, "🔴 CRITICAL", ACCENT_RED) + +add_card(slide, 0.8, 2.3, 5.5, 4.5) +add_text_box(slide, 1.1, 2.4, 5, 0.4, "What Shannon Found", 16, ACCENT_BLUE, bold=True) +add_bullet_list(slide, 1.1, 2.9, 5, 3.5, [ + "• Hardcoded verification code: 250365", + "• Works for ANY email address — instant account takeover", + "• Bypasses email ownership verification completely", + "• Affects both web (/verify) and mobile API", + "• Source comment: \"TODO: Remove backdoor code 250365", + " before production\"", + "", + "• HIPAA violation: unauthorized access to PHI", +], 13, TEXT_WHITE) + +add_card(slide, 6.6, 2.3, 5.9, 4.5) +add_text_box(slide, 6.9, 2.4, 5.4, 0.4, "Proof of Exploitation", 16, ACCENT_RED, bold=True) +add_text_box(slide, 6.9, 2.9, 5.4, 4, + "1. Navigate to inou.com/start\n" + "2. Enter ANY email address\n" + "3. Click \"Continue\"\n" + "4. Enter code: 250365\n" + "5. Full account access granted\n\n" + "Tested with:\n" + " pentest@example.com → ✅ authenticated\n" + " victim@example.com → ✅ authenticated\n\n" + "Code location:\n" + " lib/dbcore.go:347\n" + " if code != 250365 && ...", + 13, TEXT_WHITE, font_name="Consolas") + +# ============================================================ +# SLIDE 4: CRITICAL — Session Hijacking +# ============================================================ +slide = prs.slides.add_slide(prs.slide_layouts[6]) +set_slide_bg(slide) + +add_text_box(slide, 0.8, 0.4, 11, 0.5, "AUTH-VULN-05", 14, TEXT_DIM, font_name="Consolas") +add_text_box(slide, 0.8, 0.8, 11, 0.7, "Session Hijacking via Missing\nServer-Side Invalidation", 32, TEXT_WHITE, bold=True) +add_severity_badge(slide, 0.8, 1.7, "🔴 CRITICAL", ACCENT_RED) + +add_card(slide, 0.8, 2.3, 5.5, 4.5) +add_text_box(slide, 1.1, 2.4, 5, 0.4, "The Problem", 16, ACCENT_BLUE, bold=True) +add_bullet_list(slide, 1.1, 2.9, 5, 3.5, [ + "• /logout only clears the client-side cookie", + "• Session token NOT invalidated server-side", + "• Stolen cookies remain valid INDEFINITELY", + "• No session timeout mechanism exists", + "• Mobile API has NO logout endpoint at all", + "• No audit log of active sessions", + "• No ability to revoke sessions remotely", +], 13, TEXT_WHITE) + +add_card(slide, 6.6, 2.3, 5.9, 4.5) +add_text_box(slide, 6.9, 2.4, 5.4, 0.4, "Attack Flow", 16, ACCENT_RED, bold=True) +add_text_box(slide, 6.9, 2.9, 5.4, 4, + "1. Victim authenticates → cookie set\n" + " login=d74520ade621d4b8\n\n" + "2. Attacker captures cookie\n" + " (via XSS, MITM, physical access)\n\n" + "3. Victim logs out\n" + " → Cookie cleared client-side ONLY\n\n" + "4. Attacker injects stolen cookie\n" + " → Full access STILL WORKS ✅\n\n" + "5. Attacker has permanent access\n" + " → No expiration, no revocation", + 13, TEXT_WHITE, font_name="Consolas") + +# ============================================================ +# SLIDE 5: HIGH — Session Fixation + Brute Force +# ============================================================ +slide = prs.slides.add_slide(prs.slide_layouts[6]) +set_slide_bg(slide) + +add_text_box(slide, 0.8, 0.4, 11, 0.6, "High Severity Findings", 36, TEXT_WHITE, bold=True) + +# Left card: Session Fixation +add_card(slide, 0.8, 1.2, 5.8, 5.8) +add_severity_badge(slide, 1.1, 1.4, "🟠 HIGH", ACCENT_ORANGE) +add_text_box(slide, 1.1, 1.9, 5.2, 0.5, "AUTH-VULN-04: Session Fixation", 18, TEXT_WHITE, bold=True) +add_bullet_list(slide, 1.1, 2.5, 5.2, 4, [ + "Session IDs (dossierID) not rotated after auth", + "", + "• Same dossierID reused across ALL logins", + "• DossierID is deterministic per email", + "• Attacker authenticates → captures ID", + "• Logs out → re-authenticates", + "• Session ID is IDENTICAL both times", + "", + "Proven: login=f4d22b2137cf536c", + "persisted across logout/re-login cycle", + "", + "Combined with missing invalidation →", + "predictable, permanent session tokens", +], 13, TEXT_WHITE) + +# Right card: Brute Force +add_card(slide, 6.9, 1.2, 5.8, 5.8) +add_severity_badge(slide, 7.2, 1.4, "🟠 HIGH", ACCENT_ORANGE) +add_text_box(slide, 7.2, 1.9, 5.2, 0.5, "AUTH-VULN-01: Brute Force", 18, TEXT_WHITE, bold=True) +add_bullet_list(slide, 7.2, 2.5, 5.2, 4, [ + "Zero rate limiting on /verify endpoint", + "", + "• No rate limiting (0 HTTP 429s)", + "• No account lockout mechanism", + "• No CAPTCHA after failed attempts", + "• No attempt tracking or monitoring", + "", + "20 rapid requests in 3.1 seconds", + "→ All returned HTTP 200", + "", + "Brute force time estimates:", + " Sequential: ~21 hours", + " 10 parallel: ~2.1 hours", + " 100 parallel: ~12 minutes", +], 13, TEXT_WHITE) + +# ============================================================ +# SLIDE 6: Out of Scope (Internal Access Required) +# ============================================================ +slide = prs.slides.add_slide(prs.slide_layouts[6]) +set_slide_bg(slide) + +add_text_box(slide, 0.8, 0.4, 11, 0.6, "Out of Scope — Require Internal Access", 36, TEXT_WHITE, bold=True) +add_text_box(slide, 0.8, 1.0, 11, 0.4, "Valid vulnerabilities in code, but exploitation requires auth or server access", 16, TEXT_GRAY) + +# Card 1: Path Traversal +add_card(slide, 0.8, 1.6, 3.8, 5.2) +add_text_box(slide, 1.1, 1.7, 3.3, 0.4, "INJ-VULN-01", 12, TEXT_DIM, font_name="Consolas") +add_text_box(slide, 1.1, 2.0, 3.3, 0.5, "Path Traversal in\nFile Upload", 18, TEXT_WHITE, bold=True) +add_bullet_list(slide, 1.1, 2.7, 3.3, 3.8, [ + "Sanitization on wrong variable", + "(fileName vs relPath)", + "", + "filepath.Base() applied to", + "fileName, NOT relPath", + "", + "Enables arbitrary file write:", + "• /etc/cron.d/ (persistence)", + "• Web shell placement", + "• Config overwrite", + "", + "upload.go:182-186, :451-462", +], 11, TEXT_WHITE) + +# Card 2: DICOM XSS +add_card(slide, 4.9, 1.6, 3.8, 5.2) +add_text_box(slide, 5.2, 1.7, 3.3, 0.4, "XSS-VULN-01", 12, TEXT_DIM, font_name="Consolas") +add_text_box(slide, 5.2, 2.0, 3.3, 0.5, "DICOM Stored XSS\nvia SeriesDescription", 18, TEXT_WHITE, bold=True) +add_bullet_list(slide, 5.2, 2.7, 3.3, 3.8, [ + "DICOM tag 0008,103E", + "stored without HTML encoding", + "", + "Rendered via innerHTML in", + "/api/series endpoint", + "", + "Attack: upload crafted DICOM", + "with XSS in SeriesDescription", + "", + "Blocked by: auth requirement", + "(needs valid login cookie)", +], 11, TEXT_WHITE) + +# Card 3: LLM Prompt Injection XSS +add_card(slide, 9.0, 1.6, 3.8, 5.2) +add_text_box(slide, 9.3, 1.7, 3.3, 0.4, "XSS-VULN-02", 12, TEXT_DIM, font_name="Consolas") +add_text_box(slide, 9.3, 2.0, 3.3, 0.5, "LLM Prompt Injection\n→ Stored XSS", 18, TEXT_WHITE, bold=True) +add_bullet_list(slide, 9.3, 2.7, 3.3, 3.8, [ + "Freeform tracker input passed", + "to Google Gemini LLM", + "", + "LLM output stored unsanitized", + "Rendered via insertAdjacentHTML", + "", + "Attack: prompt injection to", + "make LLM generate XSS payload", + "in tracker question field", + "", + "Blocked by: auth requirement", + "(needs valid login cookie)", +], 11, TEXT_WHITE) + +# ============================================================ +# SLIDE 7: What Passed +# ============================================================ +slide = prs.slides.add_slide(prs.slide_layouts[6]) +set_slide_bg(slide) + +add_text_box(slide, 0.8, 0.4, 11, 0.6, "What Passed ✅", 36, ACCENT_GREEN, bold=True) + +for i, (title, detail) in enumerate([ + ("Authorization Controls", + "10 authorization candidates tested — ALL passed. RBAC correctly prevents cross-user access to dossiers and medical data. API layer properly enforces ownership checks."), + ("SQL Injection", + "All database queries use parameterized statements. No injection vectors found in any endpoint. SQLite with AES-256-GCM encryption at rest."), + ("Command Injection", + "No command injection vectors found. External binary calls properly sanitized."), + ("SSRF", + "Initial finding reclassified as Open Redirect (HTTP 303 client-side redirect, not server-side fetch). No true SSRF present."), + ("Network Hardening", + "Internal services (API on 8082, DICOM on 8765) properly bound to localhost only. Single public port (8443). No subdomains exposed."), +]): + y = 1.3 + i * 1.15 + add_card(slide, 0.8, y, 11.7, 1.0) + add_text_box(slide, 1.1, y + 0.05, 2.8, 0.4, title, 16, ACCENT_GREEN, bold=True) + add_text_box(slide, 1.1, y + 0.45, 11, 0.5, detail, 12, TEXT_GRAY) + +# ============================================================ +# SLIDE 8: Recommendations +# ============================================================ +slide = prs.slides.add_slide(prs.slide_layouts[6]) +set_slide_bg(slide) + +add_text_box(slide, 0.8, 0.4, 11, 0.6, "Remediation Priorities", 36, TEXT_WHITE, bold=True) + +recs = [ + ("1", "IMMEDIATE", "Remove backdoor code 250365", + "lib/dbcore.go:347 — Delete the hardcoded bypass. Deploy today.", ACCENT_RED), + ("2", "IMMEDIATE", "Implement server-side session invalidation", + "Add session revocation to /logout. Add session expiration (e.g., 24h). Add session management UI.", ACCENT_RED), + ("3", "THIS WEEK", "Add rate limiting to /verify", + "Max 5 attempts per email per 15 minutes. Add CAPTCHA after 3 failures. Log failed attempts.", ACCENT_ORANGE), + ("4", "THIS WEEK", "Rotate session IDs on authentication", + "Generate new dossierID or session token after each successful /verify. Invalidate old tokens.", ACCENT_ORANGE), + ("5", "NEXT SPRINT", "Sanitize DICOM metadata + LLM output", + "HTML-encode SeriesDescription before rendering. Sanitize all LLM output before insertAdjacentHTML.", ACCENT_BLUE), + ("6", "NEXT SPRINT", "Fix path traversal in upload.go", + "Apply filepath.Base() to relPath (not just fileName). Validate paths stay within upload directory.", ACCENT_BLUE), + ("7", "HARDENING", "Add HSTS headers + restrict CORS", + "Strict-Transport-Security on all responses. Replace wildcard CORS with specific origins.", TEXT_GRAY), +] + +for i, (num, timeline, title, detail, color) in enumerate(recs): + y = 1.2 + i * 0.85 + add_card(slide, 0.8, y, 11.7, 0.75) + add_text_box(slide, 1.0, y + 0.05, 0.3, 0.35, num, 18, color, bold=True, font_name="Consolas") + add_text_box(slide, 1.5, y + 0.02, 1.6, 0.35, timeline, 11, color, bold=True) + add_text_box(slide, 3.2, y + 0.02, 4, 0.35, title, 14, TEXT_WHITE, bold=True) + add_text_box(slide, 3.2, y + 0.38, 9, 0.35, detail, 11, TEXT_GRAY) + +# ============================================================ +# Save +# ============================================================ +out = "/home/johan/clawd/memory/shannon-scan-2026-02-14/inou-security-assessment-2026-02-14.pptx" +prs.save(out) +print(f"Saved: {out}")