Agent skill

Email Threat Posture

by SCStelz in SCStelz/security-investigator

Generate email threat protection reports and assess email security posture.

MITAuto-check passedDocuments & Office

Install Email Threat Posture

skills CLI
$ npx skills add SCStelz/security-investigator --skill email-threat-posture -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install SCStelz/security-investigator email-threat-posture --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/email-threat-posture .claude/skills/email-threat-posture && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
email-threat-posture
GitHub stars
249
Token cost
~9.7k tokens
SKILL.md length
2,396 words
Files
2
Skills in repo
22
Repo updated
First seen
Licence
MIT

At a glance

Generate email threat protection reports and assess email security posture.

  • Works in 12 steps: Prerequisites → Mail Flow & Threat Overview (Q1–Q4) → Protection Effectiveness (Q5–Q8) → …
  • Keywords like email threat report
  • SKILL.md covers Purpose, 📑 TABLE OF CONTENTS, ⚠️ CRITICAL WORKFLOW RULES -… and Email Protection Score Formula, plus 2 more sections
  • Reaches learn.microsoft.com

What it does

Email Threat Posture is an agent skill from SCStelz/security-investigator. Generate email threat protection reports and assess email security posture. Triggers on keywords like "email threat report", "email security posture", "phishing report", "MDO report", "Defender for Office 365 report", "ZAP effectiveness", "Safe Links report", "DMARC report", "spam report", "email volume report". Queries EmailEvents, EmailPostDeliveryEvents, UrlClickEvents, and EmailAttachmentInfo in Advanced Hunting for a posture assessment covering inbound mail flow, threat composition, phishing detection, email…

Its SKILL.md is about 9.7k tokens, which your agent loads only when the skill is triggered. The skill folder holds 1 other file (for example `svg-widgets.yaml`).

It sits in Documents & Office, covering Cloud office suites. It works with Microsoft 365. The repository describes itself as: Automated security investigation tool using Microsoft MCP Servers, GitHub Copilot, Python Modules and custom copilot-instructions. The licence is MIT.

When your agent uses it

  • Keywords like email threat report
  • Email security posture
  • Phishing report
  • Defender for Office 365 report

Example prompts

  • “email threat report”
  • “email security posture”
  • “phishing report”
  • “/email-threat-posture”

Workflow steps

12 steps, taken from the step headings in SKILL.md.

  1. Prerequisites
  2. Mail Flow & Threat Overview (Q1–Q4)
  3. Protection Effectiveness (Q5–Q8)
  4. Deep Dives & Governance (Q9–Q12)
  5. MDO Security Incidents (Q13–Q14)
  6. Score Computation & Report Generation
  7. DetectionMethods Is a JSON String
  8. AuthenticationDetails Is a JSON String
  9. ThreatTypes Is Pipe-Delimited
  10. Timestamp vs TimeGenerated
  11. IsFirstContact May Be Null
  12. LatestDeliveryAction vs DeliveryAction

What it can do on your machine

Read from SKILL.md and the folder at commit 51e1385. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are kql and markdown).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Hosts in commands or code, which the agent is likely to contact:

    • learn.microsoft.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Email Threat Posture loads about 9.7k tokens when it runs. Until then it costs about 194 tokens; SKILL.md has 2,396 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~194
When it runs · the whole SKILL.md, loaded when a task matches
~9.7k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from SCStelz/security-investigator at commit 51e1385, republished under its MIT licence (© SCStelz). 2,396 words, ~9,672 tokens.

Download SKILL.mdSave it as .claude/skills/email-threat-posture/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.
name
email-threat-posture
description
Generate email threat protection reports and assess email security posture. Triggers on keywords like "email threat report", "email security posture", "phishing report", "MDO report", "Defender for Office 365 report", "ZAP effectiveness", "Safe Links report", "DMARC report", "spam report", "email volume report". Queries EmailEvents, EmailPostDeliveryEvents, UrlClickEvents, and EmailAttachmentInfo in Advanced Hunting for a posture assessment covering inbound mail flow, threat composition, phishing detection, email authentication (DMARC/DKIM/SPF), post-delivery remediation (ZAP), Safe Links click protection, attachment analysis, detection method effectiveness, and delivery disposition. Supports inline chat, markdown file, and SVG dashboard output.
threat_pulse_domains
email
drill_down_prompt
Run email threat posture report — phishing trends, delivery gaps, protection effectiveness

Email Threat Protection Posture — Instructions

Purpose

This skill generates an Email Threat Protection Posture Report using Microsoft Defender for Office 365 (MDO) telemetry available through Advanced Hunting. It provides C-level visibility into how effectively the organization's email security stack is detecting, blocking, and remediating email-based threats.

What this skill covers:

DomainKey Questions Answered
📬 Mail Flow OverviewHow many inbound emails? What's the daily trend? Who are the top senders?
🛡️ Threat CompositionHow many phishing, spam, and malware threats were detected?
🎯 Phishing ProtectionHow many phishing emails were blocked vs delivered? Who are the most targeted users?
🔐 Email AuthenticationWhat are the DMARC/DKIM/SPF/CompAuth pass rates? Which domains fail authentication?
🧹 Post-Delivery RemediationHow effective is ZAP? How many remediations succeeded vs failed?
🔗 Safe Links ProtectionHow many URL clicks were scanned? Were any phishing clicks allowed through?
📎 Attachment AnalysisWhat attachment types are flowing through email? Were any malicious?
📊 Detection MethodsWhat detection technologies are catching threats (URL detonation, fingerprinting, etc.)?
📦 Delivery DispositionWhere do emails end up — inbox, junk, quarantine, blocked?
🚨 MDO IncidentsHow many security incidents were generated by Defender for Office? What severity, status, and types?

Data sources: EmailEvents, EmailPostDeliveryEvents, UrlClickEvents, EmailAttachmentInfo, SecurityAlert, SecurityIncident (Advanced Hunting)

References:

MANDATORY: When generating reports, copy URLs verbatim from this registry. NEVER construct, guess, or paraphrase a URL.

