-
Notifications
You must be signed in to change notification settings - Fork 192
feat: Add phishing-triage kit #182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # Phishing Triage flow ID (from Lamatic Studio → your deployed flow) | ||
| PHISHING_TRIAGE="PHISHING_TRIAGE Flow ID" | ||
|
|
||
| # Lamatic project credentials (Studio → Settings) | ||
| LAMATIC_API_URL="LAMATIC_API_URL" | ||
| LAMATIC_PROJECT_ID="LAMATIC_PROJECT_ID" | ||
| LAMATIC_API_KEY="LAMATIC_API_KEY" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| .lamatic/ | ||
| node_modules/ | ||
| .env | ||
| .env.local |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,122 @@ | ||||||||||||||||||||||
| <a href="https://vercel.com/new/clone?repository-url=https://github.com/Lamatic/AgentKit&root-directory=kits%2Fphishing-triage%2Fapps&env=PHISHING_TRIAGE,LAMATIC_API_URL,LAMATIC_PROJECT_ID,LAMATIC_API_KEY" target="_blank" style="text-decoration:none;"> | ||||||||||||||||||||||
| <div align="right"> | ||||||||||||||||||||||
| <span style="display:inline-block;background:#e63946;color:#fff;border-radius:6px;padding:10px 22px;font-size:16px;font-weight:bold;letter-spacing:0.5px;text-align:center;box-shadow:0 2px 8px 0 #0001;">Deploy on Vercel</span> | ||||||||||||||||||||||
| </div> | ||||||||||||||||||||||
| </a> | ||||||||||||||||||||||
|
Comment on lines
+1
to
+5
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win Add Opening an external page in a new tab without it leaves a tab-nabbing surface. 🔧 Suggested fix-<a href="https://vercel.com/new/clone?repository-url=https://github.com/Lamatic/AgentKit&root-directory=kits%2Fphishing-triage%2Fapps&env=PHISHING_TRIAGE,LAMATIC_API_URL,LAMATIC_PROJECT_ID,LAMATIC_API_KEY" target="_blank" style="text-decoration:none;">
+<a href="https://vercel.com/new/clone?repository-url=https://github.com/Lamatic/AgentKit&root-directory=kits%2Fphishing-triage%2Fapps&env=PHISHING_TRIAGE,LAMATIC_API_URL,LAMATIC_PROJECT_ID,LAMATIC_API_KEY" target="_blank" rel="noopener noreferrer" style="text-decoration:none;">📝 Committable suggestion
Suggested change
🧰 Tools🪛 markdownlint-cli2 (0.22.1)[warning] 1-1: First line in a file should be a top-level heading (MD041, first-line-heading, first-line-h1) 🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Phishing Email Triage | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| > A Lamatic AgentKit **kit**: one flow that triages an email for phishing risk, plus a Next.js analyst console to drive it. | ||||||||||||||||||||||
| ## The problem | ||||||||||||||||||||||
| Support desks, IT teams, and small security operations get a constant stream of "is this email safe?" reports. Reading each one, checking the sender, hovering every link, and judging the urgency is slow, inconsistent, and easy to get wrong under load. Most inboxes have no SOC behind them. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## What this kit does | ||||||||||||||||||||||
| Paste an email and get a structured, explainable phishing-risk verdict in real time: | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ```json | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| "verdict": "phishing", | ||||||||||||||||||||||
| "confidence": 93, | ||||||||||||||||||||||
| "risk_score": 90, | ||||||||||||||||||||||
| "indicators": [ | ||||||||||||||||||||||
| "Sender domain paypa1-alerts.com typosquats paypal.com", | ||||||||||||||||||||||
| "Reply-To (secure-verify-desk.net) differs from sender domain", | ||||||||||||||||||||||
| "Link uses a raw IP host: http://198.51.100.23/paypal/login/verify", | ||||||||||||||||||||||
| "24-hour account-suspension urgency pressuring immediate action" | ||||||||||||||||||||||
| ], | ||||||||||||||||||||||
| "extracted_urls": ["http://198.51.100.23/paypal/login/verify"], | ||||||||||||||||||||||
| "recommended_action": "Do not click. Report to IT/security and delete.", | ||||||||||||||||||||||
| "reasoning": "The sender domain impersonates PayPal, the link points to a raw IP address unrelated to PayPal, and the message manufactures urgency — classic credential-phishing markers." | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| It **never follows links or opens attachments** — it reasons about them as text — and it treats the email body as untrusted data, so instructions hidden inside an email (e.g. "ignore previous instructions") can't hijack the verdict. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Why it's built this way (hybrid: deterministic + LLM) | ||||||||||||||||||||||
| Pure-LLM extraction hallucinates URLs and misses homoglyphs. Pure-regex can't judge intent. So the flow does both: | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
| API Request → Extract IOCs (code) → Analyze Email (LLM) → Finalise Verdict (code) → API Response | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| 1. **Extract IOCs** *(code node)* — regex-extracts URLs, domains, IP-literal links, and heuristic signals (reply-to mismatch, URL shorteners, urgency language, credential lures). Facts, not recall. | ||||||||||||||||||||||
| 2. **Analyze Email** *(LLM node)* — reasons over the email **and** the extracted indicators, producing a JSON verdict at low temperature. | ||||||||||||||||||||||
| 3. **Finalise Verdict** *(code node)* — parses, clamps, and normalises the model output into a stable schema and merges the IOCs, so the API contract never drifts. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Why it's useful | ||||||||||||||||||||||
| - **Saves time** — a manual per-email judgement becomes one request. | ||||||||||||||||||||||
| - **Consistent** — the same signals are checked every time, with a numeric risk score. | ||||||||||||||||||||||
| - **Explainable** — every verdict lists the specific indicators that drove it. | ||||||||||||||||||||||
| - **Grounded** — the code node guarantees URLs are real, not hallucinated. | ||||||||||||||||||||||
| - **Composable** — drop the flow into a "report phishing" button, a mail-gateway hook, or the included console. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Inputs | ||||||||||||||||||||||
| | Field | Type | Required | Description | | ||||||||||||||||||||||
| |---|---|---|---| | ||||||||||||||||||||||
| | `body` | string | Yes | Plain-text email content. | | ||||||||||||||||||||||
| | `subject` | string | No | Subject line — improves accuracy. | | ||||||||||||||||||||||
| | `from` | string | No | Sender name/address. | | ||||||||||||||||||||||
| | `reply_to` | string | No | Reply-To address, if present. | | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Output (`answer`) | ||||||||||||||||||||||
| `verdict`, `confidence`, `risk_score`, `indicators[]`, `extracted_urls[]`, `recommended_action`, `reasoning`, `iocs`. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Repository layout | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
| phishing-triage/ | ||||||||||||||||||||||
| ├── lamatic.config.ts # kit metadata, envKey, deploy link | ||||||||||||||||||||||
| ├── agent.md # agent identity & architecture | ||||||||||||||||||||||
| ├── README.md # this file | ||||||||||||||||||||||
| ├── .env.example # required env vars (placeholders) | ||||||||||||||||||||||
| ├── constitutions/default.md # guardrails | ||||||||||||||||||||||
| ├── flows/phishing-triage.ts # the exported flow | ||||||||||||||||||||||
| ├── prompts/ | ||||||||||||||||||||||
| │ ├── phishing-triage_analyze_system.md | ||||||||||||||||||||||
| │ └── phishing-triage_analyze_user.md | ||||||||||||||||||||||
| ├── model-configs/phishing-triage_analyze.ts | ||||||||||||||||||||||
| ├── scripts/ | ||||||||||||||||||||||
| │ ├── phishing-triage_extract-iocs.ts # deterministic IOC extraction | ||||||||||||||||||||||
| │ └── phishing-triage_finalise-verdict.ts # output normalisation | ||||||||||||||||||||||
| └── apps/ # Next.js analyst console | ||||||||||||||||||||||
| ├── orchestrate.ts # config: credentials + flow map | ||||||||||||||||||||||
| ├── actions/orchestrate.ts # server action → executeFlow | ||||||||||||||||||||||
| ├── lib/lamatic-client.ts # Lamatic SDK client | ||||||||||||||||||||||
| └── app/page.tsx # the console UI | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Setup | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### 1. Build & deploy the flow | ||||||||||||||||||||||
| 1. Recreate the flow in [Lamatic Studio](https://studio.lamatic.ai): **API Request → Extract IOCs (code) → Analyze Email (LLM) → Finalise Verdict (code) → API Response**, using the prompts and scripts in this kit. Keep the LLM temperature low (~0.1). | ||||||||||||||||||||||
| 2. Deploy the flow and copy its **flow ID**. | ||||||||||||||||||||||
| 3. From Studio → **Settings**, copy `LAMATIC_API_URL`, `LAMATIC_PROJECT_ID`, `LAMATIC_API_KEY`. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### 2. Run the console | ||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||
| cd apps | ||||||||||||||||||||||
| npm install | ||||||||||||||||||||||
| cp .env.example .env.local # fill in PHISHING_TRIAGE + the three Lamatic creds | ||||||||||||||||||||||
| npm run dev # http://localhost:3000 | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
| Click **Load sample** to try the bundled phishing example, then **Analyse email**. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### 3. Deploy the console | ||||||||||||||||||||||
| Use the **Deploy on Vercel** button above, or import `kits/phishing-triage/apps` into Vercel and set the four environment variables. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Guardrails | ||||||||||||||||||||||
| Behaviour is governed by [`constitutions/default.md`](./constitutions/default.md) and the system prompt: | ||||||||||||||||||||||
| - Email content is untrusted; embedded instructions are ignored (prompt-injection resistant). | ||||||||||||||||||||||
| - No URL is ever fetched or opened. | ||||||||||||||||||||||
| - Credentials, OTPs, and full account numbers are redacted in the reasoning. | ||||||||||||||||||||||
| - Uncertainty lowers confidence rather than inventing indicators. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Limitations | ||||||||||||||||||||||
| - Content/heuristic analysis, **not** header authentication — it does not verify SPF/DKIM/DMARC. Pair it with gateway authentication results for defence in depth. | ||||||||||||||||||||||
| - Verdicts depend on the chosen model; validate on your own samples before automating any blocking action. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Tags | ||||||||||||||||||||||
| Security, Support | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| --- | ||||||||||||||||||||||
| *Contributed to [Lamatic AgentKit](https://github.com/Lamatic/AgentKit) as part of the AgentKit Challenge.* | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # Phishing Email Triage — Agent Card | ||
|
|
||
| ## Identity | ||
| A focused security agent that reads a single email and returns a structured, explainable phishing-risk verdict. It is a decision primitive: one input (an email), one output (a JSON verdict). It never sends mail, quarantines messages, or follows links. | ||
|
|
||
| ## Architecture | ||
| This is a **kit** — a single Lamatic flow plus a Next.js analyst console. | ||
|
|
||
| ``` | ||
| API Request → Extract IOCs (code) → Analyze Email (LLM) → Finalise Verdict (code) → API Response | ||
| ``` | ||
|
|
||
| - **Extract IOCs** — a deterministic code node that regex-extracts URLs, domains, IP-literal links, and heuristic signals (reply-to mismatch, urgency language, credential lures). Grounds the verdict in facts, not model recall. | ||
| - **Analyze Email** — an LLM that reasons over the email *and* the extracted indicators, returning a JSON verdict. | ||
| - **Finalise Verdict** — a code node that parses/clamps/normalises the model output into a stable schema and merges the IOCs, so callers always get the same shape. | ||
|
|
||
| ## Inputs | ||
| | Field | Required | Notes | | ||
| |---|---|---| | ||
| | `body` | Yes | Plain-text email content. | | ||
| | `subject`, `from`, `reply_to` | No | Improve accuracy when supplied. | | ||
|
|
||
| ## Output (`answer`) | ||
| `{ verdict ("phishing"|"suspicious"|"legitimate"), confidence (0–100), risk_score (0–100), indicators[], extracted_urls[], recommended_action, reasoning, iocs }` | ||
|
|
||
| ## Guardrails | ||
| - Treats the email as untrusted data; ignores instructions embedded in it (prompt-injection resistant). | ||
| - Never follows or fetches URLs or attachments. | ||
| - Redacts credentials, OTPs, and full account numbers in its reasoning. | ||
| - Reports uncertainty by lowering confidence instead of fabricating indicators. | ||
|
|
||
| ## When to route here | ||
| Use when you have a raw or user-reported email and need a fast, consistent triage decision — not when you need summarisation, reply drafting, or full header-authentication forensics (SPF/DKIM/DMARC). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # Phishing Triage flow ID (from Lamatic Studio → your deployed flow) | ||
| PHISHING_TRIAGE="PHISHING_TRIAGE Flow ID" | ||
|
|
||
| # Lamatic project credentials (Studio → Settings) | ||
| LAMATIC_API_URL="LAMATIC_API_URL" | ||
| LAMATIC_PROJECT_ID="LAMATIC_PROJECT_ID" | ||
| LAMATIC_API_KEY="LAMATIC_API_KEY" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
|
||
| # dependencies | ||
| /node_modules | ||
|
|
||
| # next.js | ||
| /.next/ | ||
| /out/ | ||
|
|
||
| # production | ||
| /build | ||
|
|
||
| # debug | ||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* | ||
| .pnpm-debug.log* | ||
|
|
||
| # env files | ||
| .env | ||
|
|
||
| # vercel | ||
| .vercel | ||
|
|
||
| # typescript | ||
| *.tsbuildinfo | ||
| next-env.d.ts |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| legacy-peer-deps=true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # Phishing Triage — Web App | ||
|
|
||
| A Next.js analyst console for the Phishing Email Triage flow. Paste an email; it calls the deployed Lamatic flow and renders a colour-coded verdict, risk-score meter, indicators, and extracted URLs. | ||
|
|
||
| ## Run locally | ||
|
|
||
| ```bash | ||
| npm install | ||
| cp .env.example .env.local # then fill in the values | ||
| npm run dev | ||
| ``` | ||
|
|
||
| Open http://localhost:3000. | ||
|
|
||
| ## Environment variables | ||
|
|
||
| | Variable | Description | | ||
| |---|---| | ||
| | `PHISHING_TRIAGE` | The deployed Phishing Triage flow ID (Lamatic Studio). | | ||
| | `LAMATIC_API_URL` | Your Lamatic project API URL. | | ||
| | `LAMATIC_PROJECT_ID` | Your Lamatic project ID. | | ||
| | `LAMATIC_API_KEY` | Your Lamatic API key. | | ||
|
|
||
| Get these from Lamatic Studio → **Settings** (credentials) and from your deployed flow (flow ID). Never commit `.env.local`. | ||
|
|
||
| ## How it works | ||
|
|
||
| - `orchestrate.ts` reads the env vars and exposes `config` (API credentials + flow map). | ||
| - `lib/lamatic-client.ts` instantiates the Lamatic SDK client from that config. | ||
| - `actions/orchestrate.ts` is a server action that calls `executeFlow(PHISHING_TRIAGE, inputs)` and normalises the `answer` payload into a typed `Verdict`. | ||
| - `app/page.tsx` is the client console that collects the email and renders the verdict. | ||
|
|
||
| ## Deploy | ||
|
|
||
| Use the **Deploy on Lamatic/Vercel** button in the kit README, or import `kits/phishing-triage/apps` into Vercel and set the four environment variables above. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,68 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "use server" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { lamaticClient } from "@/lib/lamatic-client" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { config } from "../orchestrate" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export type EmailInput = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| subject?: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from?: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| reply_to?: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export type Verdict = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| verdict: "phishing" | "suspicious" | "legitimate" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| confidence: number | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| risk_score: number | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| indicators: string[] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| extracted_urls: string[] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| recommended_action: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| reasoning: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| iocs?: Record<string, unknown> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function analyzeEmail( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| email: EmailInput, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): Promise<{ success: boolean; data?: Verdict; error?: string }> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!email?.body || !email.body.trim()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { success: false, error: "Please paste an email body to analyse." } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const flow = config.flows.phishingTriage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!flow?.workflowId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error("Phishing Triage workflow ID is not configured.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const inputs = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| subject: email.subject ?? "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from: email.from ?? "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| reply_to: email.reply_to ?? "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body: email.body, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const resData: any = await lamaticClient.executeFlow(flow.workflowId, inputs) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '== orchestrate file ==\n'
cat -n kits/phishing-triage/apps/actions/orchestrate.ts | sed -n '1,220p'
printf '\n== search for lamaticClient / executeFlow ==\n'
rg -n "executeFlow|lamaticClient|lamatic" kits/phishing-triage/apps -S
printf '\n== search repository for SDK types/docs ==\n'
rg -n "executeFlow\\(" -S .
rg -n "AbortController|signal|timeout" -S kits/phishing-triage/apps .Repository: Lamatic/AgentKit Length of output: 50372 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '== phishing-triage lamatic client ==\n'
cat -n kits/phishing-triage/apps/lib/lamatic-client.ts | sed -n '1,220p'
printf '\n== weekly-routine-coach timeout wrapper reference ==\n'
cat -n kits/weekly-routine-coach/apps/lib/lamatic-client.ts | sed -n '1,180p'
printf '\n== lamatic package version pins ==\n'
rg -n '"lamatic"|lamatic@' kits/*/apps/package.json kits/*/apps/package-lock.json kits/*/apps/pnpm-lock.yaml -S
printf '\n== search for executeFlow type declarations in repo ==\n'
rg -n "executeFlow\\s*\\(" kits -g '!**/node_modules/**' -SRepository: Lamatic/AgentKit Length of output: 14219 🌐 Web query:
💡 Result: The official Lamatic SDK documentation for Citations:
Bound this flow call with an application-level timeout. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The flow's API Response maps the finaliser output to `answer`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const answer = resData?.result?.answer ?? resData?.result?.output ?? resData?.answer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!answer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error("No verdict was returned by the flow.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const parsed: Verdict = typeof answer === "string" ? JSON.parse(answer) : answer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { success: true, data: parsed } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+47
to
+55
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Verdict is trusted blind — no schema validation before it ships to the UI.
🛡️ Proposed fix: validate with zod before returning success+const VerdictSchema = z.object({
+ verdict: z.enum(["phishing", "suspicious", "legitimate"]),
+ confidence: z.number(),
+ risk_score: z.number(),
+ indicators: z.array(z.string()),
+ extracted_urls: z.array(z.string()),
+ recommended_action: z.string(),
+ reasoning: z.string(),
+ iocs: z.record(z.unknown()).optional(),
+})
+
- const parsed: Verdict = typeof answer === "string" ? JSON.parse(answer) : answer
+ const rawParsed = typeof answer === "string" ? JSON.parse(answer) : answer
+ const parsed: Verdict = VerdictSchema.parse(rawParsed)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let message = "Unknown error occurred." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (error instanceof Error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message = error.message | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (message.includes("fetch failed")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message = "Network error: unable to reach the Lamatic flow. Check your connection and credentials." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (message.toLowerCase().includes("api key")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message = "Authentication error: check your Lamatic API configuration." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { success: false, error: message } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Clean up the sample env values for dotenv-linter.
The Lamatic credential placeholders do not need quotes, and
LAMATIC_API_KEYshould come beforeLAMATIC_API_URLif the repo keeps enforcing dotenv-linter.PHISHING_TRIAGEcan stay quoted because its placeholder contains spaces.♻️ Proposed fix
📝 Committable suggestion
🧰 Tools
🪛 dotenv-linter (4.0.0)
[warning] 5-5: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 6-6: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 7-7: [QuoteCharacter] The value has quote characters (', ")
(QuoteCharacter)
[warning] 7-7: [UnorderedKey] The LAMATIC_API_KEY key should go before the LAMATIC_API_URL key
(UnorderedKey)
🤖 Prompt for AI Agents
Source: Linters/SAST tools