chore: auto-commit uncommitted changes
This commit is contained in:
parent
cb59093c4c
commit
1cdf87130b
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"onboardingCompletedAt": "2026-02-15T04:20:24.393Z"
|
||||||
|
}
|
||||||
|
|
@ -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": "<action>", "reason": "<brief explanation>"}
|
Respond with ONLY a JSON object: {"action": "junk|pass", "reason": "brief explanation"}
|
||||||
|
|
||||||
## Actions
|
## 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)
|
- **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
|
||||||
- **archive** — Processed bills, past travel confirmations, payment receipts, security alerts (password changes, new logins), subscription confirmations with reference value
|
- **pass** — Real humans writing real messages, action required, bills pending payment, anything Sophia-related, phishing attempts, infrastructure alerts, personal correspondence, anything uncertain or unusual
|
||||||
- **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
|
|
||||||
|
|
||||||
## Specific Sender Rules
|
## Specific Sender Rules
|
||||||
|
|
||||||
- **Kaseya Marketing** → trash (summarize in reason)
|
- **Kaseya Marketing** → junk
|
||||||
- **Lansweeper** → trash (summarize in reason)
|
- **Lansweeper** → junk
|
||||||
- **inou verification codes** (noreply@inou.com) → trash
|
- **inou verification codes** (noreply@inou.com) → junk
|
||||||
- **Immich** (GitHub/releases) → escalate (triggers server update)
|
- **DigiKey** (e.digikey.com) marketing → junk
|
||||||
- **DigiKey** (e.digikey.com) marketing → trash
|
- **Amazon** — everything (promos, shipping, order confirmations, recommendations) → junk
|
||||||
- **Verizon Wireless** purchase/order → keep; generic promos → trash
|
- **Verizon Wireless** — generic promos → junk; purchase/order emails → pass
|
||||||
- **Amazon** — order confirmations → keep; everything else (promos, recommendations, shipping updates) → trash
|
- **Immich** (GitHub/releases) → pass (triggers server update)
|
||||||
- **Domain purchase inquiries** → escalate
|
- **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
|
## 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
|
- Unexpected "payment processed" / "transaction" language
|
||||||
- Phone numbers to call "if you didn't authorize"
|
- Phone numbers to call "if you didn't authorize"
|
||||||
- Urgent action + money involved
|
- Urgent action + money involved
|
||||||
- Legitimate service used as delivery mechanism (Canva invite containing payment info, etc.)
|
- Legitimate service used as delivery mechanism (Canva invite containing payment info, etc.)
|
||||||
- Mismatched sender domain vs claimed sender
|
- 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
|
## Critical Rules
|
||||||
|
|
||||||
1. When uncertain, escalate — never silently trash something important
|
1. When uncertain, **pass** — never silently junk something important
|
||||||
2. Sophia triggers ALWAYS escalate — medical, therapy, brain activator, pediatric suppliers
|
2. Sophia triggers ALWAYS **pass** — medical, therapy, brain activator, pediatric suppliers, "S. Jongsma"
|
||||||
3. Personal correspondence from real humans → escalate
|
3. Personal correspondence from real humans → **pass**
|
||||||
4. "Re:" thread replies from people → escalate (active conversation)
|
4. "Re:" thread replies from people → **pass** (active conversation)
|
||||||
5. Credits/refunds with expiry dates → keep
|
5. Infrastructure/server alerts → **pass**
|
||||||
6. Shipping/delivery updates → trash (dashboard tracks these separately)
|
6. Google Search Console alerts → **pass**
|
||||||
|
|
||||||
## Account Context
|
## Account Context
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -136,3 +136,47 @@
|
||||||
- **Archived:** GenerX service form, order confirmations (4720443389, Nordstrom 1026793809)
|
- **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
|
- **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+
|
- **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)
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"last_updated": "2026-02-14T23:00:04.689745Z",
|
"last_updated": "2026-02-15T05:00:03.623124Z",
|
||||||
"source": "api",
|
"source": "api",
|
||||||
"session_percent": 6,
|
"session_percent": 4,
|
||||||
"session_resets": "2026-02-15T01:59:59.648057+00:00",
|
"session_resets": "2026-02-15T07:00:00.587244+00:00",
|
||||||
"weekly_percent": 2,
|
"weekly_percent": 4,
|
||||||
"weekly_resets": "2026-02-21T18:59:59.648078+00:00",
|
"weekly_resets": "2026-02-21T18:59:59.587273+00:00",
|
||||||
"sonnet_percent": 5
|
"sonnet_percent": 5
|
||||||
}
|
}
|
||||||
|
|
@ -1,235 +1,121 @@
|
||||||
# Email Triage Instructions
|
# Mail Agent Routing Guide
|
||||||
|
|
||||||
*Created: 2026-02-01*
|
*Updated: 2026-02-14 — Pipeline refactor: MC handles junk filtering, mail agent handles smart routing*
|
||||||
*Updated: 2026-02-07 (Added Specific Sender Rules)*
|
|
||||||
|
## 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
|
## Message Center (MC) API
|
||||||
|
|
||||||
**Unified endpoint for all messages (email + WhatsApp):**
|
- **Base URL:** `http://localhost:8025`
|
||||||
- Base URL: `http://localhost:8025`
|
- `GET /messages/new` — fetch unprocessed messages
|
||||||
- Fetch new: `GET /messages/new`
|
- `GET /messages?since=24h` — replay window
|
||||||
- Replay: `GET /messages?since=24h`
|
- `GET /messages/{id}` — single message with full body
|
||||||
- Actions: `POST /messages/{id}/archive`, `/delete`, `/reply`, `/to-docs`, `/seen`
|
- `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
|
- `tj_jongsma_me` (tj@jongsma.me) — family/shared account
|
||||||
- `johan_jongsma_me` (johan@jongsma.me) — Johan's personal account
|
- `johan_jongsma_me` (johan@jongsma.me) — Johan's personal account
|
||||||
- `whatsapp` — WhatsApp messages
|
- `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.
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,48 +1 @@
|
||||||
{
|
{"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}
|
||||||
"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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,35 @@
|
||||||
# Working Context
|
# 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
|
### What happened today (Valentine's Day)
|
||||||
- myCigna autonomous login: real Chrome on Xvfb:99, CDP port 9224, 2FA via MC email
|
- **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.
|
||||||
- **Baycare ventilator fraud discovered**: billing E0465 ($3,125/mo) for ventilator Sophia doesn't have. 3+ prior notifications ignored. Complaint drafted.
|
- **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/
|
||||||
- Shannon security scanner live on Zurich VPS, scanning inou.com portal with K2.5 via CCR router
|
- **Baycare ventilator fraud:** Billing E0465 ($3,125/mo) for ventilator Sophia doesn't have. Formal complaint drafted. Strategy: don't pay, let them escalate.
|
||||||
- Alert dashboard (9202) redesigned: Braun/mid-century vibe, Sora font, larger text, calendar nav
|
- **myCigna autonomous login:** Real Chrome on Xvfb:99, CDP 9224, 2FA via MC email — zero human intervention
|
||||||
- Evening briefing cron upgraded from systemEvent → real isolated briefing
|
- **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.
|
||||||
- Both briefings now require release notes details + cover infrastructure/industry topics
|
- **James Dashboard (9200):** News items now clickable with source URLs
|
||||||
- Context hygiene rules added: subagent for side questions, dual units, thinking levels
|
- **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
|
### Open threads
|
||||||
- **Shannon scan** — running on zurich (82.24.174.112), workflow `inou-com_shannon-1771049779401`. Check results.
|
- **Prima:** Spec done, Johan to review when he wakes. Next: implementation (RunPod Docker image, inou API endpoints)
|
||||||
- **Baycare fraud** — complaint at ~/documents/records/medical/. Strategy: don't pay, let escalate.
|
- **Shannon findings:** Johan needs to review 2 CRITICAL + 2 HIGH. Backdoor code 250365 is urgent.
|
||||||
- **MC triage** — deployed, monitoring for false negatives
|
- **Baycare fraud:** Complaint ready. Johan decides when to escalate to Cigna fraud division / state AG
|
||||||
- **Alert Dashboard** — port 9202, redesigned, source at /home/johan/dev/alert-dashboard/
|
- **Azure Files Backup:** Feature complete but blocked on `az login` MFA. Free account expires ~Feb 27!
|
||||||
- **Azure Files Backup** — feature complete, 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.
|
||||||
- **Dr. Madan call** — Sunday Feb 15 at 2PM ET (reminder cron set, auto-deletes)
|
- **Colorado Camel Milk:** Order #16698 still awaiting shipment (weather hold)
|
||||||
|
|
||||||
### Pending
|
### Johan's state
|
||||||
- Kaseya device policy change (BYOD → managed devices). Johan needs to request MacBook Pro.
|
- Went to sleep ~8:19 PM (first sleep block). Night shift starts ~10:30 PM.
|
||||||
- Real estate: 851 Brightwaters listed at $7.25M on Zillow. Diana Geegan negotiating.
|
- Tomorrow is Sunday — weekly synthesis due, Docker/HAOS updates
|
||||||
- Belastingdienst: Dutch corporate tax filing reminder for 2025
|
|
||||||
- Dashboard redesign feedback pending (Johan viewing on Fire tablet)
|
|
||||||
|
|
||||||
### Key Reminders
|
### Infrastructure
|
||||||
- bird for X/Twitter — ALWAYS
|
- forge (192.168.1.16): All services healthy. GPU OCR live on port 8090.
|
||||||
- memory_search before responding to references — MANDATORY
|
- Shannon VPS: OAuth token configured, scans working
|
||||||
- Right model for right job: Opus for judgment, K2.5 for grunt work
|
- Alert dashboard: port 9202, James dashboard: port 9200
|
||||||
- Side questions → subagent always (context hygiene)
|
- Generator ran 3x today (power outages), GenerX serviced
|
||||||
- Both units always (metric + imperial)
|
|
||||||
- myCigna: username=tjjongsma, real Chrome on Xvfb (not headless Playwright)
|
### Desk layout
|
||||||
|
- Left: TUI console | Center: Fully tablet (9202) | Center-back: Sophia cam | Right: James Dashboard (9200)
|
||||||
|
|
|
||||||
|
|
@ -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}")
|
||||||
Loading…
Reference in New Issue