LabelCanonical URL
DOCS_EMAILEVENTShttps://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-emailevents-table
DOCS_EMAILPOSTDELIVERYhttps://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-emailpostdeliveryevents-table
DOCS_URLCLICKEVENTShttps://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-urlclickevents-table
DOCS_EMAILATTACHMENTINFOhttps://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-emailattachmentinfo-table
DOCS_MDO_EFFICACYhttps://learn.microsoft.com/en-us/defender-office-365/reports-mdo-email-collaboration-dashboard#appendix-advanced-hunting-efficacy-query-in-defender-for-office-365-plan-2
DOCS_MDO_OVERVIEWhttps://learn.microsoft.com/en-us/defender-office-365/mdo-about
DOCS_SECURITY_ALERThttps://learn.microsoft.com/en-us/azure/sentinel/data-connectors/microsoft-sentinel-security-alert
DOCS_ZAPhttps://learn.microsoft.com/en-us/defender-office-365/zero-hour-auto-purge
DOCS_SAFE_LINKShttps://learn.microsoft.com/en-us/defender-office-365/safe-links-about

📑 TABLE OF CONTENTS

  1. Critical Workflow Rules — Mandatory rules
  2. Email Protection Score Formula — Composite posture scoring
  3. Execution Workflow — Phase-by-phase query plan
  4. Sample KQL Queries — All queries (Q1–Q14)
  5. Output Modes — Inline vs Markdown report
  6. Inline Report Template — Chat-rendered format
  7. Markdown File Report Template — Disk-saved format
  8. Known Pitfalls — Schema quirks and edge cases
  9. Quality Checklist — Pre-delivery validation
  10. SVG Dashboard Generation — Visual dashboard from report

⚠️ CRITICAL WORKFLOW RULES - READ FIRST ⚠️

  1. Use RunAdvancedHuntingQuery by default — EmailEvents and related tables are XDR-native tables available in Advanced Hunting. Use Timestamp as the datetime column. If a query fails in AH, fall back to Sentinel Data Lake (query_lake) using TimeGenerated.

  2. Default lookback: 7 days — Unless the user specifies a different period. This provides a meaningful weekly snapshot for executive reporting while staying within AH's 30-day retention.

  3. ASK the user for output format before generating the report:

    • Inline chat summary (quick review in chat)
    • Markdown file report (detailed, archived to reports/email-threat-posture/)
    • Both (markdown + inline summary)
  4. ⛔ MANDATORY: Evidence-based analysis only — Report ONLY what query results show. Use the explicit absence pattern (✅ No [finding] detected) when queries return 0 results. Never fabricate data.

  5. Run queries in parallel batches where possible — Phase 1 queries (Q1–Q4) are independent. Phase 2 queries (Q5–Q8) are independent. Phase 3 queries (Q9–Q12) are independent.

  6. PII handling — Do NOT include recipient email addresses in inline reports or markdown files. Aggregate by domain or use anonymized references (e.g., "2 users in the contoso.com domain"). Top sender domains from external sources are acceptable.

  7. Percentages must be grounded — Always show both the percentage AND the raw count (e.g., "99.8% clean (5,851 of 5,864)").


Email Protection Score Formula

The Email Protection Score is a composite posture indicator summarizing the effectiveness of email security controls. Higher scores indicate stronger protection (inverse of a risk score).

Scoring Dimensions

$$ \text{EmailProtectionScore} = \sum_{i} \text{DimensionScore}_i $$

Each dimension contributes 0–20 points to a maximum of 100:

DimensionMax🟢 High (16–20)🟡 Medium (8–15)🔴 Low (0–7)
Threat Block Rate20≥95% of threats not in inbox (post-ZAP final state)80–94% remediated<80% remediated (threats remain in inbox)
Email Authentication20SPF+DMARC+DKIM all ≥95%Any one 80–94%Any one <80%
ZAP Effectiveness20≥95% ZAP success rate + 0 failed ZAPs80–94% success OR 1–2 failures<80% success OR ≥3 failures
Safe Links Protection200 phishing click-throughs AND active scanning1–2 phishing click-throughs≥3 phishing click-throughs OR no scanning
Phishing Delivery Rate200 phishing emails delivered (post-ZAP)1–5 phishing delivered (post-ZAP)>5 phishing still in mailboxes (post-ZAP)
Interpretation Scale
ScoreRatingAction
85–100✅ StrongExcellent posture — maintain current configurations
65–84🟡 GoodMinor gaps — review flagged dimensions
45–64🟠 Needs ImprovementMultiple weaknesses — prioritize remediation
0–44🔴 CriticalSignificant exposure — immediate action required

Execution Workflow

Phase 0: Prerequisites
  1. Confirm RunAdvancedHuntingQuery is available (EmailEvents tables are AH-native)
  2. Ask user for output format (inline / markdown / both)
  3. Confirm lookback period (default: 7 days)
Phase 1: Mail Flow & Threat Overview (Q1–Q4)

Run in parallel — no dependencies between queries.

QueryPurpose
Q1Inbound email summary with threat breakdown
Q2Email volume trend by day
Q3Delivery action and location breakdown
Q4Detection methods breakdown
Phase 2: Protection Effectiveness (Q5–Q8)

Run in parallel — no dependencies between queries.

QueryPurpose
Q5Email authentication pass rates (DMARC/DKIM/SPF/CompAuth)
Q6ZAP and post-delivery remediation summary
Q7Safe Links click activity summary
Q8Phishing emails delivered (not blocked)
Phase 3: Deep Dives & Governance (Q9–Q12)

Run in parallel — no dependencies between queries.

QueryPurpose
Q9Top phishing sender domains
Q10Most targeted recipients (aggregated)
Q11Attachment type distribution
Q12Post-ZAP threat state (latest delivery location)
Phase 4: MDO Security Incidents (Q13–Q14)

Run in parallel — no dependencies between queries.

QueryPurpose
Q13MDO incident summary by severity and status
Q14MDO incident type breakdown (top alert-driven incidents)

⚠️ SecurityAlert.Status is IMMUTABLE — always "New" regardless of actual state. These queries use the canonical SecurityAlert→SecurityIncident join to get real Status and Classification from the SecurityIncident table. See copilot-instructions.md Known Table Pitfalls.

