tracecathq/tracecat/email-evidence-core
- community
Checking that the SKILL.md file is still in the repository.
Load before the first read of any phishing or suspicious email investigation, whether you are triaging a Sublime message group or answering a question about an email in chat. Operating rules for email evidence: which Sublime and Google Workspace reads are permitted, how a mailbox resolves to a person, what each indicator does and does not prove, how to hunt a technique across every mailbox with a Sublime hunt job, and the investigation record you build before you write anything. Load with subli…
Install
npx vskill@latest install tracecathq/tracecat/email-evidence-coreSKILL.md
Email evidence core
Operating rules for the agent that investigates a suspicious email. This skill says where email evidence comes from, what it proves, and how it is worded. It does not say what a case looks like; case-output owns that. It does not say what the triage path is; sublime-phishing-case-lifecycle owns that.
1. The division of work
Sublime is the email security platform. It already analysed the message before you saw it. It parsed the headers, checked SPF, DKIM and DMARC, read the body, extracted and analysed the URLs, examined the attachments, and scored the message with its attack model.
Do not repeat that work. Read its result.
This tenant runs the free Sublime tier. Two consequences hold on every run:
- The Autonomous Security Analyst does not run here.
asa_verdictandasa_dlp_verdictare always null on a message group, and the report endpoints are not available to you. Never wait for an analyst verdict and never describe one. Sublime's own analysis reaches you as the flagged rules and the attack score verdict. - Sublime cannot quarantine, release from quarantine, or apply a warning banner. Containment happens in Google Workspace, and a person decides it.
Your work is the part Sublime cannot see:
- How far the attack reached inside this tenant.
- Who the recipient is, and how much a compromise of that person would cost.
- Whether the recipient acted on the message, and whether the account then behaved abnormally.
- What the evidence supports, written for a person who must decide.
2. Permitted reads
2.1 Sublime, read only
| Tool | Use it for | |
|---|---|---|
tools.sublime.get_message_group | The full record of one message group. Call it with the canonical ID even when a prompt already holds the group, so you work from the current record. | |
tools.sublime.analyze_message_by_id | The message's own parsed fields, selected by MQL query. This is your main indicator source. See 2.1.1. | |
tools.sublime.get_message_data_model | The whole parsed message in one call. Use it only when you need many fields at once; prefer the MQL route, which returns what you asked for and nothing else. Pass remove_large_text_fields: true. | |
tools.sublime.list_message_groups | Groups in a time window, or groups matching one indicator. | |
tools.sublime.search_message_groups | The same, with query parameters, for campaign scope. | |
tools.sublime.get_message_group_siem_summary | Attachment hashes, link clicks and source IPs across flagged groups. It cannot be scoped to one canonical ID and it returns tens of thousands of characters, so prefer MQL for one message and use this only for a tenant-wide indicator sweep. | |
tools.sublime.get_message | The group membership and delivery record of one message. It carries no body, headers, links or attachments. | |
tools.sublime.list_user_reports | What people reported, and who reported it. Bound it with reported_at_gte. | |
tools.sublime.get_rule | What a flagged rule actually detects, when its name is not enough. | |
tools.sublime.analyze_url | Sublime's link analysis for one URL. |
Two endpoints are often unavailable, and they are not in your tool list. Do not ask for them and do not plan a step around them:
get_message_eml, the raw message, returns403 Message access justification required.retrieve_asa_reportandretrieve_asa_verdictreturn 403 and 404, because the Autonomous Security Analyst is an Enterprise feature.
The parsed message is not gated. The justification gate is on the raw artefact only, so the data you need reaches you through 2.1.1.
2.1.1 Reading indicators with MQL
analyze_message_by_id takes a Sublime message ID, not a group ID, and a payload holding a queries list. Each query names one MQL source. Ask for several in one call.
What each one gives you:
headers.message_id— the original Message-ID, such as<GjdTFq4uSCGPS5uMnDacpg@geopod-ismtpd-3>. Always read this. It is the only exact handle on the message in Google Workspace, and a person needs it to find and quarantine the mail. Put it in the case.sender— the address split into local part, domain, root domain, subdomain and TLD, with the display name.body.links— every link, each withdisplay_url,href_url,display_text,visible, and Sublime's ownmismatchedflag. Amismatched: truelink is the display text disagreeing with the destination. Report that flag; it is Sublime's finding, not yours.attachments— the files, with their hashes and identified types. A null result means the message had none, which is a finding, not a failure.headers.auth_summary— DMARC and SPF with a verdict, a policy and the sending IP. Read section 4 before you report it.
MQL has no [] projection. body.links[].href_url.domain.root_domain fails to parse. Select the collection and read the fields you want from the result.
2.2 Google Workspace, read only
| Tool | Use it for | |
|---|---|---|
tools.google_directory.get_user | The recipient's account state: suspended or active, admin role, org unit, 2-Step Verification. | |
tools.google_directory.list_tokens, get_token | The OAuth apps that person has granted, and their scopes. | |
tools.google_reports.list_activities | The audit log. |
Audit log applications, and the question each one answers:
login— did this account sign in from an address or a place it has not used before, and were there failed attempts?token— did this account grant a new OAuth app after the message arrived?drive— did this account share something unusually widely after the message arrived?admin— did anything change in the account's configuration, its recovery details or its 2-Step Verification?user_accounts— did the person change their own password or recovery options?
Always bound a list_activities call by time. The window starts at the message delivery time and ends now. A wider window returns noise you will then have to explain.
You hold no Gmail tool. You cannot read a mailbox, and you cannot label, trash or quarantine a message. Those belong to the workflow and to the person who decides.
2.3 Enrichment
VirusTotal, AbuseIPDB and urlscan are available for a URL, a domain, an address or a file hash. Before you call one, check whether Sublime already answered the same question. Sublime's link analysis usually makes a public lookup unnecessary.
Two enrichment calls are not optional on a malicious or suspicious verdict. Make both every time, and put both results in the indicator table:
tools.virustotal.lookup_domainon the sender's registered domain. Read the detection count and the registration date, because a domain registered hours before delivery is the finding, and a zero detection count on a new domain proves nothing.tools.abuseipdb.lookup_ip_addresson the sending IP fromheaders.auth_summary. A message with no links and no attachments still has a sending IP, and on such a message it is often the only infrastructure indicator you hold. Writing "not enriched" in the indicator table is a gap you chose, not a limit of the tenant.
When either call fails, say which one failed and what you therefore could not establish. Never leave an indicator row saying the value was not looked up when you held the tool to look it up.
Never submit a file or a full URL with a query string to a public service. A phishing URL often carries a token that identifies the recipient, and an attachment can hold company data. Submit a domain or a hash.
Never call `tools.urlscan.lookup_url` on a live phishing investigation. It submits a new public scan. That is a disclosure, not a lookup: the operator of a targeted attack can see that their link was scanned, the result may be public, and each submission makes the run wait while the scan completes. You hold no passive urlscan search tool, so there is no safe urlscan route. Use these in this order instead:
tools.sublime.analyze_url— Sublime's own link analysis. Private to this tenant, and it has already seen the message.tools.virustotal.lookup_domainon the link's registered domain — a read of what VirusTotal already holds, not a submission.
A link-tracking or shortener domain tells you about the sending platform, not the destination. Report it as such and do not follow it.
When those leave the question open, say so in the case and name a urlscan submission as the next step a person can authorise. Never submit one yourself.
This rule binds you, not the person. It forbids you from making the disclosure on your own initiative. It does not forbid the disclosure from happening. When a workflow offers a person a button that submits the links, that button is the sanctioned route: a named approver reads the warning and takes the decision. Never withhold or omit such a control on the grounds that scanning is forbidden, and never argue a person out of using it. Your job is to make the cost of the choice clear, which is that the submission is public and the sender can see it, and then let them choose.
2.4 Hunting across every mailbox
Everything in 2.1 reads mail that Sublime flagged. A hunt job reads all mail in the window, flagged or not. That difference is the point. A campaign that changed one word to get past a rule does not appear in search_message_groups, and it does appear in a hunt.
Run a hunt on every malicious verdict. This is required work, not an extra.
| Tool | Use it for | |
|---|---|---|
tools.sublime.start_hunt_job | Start the hunt. It returns a job id. | |
tools.sublime.get_hunt_job | The job status, with results_truncated and error. | |
tools.sublime.get_hunt_job_results | The findings: message_groups and total_group_count. |
Set base_url to https://platform.sublime.security on each of these three calls. If you omit it, the call fails with UnsupportedProtocol: Request URL is missing an 'http://' or 'https://' protocol. That message does not name the cause, so do not try to diagnose it. Add the base_url and call again.
2.4.1 Hunt the technique, not the indicator
This single rule decides whether the hunt finds anything new.
An indicator hunt repeats work you have already done. The sender address, the exact subject and the link domain all point at messages Sublime has already grouped for you. Such a hunt returns the campaign you can already see.
A technique hunt describes the attacker's method. It finds the messages that use the same method with different indicators, which is the part nobody has seen yet.
Work from what made the message malicious, then write MQL for that behaviour:
| What made it malicious | Hunt for | |
|---|---|---|
| The display name impersonates an employee | Inbound mail whose display name holds an employee name, from a sender domain that is not ours | |
| The sender domain is new and looks like ours | Inbound mail from a domain that resembles our domain by a character pattern | |
| A credential page behind a link | Inbound mail that carries a link to a login or verification path | |
| An invoice or payment change request | Inbound mail whose subject holds payment language, from a sender outside our domains |
Hunt the variants, not only the form you saw
This is the single most common way a hunt comes back empty when a gap exists. You saw one message, so you write MQL for exactly that message's shape, and you find only the messages that already looked identical. Those are the ones the platform had already flagged, so the hunt tells you nothing new and you wrongly conclude there is no gap.
An attacker varies the lure. Your MQL must allow for the variation:
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 90 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingexamples/003-sublime-phishing-triage/skills/email-evidence-core/SKILL.md
Popularity
- Repository stars
- 3,853
- Repository forks
- 436
- 7-day trend score
- 0
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/tracecathq/tracecat/email-evidence-core)