Phase 5: Score Computation & Report Generation
  1. Compute per-dimension scores from Phase 1–4 data
  2. Sum dimension scores for composite Email Protection Score
  3. Generate report in requested output mode
  4. Offer SVG dashboard if not already requested

Sample KQL Queries

All queries below are verified against the EmailEvents family of tables. Use them exactly as written, substituting only the lookback period where noted. These queries use Timestamp for Advanced Hunting. If falling back to Data Lake, replace Timestamp with TimeGenerated.

Query 1: Inbound Email Summary with Threat Breakdown
kql
EmailEvents
| where Timestamp > ago(7d)
| where EmailDirection == "Inbound"
| summarize
    TotalInbound = count(),
    Clean = countif(isempty(ThreatTypes)),
    Phish = countif(ThreatTypes has "Phish"),
    Malware = countif(ThreatTypes has "Malware"),
    Spam = countif(ThreatTypes has "Spam"),
    HighConfPhish = countif(ConfidenceLevel has "High" and ThreatTypes has "Phish"),
    Blocked = countif(DeliveryAction == "Blocked"),
    Delivered = countif(DeliveryAction == "Delivered"),
    Junked = countif(DeliveryAction == "Junked"),
    DistinctSenders = dcount(SenderFromAddress),
    DistinctRecipients = dcount(RecipientEmailAddress)
| project TotalInbound, Clean, Phish, Malware, Spam, HighConfPhish,
    Blocked, Delivered, Junked, DistinctSenders, DistinctRecipients
Query 2: Email Volume Trend by Day
kql
EmailEvents
| where Timestamp > ago(7d)
| summarize
    Inbound = countif(EmailDirection == "Inbound"),
    Outbound = countif(EmailDirection == "Outbound"),
    IntraOrg = countif(EmailDirection == "Intra-org")
    by Day = bin(Timestamp, 1d)
| order by Day asc
Query 3: Delivery Action & Location Breakdown
kql
EmailEvents
| where Timestamp > ago(7d)
| where EmailDirection == "Inbound"
| summarize Count = count() by DeliveryAction, DeliveryLocation
| order by Count desc
Query 4: Detection Methods Breakdown
kql
EmailEvents
| where Timestamp > ago(7d)
| where isnotempty(DetectionMethods) and DetectionMethods != "{}"
| extend DetMethods = parse_json(DetectionMethods)
| extend FirstDetection = tostring(bag_keys(DetMethods)[0])
| extend FirstSubcategory = iif(
    FirstDetection != "" and array_length(DetMethods[FirstDetection]) > 0,
    strcat(FirstDetection, ": ", tostring(DetMethods[FirstDetection][0])),
    FirstDetection)
| summarize Count = count() by FirstSubcategory
| order by Count desc
Query 5: Email Authentication Pass Rates
kql
EmailEvents
| where Timestamp > ago(7d)
| where EmailDirection == "Inbound"
| extend AuthDetails = parse_json(AuthenticationDetails)
| extend
    DMARC = tostring(AuthDetails.DMARC),
    DKIM = tostring(AuthDetails.DKIM),
    SPF = tostring(AuthDetails.SPF),
    CompAuth = tostring(AuthDetails.CompAuth)
| summarize
    TotalEmails = count(),
    DMARCPass = countif(DMARC == "pass"),
    DMARCFail = countif(DMARC == "fail"),
    DKIMPass = countif(DKIM == "pass"),
    DKIMFail = countif(DKIM == "fail"),
    SPFPass = countif(SPF == "pass"),
    SPFFail = countif(SPF == "fail"),
    CompAuthPass = countif(CompAuth has "pass"),
    CompAuthFail = countif(CompAuth == "fail")
Query 6: ZAP & Post-Delivery Remediation Summary
kql
EmailPostDeliveryEvents
| where Timestamp > ago(7d)
| summarize
    TotalActions = count(),
    PhishZAP = countif(ActionType == "Phish ZAP"),
    MalwareZAP = countif(ActionType == "Malware ZAP"),
    SpamZAP = countif(ActionType == "Spam ZAP"),
    ThreatZAPTotal = countif(ActionType in ("Phish ZAP", "Malware ZAP", "Spam ZAP")),
    ManualRemediation = countif(ActionType has "Admin"),
    SuccessCount = countif(ActionResult == "Success"),
    ErrorCount = countif(ActionResult == "Error")
| project TotalActions, PhishZAP, MalwareZAP, SpamZAP, ThreatZAPTotal, ManualRemediation, SuccessCount, ErrorCount
kql
UrlClickEvents
| where Timestamp > ago(7d)
| summarize
    TotalClicks = count(),
    BlockedClicks = countif(ActionType == "ClickBlocked"),
    AllowedClicks = countif(ActionType == "ClickAllowed"),
    ClickedThrough = countif(IsClickedThrough == true),
    PhishClicks = countif(ThreatTypes has "Phish"),
    DistinctUrls = dcount(Url),
    DistinctUsers = dcount(AccountUpn)
Query 8: Phishing Emails Delivered (Not Blocked)
kql
EmailEvents
| where Timestamp > ago(7d)
| where ThreatTypes has "Phish"
| where DeliveryAction == "Delivered" or LatestDeliveryAction == "Delivered"
| summarize
    DeliveredPhish = count(),
    DistinctRecipients = dcount(RecipientEmailAddress),
    DistinctSenders = dcount(SenderFromAddress),
    Subjects = make_set(Subject, 5)
Query 9: Top Phishing Sender Domains
kql
EmailEvents
| where Timestamp > ago(7d)
| where ThreatTypes has "Phish"
| summarize
    Count = count(),
    DistinctRecipients = dcount(RecipientEmailAddress),
    DeliveredCount = countif(DeliveryAction == "Delivered" or LatestDeliveryAction == "Delivered")
    by SenderFromDomain
| top 10 by Count
Query 10: Most Targeted Recipients (Aggregated by Domain)
kql
EmailEvents
| where Timestamp > ago(7d)
| where isnotempty(ThreatTypes) and EmailDirection == "Inbound"
| extend RecipientDomain = tostring(split(RecipientEmailAddress, "@")[1])
| summarize
    ThreatCount = count(),
    PhishCount = countif(ThreatTypes has "Phish"),
    SpamCount = countif(ThreatTypes has "Spam"),
    MalwareCount = countif(ThreatTypes has "Malware"),
    DistinctRecipients = dcount(RecipientEmailAddress)
    by RecipientDomain
| order by ThreatCount desc
Query 11: Attachment Type Distribution
kql
EmailAttachmentInfo
| where Timestamp > ago(7d)
| summarize
    Count = count(),
    DistinctFiles = dcount(FileName),
    ThreatCount = countif(isnotempty(ThreatTypes))
    by FileType
| order by Count desc
| take 15
Query 12: Post-ZAP Threat State (Latest Delivery Location)
kql
EmailEvents
| where Timestamp > ago(7d)
| where EmailDirection == "Inbound"
| where isnotempty(ThreatTypes)
| summarize Count = count() by LatestDeliveryAction, LatestDeliveryLocation, ThreatTypes
| order by Count desc
Query 13: MDO Incident Summary by Severity and Status

Uses the canonical SecurityAlert→SecurityIncident join. Filters to ProductName == "Office 365 Advanced Threat Protection" and excludes Communication Compliance alerts (CC_ prefix).

kql
let MDOAlerts = SecurityAlert
| where TimeGenerated > ago(7d)
| where ProductName == "Office 365 Advanced Threat Protection"
| where AlertName !startswith "CC_"
| summarize arg_max(TimeGenerated, *) by SystemAlertId
| project SystemAlertId;
SecurityIncident
| where CreatedTime > ago(7d)
| summarize arg_max(TimeGenerated, *) by IncidentNumber
| mv-expand AlertId = AlertIds
| extend AlertId = tostring(AlertId)
| join kind=inner MDOAlerts on $left.AlertId == $right.SystemAlertId
| summarize IncidentCount = dcount(IncidentNumber) by Severity, Status, Classification
| order by Severity asc, IncidentCount desc
Query 14: MDO Incident Type Breakdown (Top Alert-Driven Incidents)

Groups incidents by title and alert composition to show the most common MDO-generated incident types.

kql
let MDOAlerts = SecurityAlert
| where TimeGenerated > ago(7d)
| where ProductName == "Office 365 Advanced Threat Protection"
| where AlertName !startswith "CC_"
| summarize arg_max(TimeGenerated, *) by SystemAlertId
| project SystemAlertId, AlertName, AlertSeverity, ProductName;
SecurityIncident
| where CreatedTime > ago(7d)
| summarize arg_max(TimeGenerated, *) by IncidentNumber
| mv-expand AlertId = AlertIds
| extend AlertId = tostring(AlertId)
| join kind=inner MDOAlerts on $left.AlertId == $right.SystemAlertId
| summarize
    IncidentCount = dcount(IncidentNumber),
    AlertCount = count(),
    OpenCount = dcountif(IncidentNumber, Status == "New" or Status == "Active"),
    ClosedCount = dcountif(IncidentNumber, Status == "Closed"),
    TruePositives = dcountif(IncidentNumber, Classification == "TruePositive")
    by AlertName, Severity
| order by IncidentCount desc
| take 10

Output Modes

Mode 1: Inline Chat Summary

Render the full analysis directly in the chat response. Best for quick review and C-level briefings.

Mode 2: Markdown File Report

Save a comprehensive report to disk at:

reports/email-threat-posture/Email_Threat_Protection_Report_YYYYMMDD_HHMMSS.md
Mode 3: Both

Generate the markdown file AND provide an inline summary in chat.

Always ask the user which mode before generating output.


Inline Report Template

Render the following sections in order. Omit sections only if explicitly noted as conditional.

🔴 URL Rule: All hyperlinks in the report MUST be copied verbatim from the URL Registry above. Do NOT generate, recall from memory, or paraphrase any URL. If a needed URL is not in the registry, use plain text (no hyperlink).

markdown
# 📧 Email Threat Protection Report

**Generated:** YYYY-MM-DD HH:MM UTC
**Data Source:** Microsoft Defender for Office 365 (Advanced Hunting)
**Analysis Period:** <StartDate> → <EndDate> (<N> days)
**Protected Mailboxes:** <DistinctRecipients>
**Total Inbound Emails:** <N>
**Email Protection Score:** <Score>/100 — <RATING>

---

## Executive Summary

<2-3 sentences: total inbound volume, threat detection rate, key findings, overall posture rating>

**Email Protection Score:** 🟢/🟡/🟠/🔴 <RATING> (<Score>/100)

---

## Key Metrics

| Metric | Value |
|--------|-------|
| Total Inbound Emails | <N> |
| Clean Email Rate | <N>% (<clean> of <total>) |
| Threats Detected | <N> (Phish: <N>, Spam: <N>, Malware: <N>) |
| Threats Blocked Pre-Delivery | <N> |
| Phishing Delivered (Now Remediated) | <N> |
| Threat ZAP Actions | <N> (Phish: <N>, Malware: <N>, Spam: <N>) |
| Total Post-Delivery Actions | <N> (includes system events) |
| ZAP Success Rate | <N>% (Failed: <N>) |
| Threats Still in Mailboxes (Post-ZAP) | <N> (Phish: <N>, Spam: <N>) |
| Safe Links Clicks Scanned | <N> |
| Phishing Click-Throughs | <N> |
| Distinct Senders | <N> |
| Protected Mailboxes | <N> |

---

## 📬 Mail Flow Overview

### Daily Volume Trend

<Table or sparkline showing inbound/outbound/intra-org by day>

| Day | Inbound | Outbound | Intra-org |
|-----|---------|----------|-----------|
| <date> | <N> | <N> | <N> |

**Observations:** <Note any spikes, trends, or anomalies>

---

## 🛡️ Threat Composition

### Threat Categories

| Category | Count | % of Threats |
|----------|-------|-------------|
| Phishing | <N> | <N>% |
| Spam | <N> | <N>% |
| Malware | <N> | <N>% |
| High-Confidence Phishing | <N> | — |

### Detection Methods

| Method | Count |
|--------|-------|
| <method> | <N> |

### Top Phishing Sender Domains

| Domain | Phish Count | Delivered | Recipients Hit |
|--------|-------------|-----------|----------------|
| <domain> | <N> | <N> | <N> |

<If Q9 returns 0 phishing domains:>
✅ No phishing sender domains detected.

---

## 📦 Delivery Disposition

### Initial Delivery Action

| Action | Location | Count |
|--------|----------|-------|
| Delivered | Inbox/folder | <N> |
| Blocked | Dropped | <N> |
| Blocked | Quarantine | <N> |
| Junked | Junk folder | <N> |

### Post-ZAP Threat State

<Shows where threats currently reside after ZAP remediation>

| Latest Action | Location | Threat Type | Count |
|---------------|----------|-------------|-------|
| <action> | <location> | <type> | <N> |

**Summary of current threat locations (post-ZAP):**

| Current Location | Threat Count | % of Threats |
|-----------------|-------------|-------------|
| 🟢 Quarantine | <N> | <N>% |
| 🟢 Junk folder | <N> | <N>% |
| 🟢 Blocked/Dropped/Failed | <N> | <N>% |
| 🟢 Deleted items | <N> | <N>% |
| 🔴 **Still in Inbox** | **<N>** | **<N>%** |
| **Total** | **<N>** | **100%** |

> Show the phishing vs spam breakdown for "Still in Inbox": e.g., "<N> phishing (<N> total threats including spam)"

---

## 🔐 Email Authentication

| Protocol | Pass Rate | Pass Count | Fail Count | Other/None |
|----------|-----------|------------|------------|------------|
| SPF | <N>% | <N> | <N> | <N> |
| DMARC | <N>% | <N> | <N> | <N> |
| DKIM | <N>% | <N> | <N> | <N> |
| CompAuth | <N>% | <N> | <N> | <N> |

> **Note:** "Other/None" = emails with no result for that protocol (e.g., no DKIM signature). A low DKIM pass rate with 0 failures means unsigned senders, not spoofing. Compare against DMARC and CompAuth for the complete authentication picture.

**Assessment:**
- <emoji> <finding for each protocol>

---

## 🧹 Post-Delivery Remediation (ZAP)

| Metric | Value |
|--------|-------|
| Threat ZAP Actions | <N> (Phish: <N>, Malware: <N>, Spam: <N>) |
| Total Post-Delivery Actions | <N> (includes system events, admin actions) |
| ZAP Success Rate | <N>% (<success> of <total>) |
| Failed Remediations | <N> |

> **Reporting guidance:** The Key Metrics "Threat ZAP Actions" row should show **only** the Phish + Malware + Spam ZAP count — NOT the TotalActions, which includes system-initiated post-delivery events (message trace updates, delivery location changes). TotalActions is shown separately with a clarifying note.

<If ErrorCount > 0:>
⚠️ **<N> ZAP remediation(s) failed** — manual follow-up recommended. Threats may remain in user mailboxes.

<If ErrorCount == 0:>
✅ All post-delivery remediations completed successfully.

---

## 🔗 Safe Links Protection

| Metric | Value |
|--------|-------|
| Total Clicks Scanned | <N> |
| Clicks Blocked | <N> |
| Clicks Allowed | <N> |
| Phishing Clicks | <N> |
| Click-Through Overrides | <N> |
| Distinct URLs Scanned | <N> |
| Users Protected | <N> |

<If PhishClicks > 0:>
🔴 **<N> phishing URL click(s) detected** — investigate affected users for credential compromise.

<If PhishClicks == 0:>
✅ No phishing URL click-throughs detected.

---

## 📎 Attachment Analysis

### Top Attachment Types

| File Type | Count | Distinct Files | Threats Detected |
|-----------|-------|----------------|------------------|
| <type> | <N> | <N> | <N> |

<If any ThreatCount > 0:>
⚠️ **Malicious attachments detected in <N> file type(s)** — verify delivery status and endpoint execution.

<If all ThreatCount == 0:>
✅ No malicious attachments detected in email flow.

---

## 🎯 Targeted Recipients

| Recipient Domain | Threat Count | Phish | Spam | Malware | Recipients |
|-----------------|-------------|-------|------|---------|------------|
| <domain> | <N> | <N> | <N> | <N> | <N> |

---

## Email Protection Score Card

```
┌──────────────────────────────────────────────────────┐
│       EMAIL PROTECTION SCORE: <NN>/100               │
│             Rating: <EMOJI> <RATING>                 │
├──────────────────────────────────────────────────────┤
│ Threat Block Rate    [<bar>] <N>/20  (<detail>)      │
│ Email Authentication [<bar>] <N>/20  (<detail>)      │
│ ZAP Effectiveness    [<bar>] <N>/20  (<detail>)      │
│ Safe Links Protection[<bar>] <N>/20  (<detail>)      │
│ Phishing Delivery    [<bar>] <N>/20  (<detail>)      │
└──────────────────────────────────────────────────────┘
```

---
## 🚨 MDO Security Incidents

### Incident Summary (Last <N> Days)

| Severity | Open | Closed | True Positive | Total |
|----------|------|--------|---------------|-------|
| 🔴 High | <N> | <N> | <N> | <N> |
| 🟠 Medium | <N> | <N> | <N> | <N> |
| 🟡 Low | <N> | <N> | <N> | <N> |
| 🔵 Informational | <N> | <N> | <N> | <N> |
| **Total** | **<N>** | **<N>** | **<N>** | **<N>** |

### Top MDO Incident Types

| Alert Name | Severity | Incidents | Open | Closed | True Positives |
|------------|----------|-----------|------|--------|----------------|
| <name> | <sev> | <N> | <N> | <N> | <N> |

<If Q13 returns 0 incidents:>
✅ No MDO-generated security incidents in the analysis period.

---
## Security Assessment

| Factor | Finding |
|--------|---------|
| <emoji> **<Factor>** | <Evidence-based finding> |

---

## Recommendations

1. <emoji> **<Priority action>** — <evidence and rationale>
2. ...

---

## Appendix: Query Execution Summary

| Query | Description | Records | Time |
|-------|-------------|---------|------|
| Q1 | Inbound Email Summary | <N> | <time> |
| Q2 | Daily Volume Trend | <N> | <time> |
| ... | ... | ... | ... |
| Q13 | MDO Incident Summary | <N> | <time> |
| Q14 | MDO Incident Types | <N> | <time> |

Markdown File Report Template

When outputting to markdown file, use the same structure as the Inline Report Template above, saved to:

reports/email-threat-posture/Email_Threat_Protection_Report_YYYYMMDD_HHMMSS.md

Include the following additional sections in the file report that are omitted from inline:

  1. Top sender domains table (full top 10 by volume with phish/spam breakdown)
  2. Authentication failure breakdown by domain (domains failing DMARC/DKIM/SPF)
  3. Overridden threats (emails detected as threats but allowed by policy)
  4. Complete detection methods table (all detection categories, not just top)
  5. First-contact phishing attempts (emails from never-before-seen senders flagged as phish)
  6. MDO security incidents — Full severity × status breakdown + top incident types from Q13/Q14
  7. Raw query references — note that full query definitions are in this SKILL.md file
Show full SKILL.md (1,025 more words)Show less
Markdown Section Ordering

Follow this exact section order in markdown file reports:

OrderSectionSource
1Header (with Total Inbound + Score)Template header
2Executive SummaryTemplate
3Key MetricsTemplate
4Mail Flow Overview (daily trend)Q2
5Threat Composition (categories + detection methods + top phish senders)Q1, Q4, Q9
6Delivery Disposition (initial + post-ZAP threat state)Q3, Q12
7Email Authentication (with auth failures by domain)Q5, QM2
8Post-Delivery Remediation (ZAP)Q6
9Safe Links ProtectionQ7
10Attachment AnalysisQ11
11Targeted RecipientsQ10
12— Deep-dive sections start here —
13Overridden ThreatsQM3
14First-Contact PhishingQM4
15MDO Security IncidentsQ13, Q14
16Top Sender Domains by VolumeQM1
17— Score and assessment —
18Email Protection Score CardComputed
19Security AssessmentSynthesized
20RecommendationsSynthesized
21Appendix: Query Execution SummaryAll queries
22ReferencesURL Registry

Key rule: Score Card → Assessment → Recommendations always come AFTER all data sections (including deep dives). This ensures the reader sees all evidence before the overall assessment.

Additional Queries for Markdown File Deep Dives

These queries provide enrichment data for the markdown file report only. Skip for inline mode.

QM1: Top Sender Domains by Volume
kql
EmailEvents
| where Timestamp > ago(7d)
| where EmailDirection == "Inbound"
| summarize
    EmailCount = count(),
    PhishCount = countif(ThreatTypes has "Phish"),
    SpamCount = countif(ThreatTypes has "Spam"),
    DistinctSenders = dcount(SenderFromAddress)
    by SenderFromDomain
| order by EmailCount desc
| take 10
QM2: Authentication Failures by Domain
kql
EmailEvents
| where Timestamp > ago(7d)
| where EmailDirection == "Inbound"
| extend AuthDetails = parse_json(AuthenticationDetails)
| extend
    DMARC = tostring(AuthDetails.DMARC),
    DKIM = tostring(AuthDetails.DKIM),
    SPF = tostring(AuthDetails.SPF)
| summarize
    TotalEmails = count(),
    DMARCFail = countif(DMARC == "fail"),
    DKIMFail = countif(DKIM == "fail"),
    SPFFail = countif(SPF == "fail")
    by SenderFromDomain
| where DMARCFail > 0 or DKIMFail > 0 or SPFFail > 0
| order by TotalEmails desc
| take 15
QM3: Overridden Threats (Allow Policies)
kql
EmailEvents
| where Timestamp > ago(7d)
| where OrgLevelAction == "Allow" and isnotempty(ThreatTypes)
| summarize Count = count() by ThreatTypes, OrgLevelPolicy, DetectionMethods
| order by Count desc
QM4: First-Contact Phishing Attempts
kql
EmailEvents
| where Timestamp > ago(7d)
| where EmailDirection == "Inbound"
| where IsFirstContact == true
| where ThreatTypes has "Phish" or UrlCount > 3
| summarize
    FirstContactCount = count(),
    PhishCount = countif(ThreatTypes has "Phish"),
    HighUrlCount = countif(UrlCount > 3),
    DistinctSenders = dcount(SenderFromAddress)
File Report Header
markdown
# Email Threat Protection Report

**Generated:** YYYY-MM-DD HH:MM UTC
**Data Source:** Microsoft Defender for Office 365 (Advanced Hunting)
**Analysis Period:** <StartDate> → <EndDate> (<N> days)
**Protected Mailboxes:** <DistinctRecipients>
**Total Inbound Emails:** <N>
**Email Protection Score:** <Score>/100 — <RATING>

---

Known Pitfalls

1. DetectionMethods Is a JSON String

Problem: DetectionMethods looks like it should be dynamic but is a string column containing JSON. Direct property access fails.

Solution: Always parse_json(DetectionMethods) before accessing sub-keys:

kql
| extend DetMethods = parse_json(DetectionMethods)
| extend FirstDetection = tostring(bag_keys(DetMethods)[0])
2. AuthenticationDetails Is a JSON String

Problem: Same as DetectionMethods — AuthenticationDetails is a string column, not dynamic.

Solution: Always parse_json(AuthenticationDetails):

kql
| extend AuthDetails = parse_json(AuthenticationDetails)
| extend DMARC = tostring(AuthDetails.DMARC)
3. ThreatTypes Is Pipe-Delimited

Problem: ThreatTypes can contain multiple values pipe-delimited (e.g., "Phish|Spam"). Using == will miss multi-category threats.

Solution: Always use has operator:

kql
| where ThreatTypes has "Phish"   // ✅ Correct
| where ThreatTypes == "Phish"    // ❌ Misses "Phish|Spam"
4. Timestamp vs TimeGenerated

Problem: Advanced Hunting uses Timestamp for XDR-native tables. Sentinel Data Lake uses TimeGenerated.

Solution: Default queries use Timestamp (AH). If falling back to Data Lake, replace Timestamp with TimeGenerated throughout.

5. IsFirstContact May Be Null

Problem: IsFirstContact can be null for outbound or intra-org emails. Filtering on it without scoping to inbound emails may miss records.

Solution: Always filter EmailDirection == "Inbound" before using IsFirstContact.

6. LatestDeliveryAction vs DeliveryAction

Problem: DeliveryAction is the initial delivery disposition. LatestDeliveryAction reflects the current state after ZAP or manual remediation. Reporting only DeliveryAction overstates the number of threats in mailboxes.

Solution: When assessing current threat exposure, use LatestDeliveryAction and LatestDeliveryLocation. When assessing initial filter effectiveness, use DeliveryAction.

7. DKIM Pass Rate May Be Lower Than Expected

Problem: DKIM pass rate can appear low because many legitimate emails (especially bulk/marketing) don't sign with DKIM at all. An email with no DKIM signature isn't a DKIM "fail" — it simply has no result. The DKIM field from AuthenticationDetails may be empty or "none" rather than "fail".

Solution: When computing DKIM pass rate, note the denominator: emails with a DKIM result vs total emails. A lower DKIM rate is expected and doesn't necessarily indicate spoofing. Compare against DMARC and CompAuth for a better authentication picture.

8. ZAP ErrorCount May Include Non-Threat Emails

Problem: ZAP errors can occur for legitimate reasons: shared mailboxes, retention policies preventing purge, user-moved emails. A ZAP error doesn't always mean a threat is still active.

Solution: When reporting ZAP failures, note that manual investigation may confirm the threat was already handled. Don't over-alarm on ZAP errors without context.

9. ZAP TotalActions ≠ Threat ZAP Count

Problem: EmailPostDeliveryEvents includes all post-delivery events — not just ZAP threat remediations. The TotalActions count from Q6 includes system-initiated events (message trace updates, delivery location changes, admin investigation submissions). Reporting TotalActions as "ZAP Remediations" in Key Metrics massively overstates the threat remediation picture (e.g., 7,790 total when only 674 are actual threat ZAPs).

Solution: Always use ThreatZAPTotal (PhishZAP + MalwareZAP + SpamZAP) for headline ZAP metrics. Show TotalActions separately with a clarifying note: "includes system events". In Key Metrics, use "Threat ZAP Actions: 674" not "ZAP Remediations: 7,790".

10. Threat Block Rate — Post-ZAP vs Pre-Delivery

Problem: The scoring dimension "Threat Block Rate" can be interpreted two ways: (a) pre-delivery block rate (threats blocked before reaching inbox), or (b) final disposition rate (threats not in inbox after ZAP). These give different numbers — e.g., 72.5% pre-delivery vs 81.2% post-ZAP.

Solution: The dimension measures final threat disposition (post-ZAP) — the percentage of detected threats that are NOT currently in user inboxes. This is the operationally relevant metric because it reflects actual user exposure. The dimension description explicitly says "not in inbox (post-ZAP final state)".


Quality Checklist

Before delivering the report, verify:

  • All percentage values show both percentage AND raw count
  • All queries used Timestamp (AH) or TimeGenerated (Data Lake) consistently
  • Zero-result queries are reported with explicit absence confirmation (✅ pattern)
  • The Email Protection Score calculation is transparent with per-dimension evidence
  • Detection methods show the full breakdown, not just "threats detected"
  • ZAP effectiveness distinguishes threat ZAP count vs TotalActions (no inflated headline metric)
  • Key Metrics ZAP row shows ThreatZAPTotal (Phish+Malware+Spam), NOT TotalActions
  • Safe Links section distinguishes blocked vs allowed vs click-through
  • Email authentication covers all four protocols with Other/None column
  • Threats-in-mailbox summary breaks down phishing vs spam (not just total)
  • Markdown report follows section ordering guidance (data → deep dives → score → assessment)
  • Post-ZAP state (Q12) shows where threats currently reside, not just initial delivery
  • MDO incidents section shows severity × status breakdown with open/closed/TP counts
  • Incident queries use canonical SecurityAlert→SecurityIncident join (NOT SecurityAlert.Status)
  • Recommendations are prioritized and evidence-based
  • All hyperlinks copied verbatim from the URL Registry — no fabricated URLs
  • No recipient PII (email addresses) in the report — aggregate by domain only
  • Daily volume trend includes at least a note on peak/anomaly days

SVG Dashboard Generation

📊 Optional post-report step. After an Email Threat Protection report is generated, the user can request a visual SVG dashboard.

Trigger phrases: "generate SVG dashboard", "create a visual dashboard", "visualize this report", "SVG from the report"

How to Request a Dashboard
  • Same chat: "Generate an SVG dashboard from the report" — data is already in context.
  • New chat: Attach or reference the report file, e.g. #file:reports/email-threat-posture/Email_Threat_Protection_Report_<date>.md
  • Customization: Edit svg-widgets.yaml before requesting — the renderer reads it at generation time.
Execution
Step 1:  Read svg-widgets.yaml (this skill's widget manifest)
Step 2:  Read .github/skills/svg-dashboard/SKILL.md (rendering rules — Manifest Mode)
Step 3:  Read the completed report file (data source)
Step 4:  Render SVG → save to reports/email-threat-posture/{report_name}_dashboard.svg

The YAML manifest is the single source of truth for layout, widgets, field mappings, colors, and data source documentation. All customization happens there.

© SCStelz, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 1 other file in .github/skills/email-threat-posture of SCStelz/security-investigator.

  • SKILL.md
  • svg-widgets.yaml

Open the folder on GitHubat commit 51e1385

Compare with similar skills

Email Threat Posture next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Email Threat Posture compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Email Threat Posture this skillSCStelz/security-investigator249—~9.7kAutomated safety check: PassMIT
M365 Agent Evaluatormicrosoft/work-iq1k—~2kAutomated safety check: NotesCustom licence
Microsoft 365 Agents ToolkitOfficeDev/microsoft-365-agents-toolkit780—~2.8kAutomated safety check: NotesCustom licence
Msgraphcodemie-ai/codemie-code294—~4.1kAutomated safety check: PassApache-2.0
aai-cli Microsoft 365aai-labs/agent-barn109—~1.2kAutomated safety check: PassApache-2.0
Workiqmicrosoft/work-iq1k—~15kAutomated safety check: PassCustom licence

Similar skills

  • M365 Agent Evaluator

    microsoft/work-iq

    Official

    A skill your agent uses when a user wants to create, run, or analyze evaluation suites for Microsoft 365 Copilot declarative agents with the public @microsoft/m365-copilot-eval CLI.

    1k GitHub stars~2k tokensUpdated yesterday
    Documents & OfficeAuto-check: notes
  • Microsoft 365 Agents Toolkit

    OfficeDev/microsoft-365-agents-toolkit

    Builds, tests, and deploys Microsoft 365 apps and agents for Teams and Copilot.

    780 GitHub stars~2.8k tokensUpdated yesterday
    Documents & OfficeAuto-check: notes
  • Msgraph

    codemie-ai/codemie-code

    Work with Microsoft 365 services via the Graph API — emails, calendar events, SharePoint sites (read and write), Teams chats and channel messages, OneDrive files, OneNote notebooks, Planner task…

    294 GitHub stars~4.1k tokensUpdated 2 days ago
    Documents & OfficeAuto-check passed
  • aai-cli Microsoft 365

    aai-labs/agent-barn

    Guides work with Outlook, OneDrive, SharePoint, Teams, Excel, To Do and Planner through aai-cli's Microsoft Graph commands, starting from which service owns the data.

    109 GitHub stars~1.2k tokensUpdated yesterday
    Documents & OfficeAuto-check passed
  • Workiq

    microsoft/work-iq

    Official

    WorkIQ tools for Microsoft 365 workplace data and actions. An agent skill from microsoft/work-iq.

    1k GitHub stars~15k tokensUpdated yesterday
    Documents & OfficeAuto-check passed
  • Workiq Preview

    microsoft/work-iq

    Official

    WorkIQ tools for Microsoft 365 workplace data and actions. An agent skill from microsoft/work-iq.

    1k GitHub stars~3.3k tokensUpdated yesterday
    Documents & OfficeAuto-check passed

More from SCStelz/security-investigator

All 22 skills in this repo
  • Ca Policy Investigation

    SCStelz/security-investigator

    A skill your agent uses when asked to investigate Conditional Access policy changes, sign-in failures related to CA policies (error codes 53000, 50074, 530032), or suspected policy…

    249 GitHub stars~3.8k tokensUpdated 2 days ago
    Auto-check passed
  • Context Memory Review

    SCStelz/security-investigator

    Weekly review of an investigation tenant-context memory file against the most recent SOC scan reports (e.g.

    249 GitHub stars~3.7k tokensUpdated 2 days ago
    Auto-check passed
  • Heatmap Visualization

    SCStelz/security-investigator

    A skill your agent uses when asked to create heatmaps, visualize patterns over time, show activity grids, or display aggregated data in a matrix format.

    249 GitHub stars~3.4k tokensUpdated 2 days ago
    Auto-check passed
  • AI Agent Activity

    SCStelz/security-investigator

    Report/investigate RUNTIME ACTIVITY of AI agents (Agent 365 / Copilot Studio / M365 Copilot / Work IQ) — agents used, tools/connectors, channels, tokens, prompt/reply content, and Prompt Shield…

    249 GitHub stars~17k tokensUpdated 2 days ago
    Auto-check passed
  • AI Agent Posture

    SCStelz/security-investigator

    Audit or report on AI agent security posture across Copilot Studio, Microsoft 365 Copilot, Microsoft Foundry, and third-party agents.

    249 GitHub stars~21k tokensUpdated 2 days ago
    Auto-check passed
  • App Registration Posture

    SCStelz/security-investigator

    Audit Entra ID app registration and service principal security posture.

    249 GitHub stars~21k tokensUpdated 2 days ago
    Auto-check passed

Works with

Questions about Email Threat Posture

What does Email Threat Posture do?

Generate email threat protection reports and assess email security posture. Email Threat Posture is an agent skill from SCStelz/security-investigator. Generate email threat protection reports and assess email security posture.

When should I use Email Threat Posture?

Email Threat Posture fits situations like: keywords like email threat report; email security posture; phishing report; defender for Office 365 report.

How do I install Email Threat Posture in Claude Code?

Run `npx skills add SCStelz/security-investigator --skill email-threat-posture -a claude-code`. Or copy the skill folder (.github/skills/email-threat-posture in SCStelz/security-investigator) into .claude/skills/email-threat-posture in your project. Claude Code loads it when a task matches its description.

How do I install Email Threat Posture in Codex?

Run `npx skills add SCStelz/security-investigator --skill email-threat-posture -a codex`. Or copy the skill folder (.github/skills/email-threat-posture in SCStelz/security-investigator) into .agents/skills/email-threat-posture in your project. Codex loads it when a task matches its description.

Can I use Email Threat Posture in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add SCStelz/security-investigator --skill email-threat-posture -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/email-threat-posture, .gemini/skills/email-threat-posture, .github/skills/email-threat-posture and .opencode/skills/email-threat-posture in your project.

What does Email Threat Posture need to run?

SKILL.md names no scripts, command-line tools or credentials: Email Threat Posture is instructions for the agent only.

Does Email Threat Posture access the network?

SKILL.md names 1 domain. In commands or code: learn.microsoft.com; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.

Is Email Threat Posture safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Email Threat Posture use?

Email Threat Posture is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Email Threat Posture use?

About 9.7k tokens (SKILL.md is roughly 39k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Email Threat Posture?

Skills that share tags, products or a category with Email Threat Posture: M365 Agent Evaluator (microsoft/work-iq, 1k stars), Microsoft 365 Agents Toolkit (OfficeDev/microsoft-365-agents-toolkit, 780 stars), Msgraph (codemie-ai/codemie-code, 294 stars) and aai-cli Microsoft 365 (aai-labs/agent-barn, 109 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Email Threat Posture?

SCStelz (a GitHub user) maintains it in SCStelz/security-investigator, which has 249 GitHub stars. The repository holds 22 skills in this directory. The repository was last updated on October 8, 2026.

Source: SCStelz/security-investigator on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.