reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
32
vendor-new/Anthropic-Cybersecurity-Skills/tools/README.md
Normal file
32
vendor-new/Anthropic-Cybersecurity-Skills/tools/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Skill Validation Tools
|
||||
|
||||
## validate-skill.py
|
||||
|
||||
Validate SKILL.md metadata before submitting a PR.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
# Validate a single skill
|
||||
python tools/validate-skill.py skills/my-new-skill/
|
||||
|
||||
# Validate all skills
|
||||
python tools/validate-skill.py --all
|
||||
```
|
||||
|
||||
### What it checks
|
||||
|
||||
- SKILL.md exists in the skill directory
|
||||
- Valid YAML frontmatter (between `---` markers)
|
||||
- Required fields present: `name`, `description`, `domain`, `subdomain`, `tags`, `version`, `author`, `license`
|
||||
- Name is kebab-case, 1–64 characters
|
||||
- Description is at least 50 characters (agentskills.io caps it at 1024; `tools/validate-agentskills.py` enforces that)
|
||||
- Domain is `cybersecurity`
|
||||
- Subdomain is from the allowed list
|
||||
- Tags is a list with at least 2 items
|
||||
|
||||
### Requirements
|
||||
|
||||
Python 3.8+ and PyYAML (`pip install pyyaml`).
|
||||
|
||||
All frontmatter is parsed by `skill_frontmatter.py`, the single PyYAML-backed loader. It replaced three hand-rolled regex parsers that silently truncated multi-line descriptions to their first line — that bug shipped 604 of 817 descriptions broken in `index.json`. Do not reintroduce regex frontmatter parsing; CI fails the build if it detects any.
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://agentskills.io/schema/skill-frontmatter.json",
|
||||
"title": "agentskills.io SKILL.md frontmatter",
|
||||
"description": "JSON Schema for the YAML frontmatter of a SKILL.md file per the agentskills.io open standard (Anthropic, 2025-12-18). Only `name` and `description` are required; `license`, `compatibility`, `metadata`, and `allowed-tools` are recognized optional fields. Additional top-level keys are PERMITTED as extra metadata (this repo uses domain/subdomain/tags/version/author and framework-mapping keys), so `additionalProperties` is true. Constraints JSON Schema cannot express (name must equal the parent directory; no angle-bracket content in frontmatter) are enforced by tools/validate-agentskills.py.",
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"required": ["name", "description"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 64,
|
||||
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
|
||||
"description": "1-64 chars, lowercase alphanumeric and single hyphens only; no leading/trailing/consecutive hyphens; must match the parent directory name; must not be a reserved word."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 1024,
|
||||
"description": "1-1024 chars. Must state BOTH what the skill does and when to use it."
|
||||
},
|
||||
"license": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "SPDX license identifier or license text."
|
||||
},
|
||||
"compatibility": {
|
||||
"type": "string",
|
||||
"maxLength": 500,
|
||||
"description": "Free-text compatibility notes (<=500 chars)."
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"description": "Arbitrary key/value map for non-standard fields. This is where domain, subdomain, tags, version, author, and framework mappings (mitre_attack, nist_csf, atlas_techniques, d3fend_techniques, nist_ai_rmf, mitre_f3) belong under strict compliance.",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"allowed-tools": {
|
||||
"description": "EXPERIMENTAL. Tools the skill is permitted to use; string or array of strings.",
|
||||
"oneOf": [
|
||||
{ "type": "string" },
|
||||
{ "type": "array", "items": { "type": "string" } }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"_comment": [
|
||||
"Skill pairs that score as near-duplicates but are legitimately distinct.",
|
||||
"",
|
||||
"Add a pair here ONLY after reading both descriptions and confirming a real",
|
||||
"difference in scope, platform, or offense-vs-defense posture. The 'reason'",
|
||||
"is not bookkeeping: it is the wording each skill's negative trigger should",
|
||||
"use, e.g. reason 'Linux vs Windows' becomes 'Do not use for Windows hosts",
|
||||
"- use hardening-windows-endpoint-with-cis-benchmark.'",
|
||||
"",
|
||||
"A pair that is genuinely ambiguous does NOT belong here. Disambiguate the",
|
||||
"two descriptions instead.",
|
||||
"",
|
||||
"Regenerate the candidate list with: python tools/detect-collisions.py"
|
||||
],
|
||||
"reviewed_distinct": [
|
||||
{
|
||||
"pair": [
|
||||
"hardening-linux-endpoint-with-cis-benchmark",
|
||||
"hardening-windows-endpoint-with-cis-benchmark"
|
||||
],
|
||||
"reason": "Same CIS Benchmark methodology, different operating systems. The controls, tooling and audit commands do not overlap, so both must exist; each description must name the OS explicitly and point at the other."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Find skill pairs whose descriptions compete for the same user request.
|
||||
|
||||
An agent picks a skill from its description alone. When two descriptions are
|
||||
near-duplicates, the router cannot reliably choose between them and misroutes
|
||||
-- "skill collision". This scores every pair by TF-IDF cosine similarity over
|
||||
description + slug and reports the ones close enough to collide.
|
||||
|
||||
A high score is NOT automatically a defect. Some near-twins are legitimately
|
||||
distinct (Linux vs Windows CIS hardening; red-team DCSync vs blue-team DCSync
|
||||
detection). Those belong in tools/collision-allowlist.json with a reason, which
|
||||
is also a record of WHY they differ -- exactly the wording their negative
|
||||
triggers need.
|
||||
|
||||
Pure stdlib; no numpy/sklearn required.
|
||||
|
||||
Usage:
|
||||
python tools/detect-collisions.py
|
||||
python tools/detect-collisions.py --threshold 0.5
|
||||
python tools/detect-collisions.py --json
|
||||
python tools/detect-collisions.py --max-unreviewed 60 # CI gate
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import itertools
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from skill_frontmatter import description_of, iter_skill_dirs, load_frontmatter, FrontmatterError
|
||||
|
||||
ALLOWLIST_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
"collision-allowlist.json")
|
||||
DEFAULT_THRESHOLD = 0.45
|
||||
|
||||
# Words too common in this corpus to carry signal.
|
||||
STOPWORDS = set("""
|
||||
the a an and or of to for in on with using use uses used when this that is are be by
|
||||
from as it its into via at not skill security detect detects detecting analyze analyzes
|
||||
analyzing perform performs performing implement implements implementing
|
||||
""".split())
|
||||
|
||||
# A term appearing in more than this many skills is treated as a domain-wide
|
||||
# background word and skipped when pairing (keeps the comparison O(usable pairs)).
|
||||
MAX_DOCS_PER_TERM = 60
|
||||
|
||||
|
||||
# A negative trigger names the sibling skill on purpose ("Do not use for X -
|
||||
# use other-skill."). Scoring that text would inject the sibling's own slug
|
||||
# tokens into this skill's vector, so correctly disambiguating a pair would
|
||||
# RAISE its similarity score -- the metric would punish the fix. Strip the
|
||||
# disambiguation scaffolding and score only the descriptive part.
|
||||
DISAMBIGUATION_RE = re.compile(r"\b(?:do\s+not\s+use|don'?t\s+use|avoid\s+(?:this\s+)?(?:skill\s+)?for)\b.*",
|
||||
re.IGNORECASE | re.DOTALL)
|
||||
KEYWORDS_LABEL_RE = re.compile(r"\bkeywords\s*:", re.IGNORECASE)
|
||||
|
||||
|
||||
def strip_disambiguation(text: str) -> str:
|
||||
"""Drop negative-trigger sentences and the literal 'Keywords:' label.
|
||||
|
||||
The keyword TERMS stay -- they are real content. Only the label is removed,
|
||||
since it would otherwise be a shared token across every fixed skill.
|
||||
"""
|
||||
text = DISAMBIGUATION_RE.sub("", text)
|
||||
return KEYWORDS_LABEL_RE.sub(" ", text)
|
||||
|
||||
|
||||
def tokenize(text: str) -> Counter:
|
||||
return Counter(w for w in re.findall(r"[a-z0-9]+", text.lower())
|
||||
if w not in STOPWORDS and len(w) > 2)
|
||||
|
||||
|
||||
def build_vectors(skills_dir: str) -> dict[str, dict[str, float]]:
|
||||
"""L2-normalized TF-IDF vectors keyed by slug."""
|
||||
docs: dict[str, Counter] = {}
|
||||
for slug, skill_dir in iter_skill_dirs(skills_dir):
|
||||
try:
|
||||
frontmatter = load_frontmatter(os.path.join(skill_dir, "SKILL.md"))
|
||||
except FrontmatterError:
|
||||
continue
|
||||
described = strip_disambiguation(description_of(frontmatter))
|
||||
docs[slug] = tokenize(f"{described} {slug.replace('-', ' ')}")
|
||||
|
||||
doc_freq: Counter = Counter()
|
||||
for counts in docs.values():
|
||||
doc_freq.update(counts.keys())
|
||||
|
||||
total = len(docs)
|
||||
vectors: dict[str, dict[str, float]] = {}
|
||||
for slug, counts in docs.items():
|
||||
weights = {term: (1 + math.log(freq)) * math.log(total / doc_freq[term])
|
||||
for term, freq in counts.items()}
|
||||
norm = math.sqrt(sum(w * w for w in weights.values())) or 1.0
|
||||
vectors[slug] = {term: w / norm for term, w in weights.items()}
|
||||
return vectors
|
||||
|
||||
|
||||
def score_pairs(vectors: dict[str, dict[str, float]], threshold: float):
|
||||
"""Cosine similarity for every pair sharing at least one discriminating term."""
|
||||
postings: dict[str, list[str]] = {}
|
||||
for slug, vector in vectors.items():
|
||||
for term in vector:
|
||||
postings.setdefault(term, []).append(slug)
|
||||
|
||||
sims: dict[tuple[str, str], float] = {}
|
||||
for term, slugs in postings.items():
|
||||
if len(slugs) > MAX_DOCS_PER_TERM:
|
||||
continue
|
||||
weight_by_slug = {s: vectors[s][term] for s in slugs}
|
||||
for a, b in itertools.combinations(sorted(slugs), 2):
|
||||
sims[(a, b)] = sims.get((a, b), 0.0) + weight_by_slug[a] * weight_by_slug[b]
|
||||
|
||||
return sorted(((s, p) for p, s in sims.items() if s >= threshold), reverse=True)
|
||||
|
||||
|
||||
def load_allowlist() -> dict[tuple[str, str], str]:
|
||||
if not os.path.isfile(ALLOWLIST_PATH):
|
||||
return {}
|
||||
with open(ALLOWLIST_PATH, encoding="utf-8") as handle:
|
||||
data = json.load(handle)
|
||||
out: dict[tuple[str, str], str] = {}
|
||||
for entry in data.get("reviewed_distinct", []):
|
||||
pair = tuple(sorted(entry["pair"]))
|
||||
out[pair] = entry.get("reason", "")
|
||||
return out
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--threshold", type=float, default=DEFAULT_THRESHOLD)
|
||||
parser.add_argument("--json", action="store_true", dest="as_json")
|
||||
parser.add_argument("--max-unreviewed", type=int, default=None,
|
||||
help="exit 1 if unreviewed colliding pairs exceed this")
|
||||
parser.add_argument("--skills-dir", default="skills")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.isdir(args.skills_dir):
|
||||
print(f"ERROR: '{args.skills_dir}' not found. Run from the repository root.")
|
||||
return 1
|
||||
|
||||
vectors = build_vectors(args.skills_dir)
|
||||
pairs = score_pairs(vectors, args.threshold)
|
||||
allowlist = load_allowlist()
|
||||
|
||||
unreviewed = [(s, p) for s, p in pairs if p not in allowlist]
|
||||
reviewed = [(s, p) for s, p in pairs if p in allowlist]
|
||||
involved = {slug for _, pair in unreviewed for slug in pair}
|
||||
|
||||
if args.as_json:
|
||||
print(json.dumps({
|
||||
"threshold": args.threshold,
|
||||
"total_pairs": len(pairs),
|
||||
"unreviewed": [{"score": round(s, 3), "pair": list(p)} for s, p in unreviewed],
|
||||
"reviewed_distinct": [{"score": round(s, 3), "pair": list(p),
|
||||
"reason": allowlist[p]} for s, p in reviewed],
|
||||
"skills_involved": sorted(involved),
|
||||
}, indent=2))
|
||||
else:
|
||||
print(f"Colliding pairs at cosine >= {args.threshold}: {len(pairs)} "
|
||||
f"({len(reviewed)} reviewed-distinct, {len(unreviewed)} unreviewed)")
|
||||
print(f"Skills involved in an unreviewed collision: {len(involved)} "
|
||||
f"of {len(vectors)}\n")
|
||||
for score, (a, b) in unreviewed:
|
||||
print(f" {score:.2f} {a}\n {b}")
|
||||
if reviewed:
|
||||
print(f"\nReviewed as legitimately distinct ({len(reviewed)}):")
|
||||
for score, (a, b) in reviewed:
|
||||
print(f" {score:.2f} {a} || {b}\n reason: {allowlist[(a, b)]}")
|
||||
|
||||
if args.max_unreviewed is not None and len(unreviewed) > args.max_unreviewed:
|
||||
print(f"\nERROR: {len(unreviewed)} unreviewed colliding pairs "
|
||||
f"exceeds --max-unreviewed {args.max_unreviewed}.")
|
||||
print("Either disambiguate the descriptions or record the pair in "
|
||||
f"{os.path.relpath(ALLOWLIST_PATH)} with a reason.")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate index.json from the SKILL.md files under skills/.
|
||||
|
||||
Previously this logic lived as an inline heredoc inside
|
||||
.github/workflows/update-index.yml with a hand-rolled regex YAML parser that
|
||||
truncated 604 of 817 descriptions. It lives here now so it is testable outside
|
||||
CI and shares one PyYAML-backed parser with every other tool.
|
||||
|
||||
Usage:
|
||||
python tools/generate-index.py # write index.json
|
||||
python tools/generate-index.py --check # verify index.json is current (CI)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from skill_frontmatter import description_of, iter_skill_dirs, load_frontmatter, FrontmatterError
|
||||
|
||||
INDEX_VERSION = "1.1.0"
|
||||
REPOSITORY = "https://github.com/mukul975/Anthropic-Cybersecurity-Skills"
|
||||
DEFAULT_DOMAIN = "cybersecurity"
|
||||
|
||||
|
||||
def build_index(skills_dir: str = "skills") -> tuple[dict, list[str]]:
|
||||
"""Build the index payload. Returns (index, errors)."""
|
||||
skills = []
|
||||
errors = []
|
||||
|
||||
for slug, skill_dir in iter_skill_dirs(skills_dir):
|
||||
try:
|
||||
frontmatter = load_frontmatter(os.path.join(skill_dir, "SKILL.md"))
|
||||
except FrontmatterError as exc:
|
||||
errors.append(f"{slug}: {exc}")
|
||||
continue
|
||||
|
||||
description = description_of(frontmatter)
|
||||
if not description:
|
||||
errors.append(f"{slug}: empty description")
|
||||
|
||||
skills.append({
|
||||
"name": slug,
|
||||
"description": description,
|
||||
"domain": frontmatter.get("domain") or DEFAULT_DOMAIN,
|
||||
"path": f"{skills_dir}/{slug}",
|
||||
})
|
||||
|
||||
index = {
|
||||
"version": INDEX_VERSION,
|
||||
"generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"repository": REPOSITORY,
|
||||
"domain": DEFAULT_DOMAIN,
|
||||
"total_skills": len(skills),
|
||||
"skills": skills,
|
||||
}
|
||||
return index, errors
|
||||
|
||||
|
||||
def _comparable(index: dict) -> str:
|
||||
"""Serialize an index ignoring generated_at, so --check tolerates a re-run."""
|
||||
return json.dumps({k: v for k, v in index.items() if k != "generated_at"}, sort_keys=True)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--check", action="store_true",
|
||||
help="verify index.json matches the skills tree; do not write")
|
||||
parser.add_argument("--skills-dir", default="skills")
|
||||
parser.add_argument("--out", default="index.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.isdir(args.skills_dir):
|
||||
print(f"ERROR: '{args.skills_dir}' not found. Run from the repository root.")
|
||||
return 1
|
||||
|
||||
index, errors = build_index(args.skills_dir)
|
||||
|
||||
for error in errors:
|
||||
print(f"ERROR {error}")
|
||||
if errors:
|
||||
print(f"\n{len(errors)} skill(s) could not be indexed.")
|
||||
return 1
|
||||
|
||||
if args.check:
|
||||
if not os.path.isfile(args.out):
|
||||
print(f"ERROR: {args.out} is missing. Run: python tools/generate-index.py")
|
||||
return 1
|
||||
with open(args.out, encoding="utf-8") as handle:
|
||||
current = json.load(handle)
|
||||
if _comparable(current) != _comparable(index):
|
||||
print(f"ERROR: {args.out} is out of date. Run: python tools/generate-index.py")
|
||||
return 1
|
||||
print(f"OK: {args.out} is up to date ({index['total_skills']} skills)")
|
||||
return 0
|
||||
|
||||
with open(args.out, "w", encoding="utf-8") as handle:
|
||||
json.dump(index, handle, separators=(",", ":"))
|
||||
|
||||
print(f"Updated {args.out}: {index['total_skills']} skills")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,992 @@
|
||||
{
|
||||
"_comment": "Pre-existing lint failures, grandfathered so CI can gate new work today. This file may only shrink. Never add a slug by hand -- fix the skill, then run: python tools/lint-descriptions.py --update-baseline",
|
||||
"_total_grandfathered": 980,
|
||||
"desc-ends-punctuation": [
|
||||
"analyzing-active-directory-acl-abuse",
|
||||
"analyzing-malicious-url-with-urlscan",
|
||||
"detecting-aws-iam-privilege-escalation",
|
||||
"exploiting-active-directory-with-bloodhound",
|
||||
"implementing-email-sandboxing-with-proofpoint",
|
||||
"implementing-patch-management-workflow",
|
||||
"implementing-secrets-scanning-in-ci-cd",
|
||||
"performing-cryptographic-audit-of-application",
|
||||
"performing-dark-web-monitoring-for-threats",
|
||||
"performing-malware-ioc-extraction",
|
||||
"performing-open-source-intelligence-gathering",
|
||||
"performing-service-account-audit",
|
||||
"prioritizing-vulnerabilities-with-cvss-scoring"
|
||||
],
|
||||
"desc-has-use-when": [
|
||||
"analyzing-active-directory-acl-abuse",
|
||||
"analyzing-android-malware-with-apktool",
|
||||
"analyzing-apt-group-with-mitre-navigator",
|
||||
"analyzing-certificate-transparency-for-phishing",
|
||||
"analyzing-cobalt-strike-beacon-configuration",
|
||||
"analyzing-docker-container-forensics",
|
||||
"analyzing-ethereum-smart-contract-vulnerabilities",
|
||||
"analyzing-heap-spray-exploitation",
|
||||
"analyzing-ios-app-security-with-objection",
|
||||
"analyzing-linux-audit-logs-for-intrusion",
|
||||
"analyzing-linux-kernel-rootkits",
|
||||
"analyzing-macro-malware-in-office-documents",
|
||||
"analyzing-malicious-url-with-urlscan",
|
||||
"analyzing-memory-dumps-with-volatility",
|
||||
"analyzing-network-flow-data-with-netflow",
|
||||
"analyzing-network-traffic-for-incidents",
|
||||
"analyzing-network-traffic-of-malware",
|
||||
"analyzing-network-traffic-with-wireshark",
|
||||
"analyzing-office365-audit-logs-for-compromise",
|
||||
"analyzing-pdf-malware-with-pdfid",
|
||||
"analyzing-powershell-script-block-logging",
|
||||
"analyzing-ransomware-encryption-mechanisms",
|
||||
"analyzing-sbom-for-supply-chain-vulnerabilities",
|
||||
"analyzing-security-logs-with-splunk",
|
||||
"analyzing-supply-chain-malware-artifacts",
|
||||
"analyzing-web-server-logs-for-intrusion",
|
||||
"auditing-aws-s3-bucket-permissions",
|
||||
"auditing-azure-active-directory-configuration",
|
||||
"auditing-gcp-iam-permissions",
|
||||
"auditing-kubernetes-cluster-rbac",
|
||||
"auditing-terraform-infrastructure-for-security",
|
||||
"auditing-tls-certificate-transparency-logs",
|
||||
"auditing-uefi-firmware-with-chipsec",
|
||||
"building-detection-rule-with-splunk-spl",
|
||||
"bypassing-authentication-with-forced-browsing",
|
||||
"coercing-authentication-with-coercer-petitpotam",
|
||||
"collecting-indicators-of-compromise",
|
||||
"conducting-api-security-testing",
|
||||
"conducting-cloud-penetration-testing",
|
||||
"conducting-domain-persistence-with-dcsync",
|
||||
"conducting-internal-network-penetration-test",
|
||||
"conducting-man-in-the-middle-attack-simulation",
|
||||
"conducting-memory-forensics-with-volatility",
|
||||
"conducting-mobile-app-penetration-test",
|
||||
"conducting-network-penetration-test",
|
||||
"conducting-post-incident-lessons-learned",
|
||||
"conducting-social-engineering-pretext-call",
|
||||
"conducting-spearphishing-simulation-campaign",
|
||||
"conducting-wireless-network-penetration-test",
|
||||
"containing-active-breach",
|
||||
"deploying-decoy-files-for-ransomware-detection",
|
||||
"detecting-anomalous-authentication-patterns",
|
||||
"detecting-attacks-on-scada-systems",
|
||||
"detecting-aws-cloudtrail-anomalies",
|
||||
"detecting-aws-iam-privilege-escalation",
|
||||
"detecting-azure-lateral-movement",
|
||||
"detecting-business-email-compromise-with-ai",
|
||||
"detecting-cryptomining-in-cloud",
|
||||
"detecting-lateral-movement-in-network",
|
||||
"detecting-lateral-movement-with-zeek",
|
||||
"detecting-living-off-the-land-attacks",
|
||||
"detecting-malicious-scheduled-tasks-with-sysmon",
|
||||
"detecting-network-scanning-with-ids-signatures",
|
||||
"detecting-port-scanning-with-fail2ban",
|
||||
"detecting-process-injection-techniques",
|
||||
"detecting-ransomware-precursors-in-network",
|
||||
"detecting-s3-data-exfiltration-attempts",
|
||||
"detecting-serverless-function-injection",
|
||||
"detecting-sql-injection-via-waf-logs",
|
||||
"detecting-suspicious-oauth-application-consent",
|
||||
"detecting-typosquatting-packages-in-npm-pypi",
|
||||
"emulating-cloud-attacks-with-stratus-red-team",
|
||||
"enumerating-cloud-with-cloudfox",
|
||||
"executing-active-directory-attack-simulation",
|
||||
"executing-phishing-simulation-campaign",
|
||||
"exploiting-active-directory-certificate-services-esc1",
|
||||
"exploiting-active-directory-with-bloodhound",
|
||||
"exploiting-bgp-hijacking-vulnerabilities",
|
||||
"exploiting-insecure-deserialization",
|
||||
"exploiting-ipv6-vulnerabilities",
|
||||
"exploiting-oauth-misconfiguration",
|
||||
"exploiting-server-side-request-forgery",
|
||||
"exploiting-smb-vulnerabilities-with-metasploit",
|
||||
"exploiting-sql-injection-vulnerabilities",
|
||||
"exploiting-sql-injection-with-sqlmap",
|
||||
"exploiting-websocket-vulnerabilities",
|
||||
"extracting-windows-event-logs-artifacts",
|
||||
"hunting-for-anomalous-powershell-execution",
|
||||
"hunting-for-beaconing-with-frequency-analysis",
|
||||
"hunting-for-command-and-control-beaconing",
|
||||
"hunting-for-defense-evasion-via-timestomping",
|
||||
"hunting-for-registry-run-key-persistence",
|
||||
"hunting-for-spearphishing-indicators",
|
||||
"implementing-aqua-security-for-container-scanning",
|
||||
"implementing-cloud-trail-log-analysis",
|
||||
"implementing-ddos-mitigation-with-cloudflare",
|
||||
"implementing-diamond-model-analysis",
|
||||
"implementing-email-sandboxing-with-proofpoint",
|
||||
"implementing-honeypot-for-ransomware-detection",
|
||||
"implementing-mitre-attack-coverage-mapping",
|
||||
"implementing-network-deception-with-honeypots",
|
||||
"implementing-patch-management-workflow",
|
||||
"implementing-proofpoint-email-security-gateway",
|
||||
"implementing-runtime-application-self-protection",
|
||||
"implementing-secret-scanning-with-gitleaks",
|
||||
"implementing-secrets-scanning-in-ci-cd",
|
||||
"implementing-security-monitoring-with-datadog",
|
||||
"implementing-semgrep-for-custom-sast-rules",
|
||||
"implementing-velociraptor-for-ir-collection",
|
||||
"investigating-ransomware-attack-artifacts",
|
||||
"monitoring-scada-modbus-traffic-anomalies",
|
||||
"performing-active-directory-compromise-investigation",
|
||||
"performing-active-directory-vulnerability-assessment",
|
||||
"performing-agentless-vulnerability-scanning",
|
||||
"performing-api-inventory-and-discovery",
|
||||
"performing-aws-privilege-escalation-assessment",
|
||||
"performing-brand-monitoring-for-impersonation",
|
||||
"performing-clickjacking-attack-test",
|
||||
"performing-credential-access-with-lazagne",
|
||||
"performing-cryptographic-audit-of-application",
|
||||
"performing-csrf-attack-simulation",
|
||||
"performing-dark-web-monitoring-for-threats",
|
||||
"performing-dns-enumeration-and-zone-transfer",
|
||||
"performing-external-network-penetration-test",
|
||||
"performing-firmware-extraction-with-binwalk",
|
||||
"performing-gcp-security-assessment-with-forseti",
|
||||
"performing-graphql-depth-limit-attack",
|
||||
"performing-graphql-security-assessment",
|
||||
"performing-initial-access-with-evilginx3",
|
||||
"performing-iot-security-assessment",
|
||||
"performing-ip-reputation-analysis-with-shodan",
|
||||
"performing-lateral-movement-with-wmiexec",
|
||||
"performing-log-analysis-for-forensic-investigation",
|
||||
"performing-malware-ioc-extraction",
|
||||
"performing-malware-persistence-investigation",
|
||||
"performing-memory-forensics-with-volatility3-plugins",
|
||||
"performing-mobile-app-certificate-pinning-bypass",
|
||||
"performing-oauth-scope-minimization-review",
|
||||
"performing-open-source-intelligence-gathering",
|
||||
"performing-ot-network-security-assessment",
|
||||
"performing-ot-vulnerability-scanning-safely",
|
||||
"performing-packet-injection-attack",
|
||||
"performing-paste-site-monitoring-for-credentials",
|
||||
"performing-physical-intrusion-assessment",
|
||||
"performing-plc-firmware-security-analysis",
|
||||
"performing-privilege-escalation-assessment",
|
||||
"performing-s7comm-protocol-security-analysis",
|
||||
"performing-sca-dependency-scanning-with-snyk",
|
||||
"performing-scada-hmi-security-assessment",
|
||||
"performing-second-order-sql-injection",
|
||||
"performing-security-headers-audit",
|
||||
"performing-serverless-function-security-review",
|
||||
"performing-service-account-audit",
|
||||
"performing-subdomain-enumeration-with-subfinder",
|
||||
"performing-thick-client-application-penetration-test",
|
||||
"performing-threat-hunting-with-yara-rules",
|
||||
"performing-vulnerability-scanning-with-nessus",
|
||||
"performing-web-application-penetration-test",
|
||||
"performing-web-cache-poisoning-attack",
|
||||
"performing-wifi-password-cracking-with-aircrack",
|
||||
"performing-wireless-network-penetration-test",
|
||||
"performing-wireless-security-assessment-with-kismet",
|
||||
"prioritizing-vulnerabilities-with-cvss-scoring",
|
||||
"reverse-engineering-ransomware-encryption-routine",
|
||||
"scanning-infrastructure-with-nessus",
|
||||
"testing-api-authentication-weaknesses",
|
||||
"testing-cors-misconfiguration",
|
||||
"testing-for-host-header-injection",
|
||||
"testing-for-sensitive-data-exposure",
|
||||
"testing-for-xml-injection-vulnerabilities",
|
||||
"testing-for-xss-vulnerabilities-with-burpsuite",
|
||||
"testing-for-xxe-injection-vulnerabilities",
|
||||
"testing-jwt-token-security",
|
||||
"validating-tpm-measured-boot-attestation",
|
||||
"verifying-build-provenance-with-slsa-sigstore"
|
||||
],
|
||||
"desc-has-negative-trigger": [
|
||||
"abusing-dpapi-for-credential-access",
|
||||
"abusing-shadow-credentials-for-privesc",
|
||||
"achieving-cmmc-level-2-compliance",
|
||||
"acquiring-disk-image-with-dd-and-dcfldd",
|
||||
"analyzing-active-directory-acl-abuse",
|
||||
"analyzing-android-malware-with-apktool",
|
||||
"analyzing-api-gateway-access-logs",
|
||||
"analyzing-apt-group-with-mitre-navigator",
|
||||
"analyzing-azure-activity-logs-for-threats",
|
||||
"analyzing-bootkit-and-rootkit-samples",
|
||||
"analyzing-browser-forensics-with-hindsight",
|
||||
"analyzing-campaign-attribution-evidence",
|
||||
"analyzing-certificate-transparency-for-phishing",
|
||||
"analyzing-cloud-storage-access-patterns",
|
||||
"analyzing-cobalt-strike-beacon-configuration",
|
||||
"analyzing-cobaltstrike-malleable-c2-profiles",
|
||||
"analyzing-command-and-control-communication",
|
||||
"analyzing-cyber-kill-chain",
|
||||
"analyzing-disk-image-with-autopsy",
|
||||
"analyzing-dns-logs-for-exfiltration",
|
||||
"analyzing-docker-container-forensics",
|
||||
"analyzing-email-headers-for-phishing-investigation",
|
||||
"analyzing-ethereum-smart-contract-vulnerabilities",
|
||||
"analyzing-golang-malware-with-ghidra",
|
||||
"analyzing-heap-spray-exploitation",
|
||||
"analyzing-indicators-of-compromise",
|
||||
"analyzing-ios-app-security-with-objection",
|
||||
"analyzing-linux-audit-logs-for-intrusion",
|
||||
"analyzing-linux-elf-malware",
|
||||
"analyzing-linux-kernel-rootkits",
|
||||
"analyzing-linux-system-artifacts",
|
||||
"analyzing-lnk-file-and-jump-list-artifacts",
|
||||
"analyzing-macro-malware-in-office-documents",
|
||||
"analyzing-malicious-pdf-with-peepdf",
|
||||
"analyzing-malicious-url-with-urlscan",
|
||||
"analyzing-malware-behavior-with-cuckoo-sandbox",
|
||||
"analyzing-malware-family-relationships-with-malpedia",
|
||||
"analyzing-malware-persistence-with-autoruns",
|
||||
"analyzing-malware-sandbox-evasion-techniques",
|
||||
"analyzing-memory-dumps-with-volatility",
|
||||
"analyzing-memory-forensics-with-lime-and-volatility",
|
||||
"analyzing-mft-for-deleted-file-recovery",
|
||||
"analyzing-network-covert-channels-in-malware",
|
||||
"analyzing-network-flow-data-with-netflow",
|
||||
"analyzing-network-packets-with-scapy",
|
||||
"analyzing-network-traffic-for-incidents",
|
||||
"analyzing-network-traffic-of-malware",
|
||||
"analyzing-network-traffic-with-wireshark",
|
||||
"analyzing-office365-audit-logs-for-compromise",
|
||||
"analyzing-outlook-pst-for-email-forensics",
|
||||
"analyzing-packed-malware-with-upx-unpacker",
|
||||
"analyzing-pdf-malware-with-pdfid",
|
||||
"analyzing-persistence-mechanisms-in-linux",
|
||||
"analyzing-powershell-empire-artifacts",
|
||||
"analyzing-powershell-script-block-logging",
|
||||
"analyzing-prefetch-files-for-execution-history",
|
||||
"analyzing-ransomware-encryption-mechanisms",
|
||||
"analyzing-ransomware-leak-site-intelligence",
|
||||
"analyzing-ransomware-network-indicators",
|
||||
"analyzing-ransomware-payment-wallets",
|
||||
"analyzing-sbom-for-supply-chain-vulnerabilities",
|
||||
"analyzing-security-logs-with-splunk",
|
||||
"analyzing-slack-space-and-file-system-artifacts",
|
||||
"analyzing-supply-chain-malware-artifacts",
|
||||
"analyzing-threat-actor-ttps-with-mitre-attack",
|
||||
"analyzing-threat-actor-ttps-with-mitre-navigator",
|
||||
"analyzing-threat-intelligence-feeds",
|
||||
"analyzing-threat-landscape-with-misp",
|
||||
"analyzing-tls-certificate-transparency-logs",
|
||||
"analyzing-typosquatting-domains-with-dnstwist",
|
||||
"analyzing-uefi-bootkit-persistence",
|
||||
"analyzing-usb-device-connection-history",
|
||||
"analyzing-web-server-logs-for-intrusion",
|
||||
"analyzing-windows-amcache-artifacts",
|
||||
"analyzing-windows-event-logs-in-splunk",
|
||||
"analyzing-windows-lnk-files-for-artifacts",
|
||||
"analyzing-windows-prefetch-with-python",
|
||||
"analyzing-windows-registry-for-artifacts",
|
||||
"analyzing-windows-shellbag-artifacts",
|
||||
"assessing-vector-and-embedding-weaknesses",
|
||||
"attacking-entra-id-with-roadtools",
|
||||
"attacking-oauth-with-device-code-phishing",
|
||||
"auditing-aws-s3-bucket-permissions",
|
||||
"auditing-azure-active-directory-configuration",
|
||||
"auditing-cloud-with-cis-benchmarks",
|
||||
"auditing-entra-id-with-aadinternals",
|
||||
"auditing-foundry-smart-contract-security",
|
||||
"auditing-gcp-iam-permissions",
|
||||
"auditing-kubernetes-cluster-rbac",
|
||||
"auditing-mcp-servers-for-tool-poisoning",
|
||||
"auditing-terraform-infrastructure-for-security",
|
||||
"auditing-tls-certificate-transparency-logs",
|
||||
"auditing-uefi-firmware-with-chipsec",
|
||||
"automating-ioc-enrichment",
|
||||
"building-adversary-infrastructure-tracking-system",
|
||||
"building-attack-pattern-library-from-cti-reports",
|
||||
"building-automated-malware-submission-pipeline",
|
||||
"building-c2-infrastructure-with-sliver-framework",
|
||||
"building-c2-redirector-infrastructure",
|
||||
"building-detection-rule-with-splunk-spl",
|
||||
"building-detection-rules-with-sigma",
|
||||
"building-devsecops-pipeline-with-gitlab-ci",
|
||||
"building-identity-federation-with-saml-azure-ad",
|
||||
"building-identity-governance-lifecycle-process",
|
||||
"building-incident-response-dashboard",
|
||||
"building-incident-response-playbook",
|
||||
"building-incident-timeline-with-timesketch",
|
||||
"building-ioc-defanging-and-sharing-pipeline",
|
||||
"building-ioc-enrichment-pipeline-with-opencti",
|
||||
"building-malware-incident-communication-template",
|
||||
"building-patch-tuesday-response-process",
|
||||
"building-phishing-reporting-button-workflow",
|
||||
"building-ransomware-playbook-with-cisa-framework",
|
||||
"building-red-team-c2-infrastructure-with-havoc",
|
||||
"building-role-mining-for-rbac-optimization",
|
||||
"building-soc-escalation-matrix",
|
||||
"building-soc-metrics-and-kpi-tracking",
|
||||
"building-soc-playbook-for-ransomware",
|
||||
"building-super-timelines-with-plaso",
|
||||
"building-threat-actor-profile-from-osint",
|
||||
"building-threat-feed-aggregation-with-misp",
|
||||
"building-threat-hunt-hypothesis-framework",
|
||||
"building-threat-intelligence-enrichment-in-splunk",
|
||||
"building-threat-intelligence-feed-integration",
|
||||
"building-threat-intelligence-platform",
|
||||
"building-vulnerability-aging-and-sla-tracking",
|
||||
"building-vulnerability-dashboard-with-defectdojo",
|
||||
"building-vulnerability-exception-tracking-system",
|
||||
"building-vulnerability-scanning-workflow",
|
||||
"bypassing-authentication-with-forced-browsing",
|
||||
"coercing-authentication-with-coercer-petitpotam",
|
||||
"collecting-indicators-of-compromise",
|
||||
"collecting-open-source-intelligence",
|
||||
"collecting-threat-intelligence-with-misp",
|
||||
"collecting-volatile-evidence-from-compromised-host",
|
||||
"conducting-api-security-testing",
|
||||
"conducting-cloud-incident-response",
|
||||
"conducting-cloud-penetration-testing",
|
||||
"conducting-cyber-risk-assessment-with-nist-800-30",
|
||||
"conducting-domain-persistence-with-dcsync",
|
||||
"conducting-external-reconnaissance-with-osint",
|
||||
"conducting-full-scope-red-team-engagement",
|
||||
"conducting-internal-network-penetration-test",
|
||||
"conducting-internal-reconnaissance-with-bloodhound-ce",
|
||||
"conducting-malware-incident-response",
|
||||
"conducting-man-in-the-middle-attack-simulation",
|
||||
"conducting-memory-forensics-with-volatility",
|
||||
"conducting-mobile-app-penetration-test",
|
||||
"conducting-network-penetration-test",
|
||||
"conducting-pass-the-ticket-attack",
|
||||
"conducting-phishing-incident-response",
|
||||
"conducting-post-incident-lessons-learned",
|
||||
"conducting-social-engineering-penetration-test",
|
||||
"conducting-social-engineering-pretext-call",
|
||||
"conducting-spearphishing-simulation-campaign",
|
||||
"conducting-wireless-network-penetration-test",
|
||||
"configuring-active-directory-tiered-model",
|
||||
"configuring-aws-verified-access-for-ztna",
|
||||
"configuring-certificate-authority-with-openssl",
|
||||
"configuring-host-based-intrusion-detection",
|
||||
"configuring-hsm-for-key-storage",
|
||||
"configuring-identity-aware-proxy-with-google-iap",
|
||||
"configuring-ldap-security-hardening",
|
||||
"configuring-microsegmentation-for-zero-trust",
|
||||
"configuring-multi-factor-authentication-with-duo",
|
||||
"configuring-network-segmentation-with-vlans",
|
||||
"configuring-oauth2-authorization-flow",
|
||||
"configuring-pfsense-firewall-rules",
|
||||
"configuring-snort-ids-for-intrusion-detection",
|
||||
"configuring-suricata-for-network-monitoring",
|
||||
"configuring-tls-1-3-for-secure-communications",
|
||||
"configuring-windows-defender-advanced-settings",
|
||||
"configuring-windows-event-logging-for-detection",
|
||||
"configuring-zscaler-private-access-for-ztna",
|
||||
"containing-active-breach",
|
||||
"continuous-llm-red-teaming-with-promptfoo",
|
||||
"correlating-security-events-in-qradar",
|
||||
"correlating-threat-campaigns",
|
||||
"defending-llms-with-guardrails",
|
||||
"deobfuscating-javascript-malware",
|
||||
"deobfuscating-powershell-obfuscated-malware",
|
||||
"deploying-active-directory-honeytokens",
|
||||
"deploying-cloud-deception-with-decoy-resources",
|
||||
"deploying-cloudflare-access-for-zero-trust",
|
||||
"deploying-decoy-files-for-ransomware-detection",
|
||||
"deploying-edr-agent-with-crowdstrike",
|
||||
"deploying-honeytokens-and-canarytokens",
|
||||
"deploying-osquery-for-endpoint-monitoring",
|
||||
"deploying-palo-alto-prisma-access-zero-trust",
|
||||
"deploying-ransomware-canary-files",
|
||||
"deploying-software-defined-perimeter",
|
||||
"deploying-tailscale-for-zero-trust-vpn",
|
||||
"designing-adversary-engagement-with-mitre-engage",
|
||||
"detecting-ai-model-prompt-injection-attacks",
|
||||
"detecting-anomalies-in-industrial-control-systems",
|
||||
"detecting-anomalous-authentication-patterns",
|
||||
"detecting-api-enumeration-attacks",
|
||||
"detecting-arp-poisoning-in-network-traffic",
|
||||
"detecting-attacks-on-historian-servers",
|
||||
"detecting-attacks-on-scada-systems",
|
||||
"detecting-aws-cloudtrail-anomalies",
|
||||
"detecting-aws-credential-exposure-with-trufflehog",
|
||||
"detecting-aws-guardduty-findings-automation",
|
||||
"detecting-aws-iam-privilege-escalation",
|
||||
"detecting-azure-lateral-movement",
|
||||
"detecting-azure-service-principal-abuse",
|
||||
"detecting-azure-storage-account-misconfigurations",
|
||||
"detecting-beaconing-patterns-with-zeek",
|
||||
"detecting-bluetooth-low-energy-attacks",
|
||||
"detecting-broken-object-property-level-authorization",
|
||||
"detecting-business-email-compromise",
|
||||
"detecting-business-email-compromise-with-ai",
|
||||
"detecting-cloud-threats-with-guardduty",
|
||||
"detecting-command-and-control-over-dns",
|
||||
"detecting-compromised-cloud-credentials",
|
||||
"detecting-credential-dumping-techniques",
|
||||
"detecting-cryptomining-in-cloud",
|
||||
"detecting-data-and-model-poisoning",
|
||||
"detecting-dcsync-attack-in-active-directory",
|
||||
"detecting-deepfake-audio-in-vishing-attacks",
|
||||
"detecting-dependency-confusion",
|
||||
"detecting-dll-sideloading-attacks",
|
||||
"detecting-dnp3-protocol-anomalies",
|
||||
"detecting-dns-exfiltration-with-dns-query-analysis",
|
||||
"detecting-email-account-compromise",
|
||||
"detecting-email-forwarding-rules-attack",
|
||||
"detecting-entra-offensive-tools-in-graph-logs",
|
||||
"detecting-evasion-techniques-in-endpoint-logs",
|
||||
"detecting-exfiltration-over-dns-with-zeek",
|
||||
"detecting-fileless-attacks-on-endpoints",
|
||||
"detecting-fileless-malware-techniques",
|
||||
"detecting-golden-ticket-attacks-in-kerberos-logs",
|
||||
"detecting-golden-ticket-forgery",
|
||||
"detecting-indirect-prompt-injection",
|
||||
"detecting-insider-data-exfiltration-via-dlp",
|
||||
"detecting-insider-threat-behaviors",
|
||||
"detecting-insider-threat-with-ueba",
|
||||
"detecting-kerberoasting-attacks",
|
||||
"detecting-lateral-movement-in-network",
|
||||
"detecting-lateral-movement-with-splunk",
|
||||
"detecting-lateral-movement-with-zeek",
|
||||
"detecting-living-off-the-land-attacks",
|
||||
"detecting-living-off-the-land-with-lolbas",
|
||||
"detecting-malicious-npm-packages",
|
||||
"detecting-malicious-scheduled-tasks-with-sysmon",
|
||||
"detecting-mimikatz-execution-patterns",
|
||||
"detecting-misconfigured-azure-storage",
|
||||
"detecting-mobile-malware-behavior",
|
||||
"detecting-modbus-command-injection-attacks",
|
||||
"detecting-modbus-protocol-anomalies",
|
||||
"detecting-model-extraction-attacks",
|
||||
"detecting-network-anomalies-with-zeek",
|
||||
"detecting-network-scanning-with-ids-signatures",
|
||||
"detecting-ntlm-relay-with-event-correlation",
|
||||
"detecting-oauth-token-theft",
|
||||
"detecting-pass-the-hash-attacks",
|
||||
"detecting-pass-the-ticket-attacks",
|
||||
"detecting-port-scanning-with-fail2ban",
|
||||
"detecting-privilege-escalation-attempts",
|
||||
"detecting-process-hollowing-technique",
|
||||
"detecting-process-injection-techniques",
|
||||
"detecting-qr-code-phishing-with-email-security",
|
||||
"detecting-ransomware-encryption-behavior",
|
||||
"detecting-ransomware-precursors-in-network",
|
||||
"detecting-rdp-brute-force-attacks",
|
||||
"detecting-rootkit-activity",
|
||||
"detecting-s3-data-exfiltration-attempts",
|
||||
"detecting-secure-boot-bypass",
|
||||
"detecting-serverless-function-injection",
|
||||
"detecting-service-account-abuse",
|
||||
"detecting-shadow-api-endpoints",
|
||||
"detecting-shadow-it-cloud-usage",
|
||||
"detecting-spearphishing-with-email-gateway",
|
||||
"detecting-sql-injection-via-waf-logs",
|
||||
"detecting-stuxnet-style-attacks",
|
||||
"detecting-supply-chain-attacks-in-ci-cd",
|
||||
"detecting-suspicious-oauth-application-consent",
|
||||
"detecting-suspicious-powershell-execution",
|
||||
"detecting-t1003-credential-dumping-with-edr",
|
||||
"detecting-t1055-process-injection-with-sysmon",
|
||||
"detecting-t1548-abuse-elevation-control-mechanism",
|
||||
"detecting-typosquatting-packages",
|
||||
"detecting-typosquatting-packages-in-npm-pypi",
|
||||
"detecting-wmi-persistence",
|
||||
"emulating-cloud-attacks-with-stratus-red-team",
|
||||
"enumerating-cloud-with-cloudfox",
|
||||
"eradicating-malware-from-infected-systems",
|
||||
"evaluating-threat-intelligence-platforms",
|
||||
"executing-active-directory-attack-simulation",
|
||||
"executing-nist-rmf-authorization-to-operate",
|
||||
"executing-phishing-simulation-campaign",
|
||||
"executing-red-team-engagement-planning",
|
||||
"executing-red-team-exercise",
|
||||
"exploiting-active-directory-certificate-services-esc1",
|
||||
"exploiting-active-directory-with-bloodhound",
|
||||
"exploiting-adcs-with-certipy",
|
||||
"exploiting-api-injection-vulnerabilities",
|
||||
"exploiting-aws-with-pacu",
|
||||
"exploiting-bgp-hijacking-vulnerabilities",
|
||||
"exploiting-broken-function-level-authorization",
|
||||
"exploiting-broken-link-hijacking",
|
||||
"exploiting-constrained-delegation-abuse",
|
||||
"exploiting-deeplink-vulnerabilities",
|
||||
"exploiting-excessive-data-exposure-in-api",
|
||||
"exploiting-http-request-smuggling",
|
||||
"exploiting-idor-vulnerabilities",
|
||||
"exploiting-insecure-data-storage-in-mobile",
|
||||
"exploiting-insecure-deserialization",
|
||||
"exploiting-ipv6-vulnerabilities",
|
||||
"exploiting-jwt-algorithm-confusion-attack",
|
||||
"exploiting-kerberoasting-with-impacket",
|
||||
"exploiting-mass-assignment-in-rest-apis",
|
||||
"exploiting-ms17-010-eternalblue-vulnerability",
|
||||
"exploiting-nopac-cve-2021-42278-42287",
|
||||
"exploiting-nosql-injection-vulnerabilities",
|
||||
"exploiting-oauth-misconfiguration",
|
||||
"exploiting-prototype-pollution-in-javascript",
|
||||
"exploiting-race-condition-vulnerabilities",
|
||||
"exploiting-server-side-request-forgery",
|
||||
"exploiting-smb-vulnerabilities-with-metasploit",
|
||||
"exploiting-sql-injection-vulnerabilities",
|
||||
"exploiting-sql-injection-with-sqlmap",
|
||||
"exploiting-template-injection-vulnerabilities",
|
||||
"exploiting-type-juggling-vulnerabilities",
|
||||
"exploiting-vulnerabilities-with-metasploit-framework",
|
||||
"exploiting-websocket-vulnerabilities",
|
||||
"exploiting-zerologon-vulnerability-cve-2020-1472",
|
||||
"extracting-browser-history-artifacts",
|
||||
"extracting-config-from-agent-tesla-rat",
|
||||
"extracting-credentials-from-memory-dump",
|
||||
"extracting-iocs-from-malware-samples",
|
||||
"extracting-memory-artifacts-with-rekall",
|
||||
"extracting-windows-event-logs-artifacts",
|
||||
"fleet-hunting-with-velociraptor",
|
||||
"generating-and-analyzing-sboms",
|
||||
"generating-forensic-timelines-with-hayabusa",
|
||||
"generating-threat-intelligence-reports",
|
||||
"hardening-linux-endpoint-with-cis-benchmark",
|
||||
"hardening-windows-endpoint-with-cis-benchmark",
|
||||
"hunting-advanced-persistent-threats",
|
||||
"hunting-bootkits-in-efi-system-partition",
|
||||
"hunting-credential-stuffing-attacks",
|
||||
"hunting-evtx-with-chainsaw",
|
||||
"hunting-for-anomalous-powershell-execution",
|
||||
"hunting-for-beaconing-with-frequency-analysis",
|
||||
"hunting-for-cobalt-strike-beacons",
|
||||
"hunting-for-command-and-control-beaconing",
|
||||
"hunting-for-data-exfiltration-indicators",
|
||||
"hunting-for-data-staging-before-exfiltration",
|
||||
"hunting-for-dcom-lateral-movement",
|
||||
"hunting-for-dcsync-attacks",
|
||||
"hunting-for-defense-evasion-via-timestomping",
|
||||
"hunting-for-dns-based-persistence",
|
||||
"hunting-for-dns-tunneling-with-zeek",
|
||||
"hunting-for-domain-fronting-c2-traffic",
|
||||
"hunting-for-lateral-movement-via-wmi",
|
||||
"hunting-for-living-off-the-cloud-techniques",
|
||||
"hunting-for-living-off-the-land-binaries",
|
||||
"hunting-for-lolbins-execution-in-endpoint-logs",
|
||||
"hunting-for-ntlm-relay-attacks",
|
||||
"hunting-for-persistence-mechanisms-in-windows",
|
||||
"hunting-for-persistence-via-wmi-subscriptions",
|
||||
"hunting-for-process-injection-techniques",
|
||||
"hunting-for-registry-persistence-mechanisms",
|
||||
"hunting-for-registry-run-key-persistence",
|
||||
"hunting-for-scheduled-task-persistence",
|
||||
"hunting-for-shadow-copy-deletion",
|
||||
"hunting-for-spearphishing-indicators",
|
||||
"hunting-for-startup-folder-persistence",
|
||||
"hunting-for-supply-chain-compromise",
|
||||
"hunting-for-suspicious-scheduled-tasks",
|
||||
"hunting-for-t1098-account-manipulation",
|
||||
"hunting-for-unusual-network-connections",
|
||||
"hunting-for-unusual-service-installations",
|
||||
"hunting-for-webshell-activity",
|
||||
"hunting-saas-sso-token-abuse",
|
||||
"implementing-aes-encryption-for-data-at-rest",
|
||||
"implementing-alert-fatigue-reduction",
|
||||
"implementing-anti-phishing-training-program",
|
||||
"implementing-anti-ransomware-group-policy",
|
||||
"implementing-api-abuse-detection-with-rate-limiting",
|
||||
"implementing-api-gateway-security-controls",
|
||||
"implementing-api-key-security-controls",
|
||||
"implementing-api-rate-limiting-and-throttling",
|
||||
"implementing-api-schema-validation-security",
|
||||
"implementing-api-security-posture-management",
|
||||
"implementing-api-security-testing-with-42crunch",
|
||||
"implementing-api-threat-protection-with-apigee",
|
||||
"implementing-application-whitelisting-with-applocker",
|
||||
"implementing-aqua-security-for-container-scanning",
|
||||
"implementing-attack-path-analysis-with-xm-cyber",
|
||||
"implementing-attack-surface-management",
|
||||
"implementing-aws-config-rules-for-compliance",
|
||||
"implementing-aws-iam-permission-boundaries",
|
||||
"implementing-aws-macie-for-data-classification",
|
||||
"implementing-aws-nitro-enclave-security",
|
||||
"implementing-azure-ad-privileged-identity-management",
|
||||
"implementing-bgp-security-with-rpki",
|
||||
"implementing-browser-isolation-for-zero-trust",
|
||||
"implementing-canary-tokens-for-network-intrusion",
|
||||
"implementing-cisa-zero-trust-maturity-model",
|
||||
"implementing-cloud-trail-log-analysis",
|
||||
"implementing-cloud-vulnerability-posture-management",
|
||||
"implementing-cloud-waf-rules",
|
||||
"implementing-cloud-workload-protection",
|
||||
"implementing-code-signing-for-artifacts",
|
||||
"implementing-conditional-access-policies-azure-ad",
|
||||
"implementing-conduit-security-for-ot-remote-access",
|
||||
"implementing-continuous-security-validation-with-bas",
|
||||
"implementing-data-loss-prevention-with-microsoft-purview",
|
||||
"implementing-ddos-mitigation-with-cloudflare",
|
||||
"implementing-deception-based-detection-with-canarytoken",
|
||||
"implementing-delinea-secret-server-for-pam",
|
||||
"implementing-device-posture-assessment-in-zero-trust",
|
||||
"implementing-devsecops-security-scanning",
|
||||
"implementing-diamond-model-analysis",
|
||||
"implementing-digital-signatures-with-ed25519",
|
||||
"implementing-disk-encryption-with-bitlocker",
|
||||
"implementing-dmarc-dkim-spf-email-security",
|
||||
"implementing-ebpf-security-monitoring",
|
||||
"implementing-email-sandboxing-with-proofpoint",
|
||||
"implementing-end-to-end-encryption-for-messaging",
|
||||
"implementing-endpoint-detection-with-wazuh",
|
||||
"implementing-endpoint-dlp-controls",
|
||||
"implementing-envelope-encryption-with-aws-kms",
|
||||
"implementing-epss-score-for-vulnerability-prioritization",
|
||||
"implementing-file-integrity-monitoring-with-aide",
|
||||
"implementing-fuzz-testing-in-cicd-with-aflplusplus",
|
||||
"implementing-gcp-binary-authorization",
|
||||
"implementing-gcp-organization-policy-constraints",
|
||||
"implementing-gdpr-data-protection-controls",
|
||||
"implementing-gdpr-data-subject-access-request",
|
||||
"implementing-github-advanced-security-for-code-scanning",
|
||||
"implementing-google-workspace-admin-security",
|
||||
"implementing-google-workspace-phishing-protection",
|
||||
"implementing-google-workspace-sso-configuration",
|
||||
"implementing-hardware-security-key-authentication",
|
||||
"implementing-hashicorp-vault-dynamic-secrets",
|
||||
"implementing-hipaa-security-rule-safeguards",
|
||||
"implementing-honeypot-for-ransomware-detection",
|
||||
"implementing-honeytokens-for-breach-detection",
|
||||
"implementing-ics-firewall-with-tofino",
|
||||
"implementing-identity-governance-with-sailpoint",
|
||||
"implementing-identity-verification-for-zero-trust",
|
||||
"implementing-iec-62443-security-zones",
|
||||
"implementing-immutable-backup-with-restic",
|
||||
"implementing-infrastructure-as-code-security-scanning",
|
||||
"implementing-iso-27001-information-security-management",
|
||||
"implementing-just-in-time-access-provisioning",
|
||||
"implementing-jwt-signing-and-verification",
|
||||
"implementing-llm-guardrails-for-security",
|
||||
"implementing-log-forwarding-with-fluentd",
|
||||
"implementing-log-integrity-with-blockchain",
|
||||
"implementing-memory-protection-with-dep-aslr",
|
||||
"implementing-microsegmentation-with-guardicore",
|
||||
"implementing-mimecast-targeted-attack-protection",
|
||||
"implementing-mitre-attack-coverage-mapping",
|
||||
"implementing-mobile-application-management",
|
||||
"implementing-mtls-for-zero-trust-services",
|
||||
"implementing-nerc-cip-compliance-controls",
|
||||
"implementing-network-access-control",
|
||||
"implementing-network-access-control-with-cisco-ise",
|
||||
"implementing-network-deception-with-honeypots",
|
||||
"implementing-network-intrusion-prevention-with-suricata",
|
||||
"implementing-network-segmentation-for-ot",
|
||||
"implementing-network-segmentation-with-firewall-zones",
|
||||
"implementing-network-traffic-analysis-with-arkime",
|
||||
"implementing-network-traffic-baselining",
|
||||
"implementing-next-generation-firewall-with-palo-alto",
|
||||
"implementing-ot-incident-response-playbook",
|
||||
"implementing-ot-network-traffic-analysis-with-nozomi",
|
||||
"implementing-pam-for-database-access",
|
||||
"implementing-passwordless-auth-with-microsoft-entra",
|
||||
"implementing-passwordless-authentication-with-fido2",
|
||||
"implementing-patch-management-for-ot-systems",
|
||||
"implementing-patch-management-workflow",
|
||||
"implementing-pci-dss-compliance-controls",
|
||||
"implementing-policy-as-code-with-open-policy-agent",
|
||||
"implementing-privileged-access-management-with-cyberark",
|
||||
"implementing-privileged-access-workstation",
|
||||
"implementing-privileged-session-monitoring",
|
||||
"implementing-proofpoint-email-security-gateway",
|
||||
"implementing-purdue-model-network-segmentation",
|
||||
"implementing-ransomware-backup-strategy",
|
||||
"implementing-ransomware-kill-switch-detection",
|
||||
"implementing-rapid7-insightvm-for-scanning",
|
||||
"implementing-rsa-key-pair-management",
|
||||
"implementing-runtime-application-self-protection",
|
||||
"implementing-saml-sso-with-okta",
|
||||
"implementing-scim-provisioning-with-okta",
|
||||
"implementing-secret-scanning-with-gitleaks",
|
||||
"implementing-secrets-management-with-vault",
|
||||
"implementing-secrets-scanning-in-ci-cd",
|
||||
"implementing-security-chaos-engineering",
|
||||
"implementing-security-information-sharing-with-stix2",
|
||||
"implementing-security-monitoring-with-datadog",
|
||||
"implementing-semgrep-for-custom-sast-rules",
|
||||
"implementing-siem-correlation-rules-for-apt",
|
||||
"implementing-siem-use-case-tuning",
|
||||
"implementing-siem-use-cases-for-detection",
|
||||
"implementing-sigstore-for-software-signing",
|
||||
"implementing-soar-automation-with-phantom",
|
||||
"implementing-soar-playbook-for-phishing",
|
||||
"implementing-soar-playbook-with-palo-alto-xsoar",
|
||||
"implementing-stix-taxii-feed-integration",
|
||||
"implementing-syslog-centralization-with-rsyslog",
|
||||
"implementing-taxii-server-with-opentaxii",
|
||||
"implementing-threat-intelligence-lifecycle-management",
|
||||
"implementing-threat-modeling-with-mitre-attack",
|
||||
"implementing-ticketing-system-for-incidents",
|
||||
"implementing-usb-device-control-policy",
|
||||
"implementing-velociraptor-for-ir-collection",
|
||||
"implementing-vulnerability-management-with-greenbone",
|
||||
"implementing-vulnerability-remediation-sla",
|
||||
"implementing-vulnerability-sla-breach-alerting",
|
||||
"implementing-web-application-logging-with-modsecurity",
|
||||
"implementing-zero-knowledge-proof-for-authentication",
|
||||
"implementing-zero-standing-privilege-with-cyberark",
|
||||
"implementing-zero-trust-dns-with-nextdns",
|
||||
"implementing-zero-trust-for-saas-applications",
|
||||
"implementing-zero-trust-in-cloud",
|
||||
"implementing-zero-trust-network-access",
|
||||
"implementing-zero-trust-network-access-with-zscaler",
|
||||
"implementing-zero-trust-with-beyondcorp",
|
||||
"implementing-zero-trust-with-hashicorp-boundary",
|
||||
"integrating-dast-with-owasp-zap-in-pipeline",
|
||||
"integrating-sast-into-github-actions-pipeline",
|
||||
"intercepting-mobile-traffic-with-burpsuite",
|
||||
"investigating-insider-threat-indicators",
|
||||
"investigating-phishing-email-incident",
|
||||
"investigating-ransomware-attack-artifacts",
|
||||
"managing-cloud-identity-with-okta",
|
||||
"managing-intelligence-lifecycle",
|
||||
"managing-third-party-vendor-risk",
|
||||
"mapping-attack-paths-with-bloodhound-ce",
|
||||
"mapping-mitre-attack-techniques",
|
||||
"migrating-to-post-quantum-cryptography",
|
||||
"modeling-threats-with-opencti",
|
||||
"monitoring-darkweb-sources",
|
||||
"monitoring-scada-modbus-traffic-anomalies",
|
||||
"moving-laterally-with-netexec",
|
||||
"operating-havoc-c2",
|
||||
"operating-sliver-c2",
|
||||
"operationalizing-misp-threat-feeds",
|
||||
"orchestrating-llm-attacks-with-pyrit",
|
||||
"parsing-artifacts-with-eric-zimmerman-tools",
|
||||
"performing-access-recertification-with-saviynt",
|
||||
"performing-access-review-and-certification",
|
||||
"performing-active-directory-bloodhound-analysis",
|
||||
"performing-active-directory-compromise-investigation",
|
||||
"performing-active-directory-forest-trust-attack",
|
||||
"performing-active-directory-penetration-test",
|
||||
"performing-active-directory-vulnerability-assessment",
|
||||
"performing-adversary-in-the-middle-phishing-detection",
|
||||
"performing-agentless-vulnerability-scanning",
|
||||
"performing-ai-driven-osint-correlation",
|
||||
"performing-alert-triage-with-elastic-siem",
|
||||
"performing-android-app-static-analysis-with-mobsf",
|
||||
"performing-api-fuzzing-with-restler",
|
||||
"performing-api-inventory-and-discovery",
|
||||
"performing-api-rate-limiting-bypass",
|
||||
"performing-api-security-testing-with-postman",
|
||||
"performing-asset-criticality-scoring-for-vulns",
|
||||
"performing-authenticated-scan-with-openvas",
|
||||
"performing-authenticated-vulnerability-scan",
|
||||
"performing-automated-malware-analysis-with-cape",
|
||||
"performing-aws-account-enumeration-with-scout-suite",
|
||||
"performing-aws-privilege-escalation-assessment",
|
||||
"performing-bandwidth-throttling-attack-simulation",
|
||||
"performing-binary-exploitation-analysis",
|
||||
"performing-blind-ssrf-exploitation",
|
||||
"performing-bluetooth-security-assessment",
|
||||
"performing-brand-monitoring-for-impersonation",
|
||||
"performing-clickjacking-attack-test",
|
||||
"performing-cloud-asset-inventory-with-cartography",
|
||||
"performing-cloud-forensics-investigation",
|
||||
"performing-cloud-forensics-with-aws-cloudtrail",
|
||||
"performing-cloud-incident-containment-procedures",
|
||||
"performing-cloud-log-forensics-with-athena",
|
||||
"performing-cloud-native-forensics-with-falco",
|
||||
"performing-cloud-native-threat-hunting-with-aws-detective",
|
||||
"performing-cloud-penetration-testing-with-pacu",
|
||||
"performing-cloud-storage-forensic-acquisition",
|
||||
"performing-container-image-hardening",
|
||||
"performing-content-security-policy-bypass",
|
||||
"performing-credential-access-with-lazagne",
|
||||
"performing-cryptographic-audit-of-application",
|
||||
"performing-csrf-attack-simulation",
|
||||
"performing-cve-prioritization-with-kev-catalog",
|
||||
"performing-dark-web-monitoring-for-threats",
|
||||
"performing-deception-technology-deployment",
|
||||
"performing-directory-traversal-testing",
|
||||
"performing-disk-forensics-investigation",
|
||||
"performing-dmarc-policy-enforcement-rollout",
|
||||
"performing-dns-enumeration-and-zone-transfer",
|
||||
"performing-dns-tunneling-detection",
|
||||
"performing-dynamic-analysis-of-android-app",
|
||||
"performing-dynamic-analysis-with-any-run",
|
||||
"performing-endpoint-forensics-investigation",
|
||||
"performing-endpoint-vulnerability-remediation",
|
||||
"performing-entitlement-review-with-sailpoint-iiq",
|
||||
"performing-external-network-penetration-test",
|
||||
"performing-false-positive-reduction-in-siem",
|
||||
"performing-file-carving-with-foremost",
|
||||
"performing-firmware-extraction-with-binwalk",
|
||||
"performing-firmware-malware-analysis",
|
||||
"performing-fuzzing-with-aflplusplus",
|
||||
"performing-gcp-penetration-testing-with-gcpbucketbrute",
|
||||
"performing-gcp-security-assessment-with-forseti",
|
||||
"performing-graphql-depth-limit-attack",
|
||||
"performing-graphql-introspection-attack",
|
||||
"performing-graphql-security-assessment",
|
||||
"performing-hardware-security-module-integration",
|
||||
"performing-hash-cracking-with-hashcat",
|
||||
"performing-http-parameter-pollution-attack",
|
||||
"performing-indicator-lifecycle-management",
|
||||
"performing-initial-access-with-evilginx3",
|
||||
"performing-insider-threat-investigation",
|
||||
"performing-ioc-enrichment-automation",
|
||||
"performing-ios-app-security-assessment",
|
||||
"performing-iot-security-assessment",
|
||||
"performing-ip-reputation-analysis-with-shodan",
|
||||
"performing-jwt-none-algorithm-attack",
|
||||
"performing-kerberoasting-attack",
|
||||
"performing-lateral-movement-with-wmiexec",
|
||||
"performing-linux-log-forensics-investigation",
|
||||
"performing-log-analysis-for-forensic-investigation",
|
||||
"performing-log-source-onboarding-in-siem",
|
||||
"performing-malware-hash-enrichment-with-virustotal",
|
||||
"performing-malware-ioc-extraction",
|
||||
"performing-malware-persistence-investigation",
|
||||
"performing-malware-triage-with-yara",
|
||||
"performing-memory-forensics-with-volatility3",
|
||||
"performing-memory-forensics-with-volatility3-plugins",
|
||||
"performing-mobile-app-certificate-pinning-bypass",
|
||||
"performing-mobile-device-forensics-with-cellebrite",
|
||||
"performing-network-forensics-with-wireshark",
|
||||
"performing-network-packet-capture-analysis",
|
||||
"performing-network-traffic-analysis-with-tshark",
|
||||
"performing-network-traffic-analysis-with-zeek",
|
||||
"performing-nist-csf-maturity-assessment",
|
||||
"performing-oauth-scope-minimization-review",
|
||||
"performing-open-source-intelligence-gathering",
|
||||
"performing-osint-with-spiderfoot",
|
||||
"performing-ot-network-security-assessment",
|
||||
"performing-ot-vulnerability-scanning-safely",
|
||||
"performing-packet-injection-attack",
|
||||
"performing-paste-site-monitoring-for-credentials",
|
||||
"performing-phishing-simulation-with-gophish",
|
||||
"performing-physical-intrusion-assessment",
|
||||
"performing-plc-firmware-security-analysis",
|
||||
"performing-post-quantum-cryptography-migration",
|
||||
"performing-privacy-impact-assessment",
|
||||
"performing-privilege-escalation-assessment",
|
||||
"performing-privilege-escalation-on-linux",
|
||||
"performing-privileged-account-access-review",
|
||||
"performing-privileged-account-discovery",
|
||||
"performing-purple-team-atomic-testing",
|
||||
"performing-purple-team-exercise",
|
||||
"performing-ransomware-response",
|
||||
"performing-ransomware-tabletop-exercise",
|
||||
"performing-red-team-phishing-with-gophish",
|
||||
"performing-red-team-with-covenant",
|
||||
"performing-s7comm-protocol-security-analysis",
|
||||
"performing-sca-dependency-scanning-with-snyk",
|
||||
"performing-scada-hmi-security-assessment",
|
||||
"performing-second-order-sql-injection",
|
||||
"performing-security-headers-audit",
|
||||
"performing-serverless-function-security-review",
|
||||
"performing-service-account-audit",
|
||||
"performing-service-account-credential-rotation",
|
||||
"performing-soap-web-service-security-testing",
|
||||
"performing-soc-tabletop-exercise",
|
||||
"performing-soc2-type2-audit-preparation",
|
||||
"performing-sqlite-database-forensics",
|
||||
"performing-ssl-certificate-lifecycle-management",
|
||||
"performing-ssl-stripping-attack",
|
||||
"performing-ssl-tls-inspection-configuration",
|
||||
"performing-ssl-tls-security-assessment",
|
||||
"performing-ssrf-vulnerability-exploitation",
|
||||
"performing-static-malware-analysis-with-pe-studio",
|
||||
"performing-steganography-detection",
|
||||
"performing-subdomain-enumeration-with-subfinder",
|
||||
"performing-supply-chain-attack-simulation",
|
||||
"performing-thick-client-application-penetration-test",
|
||||
"performing-threat-emulation-with-atomic-red-team",
|
||||
"performing-threat-hunting-with-elastic-siem",
|
||||
"performing-threat-hunting-with-yara-rules",
|
||||
"performing-threat-intelligence-sharing-with-misp",
|
||||
"performing-threat-landscape-assessment-for-sector",
|
||||
"performing-threat-modeling-with-owasp-threat-dragon",
|
||||
"performing-timeline-reconstruction-with-plaso",
|
||||
"performing-user-behavior-analytics",
|
||||
"performing-vlan-hopping-attack",
|
||||
"performing-vulnerability-scanning-with-nessus",
|
||||
"performing-web-application-firewall-bypass",
|
||||
"performing-web-application-penetration-test",
|
||||
"performing-web-application-scanning-with-nikto",
|
||||
"performing-web-application-vulnerability-triage",
|
||||
"performing-web-cache-deception-attack",
|
||||
"performing-web-cache-poisoning-attack",
|
||||
"performing-wifi-password-cracking-with-aircrack",
|
||||
"performing-windows-artifact-analysis-with-eric-zimmerman-tools",
|
||||
"performing-wireless-network-penetration-test",
|
||||
"performing-wireless-security-assessment-with-kismet",
|
||||
"performing-yara-rule-development-for-detection",
|
||||
"post-exploiting-microsoft-graph-with-graphrunner",
|
||||
"prioritizing-vulnerabilities-with-cvss-scoring",
|
||||
"processing-stix-taxii-feeds",
|
||||
"profiling-threat-actor-groups",
|
||||
"recovering-deleted-files-with-photorec",
|
||||
"recovering-from-ransomware-attack",
|
||||
"red-teaming-llms-with-garak",
|
||||
"relaying-ntlm-for-adcs-esc8",
|
||||
"remediating-s3-bucket-misconfiguration",
|
||||
"reverse-engineering-android-malware-with-jadx",
|
||||
"reverse-engineering-dotnet-malware-with-dnspy",
|
||||
"reverse-engineering-ios-app-with-frida",
|
||||
"reverse-engineering-malware-with-ghidra",
|
||||
"reverse-engineering-ransomware-encryption-routine",
|
||||
"reverse-engineering-rust-malware",
|
||||
"scanning-containers-with-trivy-in-cicd",
|
||||
"scanning-iac-and-images-with-trivy",
|
||||
"scanning-infrastructure-with-nessus",
|
||||
"scanning-network-with-nmap-advanced",
|
||||
"securing-agentic-ai-tool-invocation",
|
||||
"securing-api-gateway-with-aws-waf",
|
||||
"securing-aws-iam-permissions",
|
||||
"securing-aws-lambda-execution-roles",
|
||||
"securing-azure-with-microsoft-defender",
|
||||
"securing-container-registry-images",
|
||||
"securing-github-actions-workflows",
|
||||
"securing-historian-server-in-ot-environment",
|
||||
"securing-kubernetes-on-cloud",
|
||||
"securing-remote-access-to-ot-environment",
|
||||
"securing-serverless-functions",
|
||||
"testing-android-intents-for-vulnerabilities",
|
||||
"testing-api-authentication-weaknesses",
|
||||
"testing-api-for-broken-object-level-authorization",
|
||||
"testing-api-for-mass-assignment-vulnerability",
|
||||
"testing-api-security-with-owasp-top-10",
|
||||
"testing-cors-misconfiguration",
|
||||
"testing-for-broken-access-control",
|
||||
"testing-for-business-logic-vulnerabilities",
|
||||
"testing-for-email-header-injection",
|
||||
"testing-for-host-header-injection",
|
||||
"testing-for-json-web-token-vulnerabilities",
|
||||
"testing-for-open-redirect-vulnerabilities",
|
||||
"testing-for-sensitive-data-exposure",
|
||||
"testing-for-system-prompt-leakage",
|
||||
"testing-for-xml-injection-vulnerabilities",
|
||||
"testing-for-xss-vulnerabilities",
|
||||
"testing-for-xss-vulnerabilities-with-burpsuite",
|
||||
"testing-for-xxe-injection-vulnerabilities",
|
||||
"testing-jwt-token-security",
|
||||
"testing-mobile-api-authentication",
|
||||
"testing-oauth2-implementation-flaws",
|
||||
"testing-prompt-injection-in-rag-pipelines",
|
||||
"testing-ransomware-recovery-procedures",
|
||||
"testing-websocket-api-security",
|
||||
"tracking-threat-actor-infrastructure",
|
||||
"triaging-security-alerts-in-splunk",
|
||||
"triaging-security-incident",
|
||||
"triaging-security-incident-with-ir-playbook",
|
||||
"triaging-vulnerabilities-with-ssvc-framework",
|
||||
"triaging-windows-with-kape",
|
||||
"validating-backup-integrity-for-recovery",
|
||||
"validating-tpm-measured-boot-attestation",
|
||||
"verifying-build-provenance-with-slsa-sigstore"
|
||||
],
|
||||
"body-max-lines": [
|
||||
"building-automated-malware-submission-pipeline",
|
||||
"building-identity-governance-lifecycle-process",
|
||||
"detecting-anomalous-authentication-patterns",
|
||||
"detecting-attacks-on-scada-systems",
|
||||
"detecting-command-and-control-over-dns",
|
||||
"detecting-living-off-the-land-attacks",
|
||||
"detecting-modbus-command-injection-attacks",
|
||||
"detecting-ntlm-relay-with-event-correlation",
|
||||
"detecting-serverless-function-injection",
|
||||
"hunting-for-dcom-lateral-movement",
|
||||
"implementing-data-loss-prevention-with-microsoft-purview",
|
||||
"implementing-google-workspace-admin-security",
|
||||
"implementing-hashicorp-vault-dynamic-secrets",
|
||||
"implementing-iec-62443-security-zones",
|
||||
"implementing-passwordless-auth-with-microsoft-entra",
|
||||
"implementing-zero-trust-with-hashicorp-boundary",
|
||||
"performing-cloud-log-forensics-with-athena",
|
||||
"performing-graphql-introspection-attack",
|
||||
"performing-ics-asset-discovery-with-claroty",
|
||||
"performing-oauth-scope-minimization-review",
|
||||
"performing-ot-network-security-assessment",
|
||||
"performing-plc-firmware-security-analysis",
|
||||
"performing-purple-team-atomic-testing"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Lint SKILL.md descriptions for the qualities that drive agent routing.
|
||||
|
||||
The description is the ONLY signal an agent sees at discovery time, so it must
|
||||
say what the skill does, when to fire, and -- critically -- when NOT to fire.
|
||||
Skills whose descriptions overlap without a distinguishing negative trigger get
|
||||
misrouted ("skill collision").
|
||||
|
||||
Rules
|
||||
-----
|
||||
name-matches-folder frontmatter name == directory name
|
||||
desc-max-length description <= 1024 chars (agentskills.io limit)
|
||||
desc-ends-punctuation ends in . ! ? ) " ' -- a truncation canary
|
||||
desc-has-use-when carries an explicit trigger clause
|
||||
desc-has-negative-trigger says what it is NOT for
|
||||
body-max-lines SKILL.md <= 500 lines (Anthropic guidance)
|
||||
|
||||
Grandfathering
|
||||
--------------
|
||||
Known pre-existing failures live in tools/lint-baseline.json so CI can go green
|
||||
today while the debt is paid down. A baselined skill that starts passing is
|
||||
reported as a stale entry -- run --update-baseline to shrink the file. The
|
||||
baseline can only ever shrink in review, never silently grow: any NEW failure
|
||||
is a hard error.
|
||||
|
||||
Usage:
|
||||
python tools/lint-descriptions.py --all
|
||||
python tools/lint-descriptions.py --all --stats
|
||||
python tools/lint-descriptions.py --update-baseline
|
||||
python tools/lint-descriptions.py skills/<slug>
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from skill_frontmatter import description_of, iter_skill_dirs, load_frontmatter, FrontmatterError
|
||||
|
||||
BASELINE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lint-baseline.json")
|
||||
|
||||
DESCRIPTION_MAX_CHARS = 1024
|
||||
BODY_MAX_LINES = 500
|
||||
SENTENCE_ENDINGS = ".!?)\"'"
|
||||
|
||||
# "Use when ...", "Use this skill when ...", "Use for ...", "Use during ..."
|
||||
USE_WHEN_RE = re.compile(
|
||||
r"\buse\s+(?:this\s+)?(?:skill\s+)?(?:when|whenever|for|during|after|before)\b",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
# "Do not use for X", "Don't use when Y", "Not for Z"
|
||||
NEGATIVE_TRIGGER_RE = re.compile(
|
||||
r"\b(?:do\s+not\s+use|don'?t\s+use|not\s+for\b|avoid\s+(?:this\s+)?(?:skill\s+)?for)\b",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
RULES = (
|
||||
"name-matches-folder",
|
||||
"desc-max-length",
|
||||
"desc-ends-punctuation",
|
||||
"desc-has-use-when",
|
||||
"desc-has-negative-trigger",
|
||||
"body-max-lines",
|
||||
)
|
||||
|
||||
RED, GREEN, YELLOW, DIM, RESET = "\033[91m", "\033[92m", "\033[93m", "\033[2m", "\033[0m"
|
||||
|
||||
|
||||
def check_skill(slug: str, skill_dir: str) -> dict[str, str]:
|
||||
"""Return {rule_id: human message} for every rule this skill violates."""
|
||||
skill_md = os.path.join(skill_dir, "SKILL.md")
|
||||
violations: dict[str, str] = {}
|
||||
|
||||
try:
|
||||
frontmatter = load_frontmatter(skill_md)
|
||||
except FrontmatterError as exc:
|
||||
return {"name-matches-folder": f"unparseable frontmatter: {exc}"}
|
||||
|
||||
name = str(frontmatter.get("name", "") or "")
|
||||
if name != slug:
|
||||
violations["name-matches-folder"] = f"name {name!r} != folder {slug!r}"
|
||||
|
||||
description = description_of(frontmatter)
|
||||
|
||||
if len(description) > DESCRIPTION_MAX_CHARS:
|
||||
violations["desc-max-length"] = (
|
||||
f"{len(description)} chars, max {DESCRIPTION_MAX_CHARS}")
|
||||
|
||||
if description and description[-1] not in SENTENCE_ENDINGS:
|
||||
violations["desc-ends-punctuation"] = f"ends with {description[-40:]!r}"
|
||||
|
||||
if not USE_WHEN_RE.search(description):
|
||||
violations["desc-has-use-when"] = "no trigger clause (add 'Use when ...')"
|
||||
|
||||
if not NEGATIVE_TRIGGER_RE.search(description):
|
||||
violations["desc-has-negative-trigger"] = (
|
||||
"no negative trigger (add 'Do not use for X - use <other-skill>.')")
|
||||
|
||||
with open(skill_md, encoding="utf-8") as handle:
|
||||
line_count = sum(1 for _ in handle)
|
||||
if line_count > BODY_MAX_LINES:
|
||||
violations["body-max-lines"] = f"{line_count} lines, max {BODY_MAX_LINES}"
|
||||
|
||||
return violations
|
||||
|
||||
|
||||
def collect(skills_dir: str = "skills") -> dict[str, dict[str, str]]:
|
||||
return {slug: v for slug, d in iter_skill_dirs(skills_dir)
|
||||
if (v := check_skill(slug, d))}
|
||||
|
||||
|
||||
def load_baseline() -> dict[str, list[str]]:
|
||||
if not os.path.isfile(BASELINE_PATH):
|
||||
return {}
|
||||
with open(BASELINE_PATH, encoding="utf-8") as handle:
|
||||
return {k: v for k, v in json.load(handle).items() if not k.startswith("_")}
|
||||
|
||||
|
||||
def write_baseline(violations: dict[str, dict[str, str]]) -> dict[str, list[str]]:
|
||||
baseline = {rule: sorted(s for s, v in violations.items() if rule in v)
|
||||
for rule in RULES}
|
||||
baseline = {rule: slugs for rule, slugs in baseline.items() if slugs}
|
||||
payload = {
|
||||
"_comment": (
|
||||
"Pre-existing lint failures, grandfathered so CI can gate new work today. "
|
||||
"This file may only shrink. Never add a slug by hand -- fix the skill, then "
|
||||
"run: python tools/lint-descriptions.py --update-baseline"
|
||||
),
|
||||
"_total_grandfathered": sum(len(s) for s in baseline.values()),
|
||||
**baseline,
|
||||
}
|
||||
with open(BASELINE_PATH, "w", encoding="utf-8") as handle:
|
||||
json.dump(payload, handle, indent=2)
|
||||
handle.write("\n")
|
||||
return baseline
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("target", nargs="?", help="a single skills/<slug> directory")
|
||||
parser.add_argument("--all", action="store_true", help="lint every skill")
|
||||
parser.add_argument("--stats", action="store_true", help="show per-rule totals")
|
||||
parser.add_argument("--update-baseline", action="store_true",
|
||||
help="rewrite lint-baseline.json from current state")
|
||||
parser.add_argument("--skills-dir", default="skills")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.isdir(args.skills_dir):
|
||||
print(f"ERROR: '{args.skills_dir}' not found. Run from the repository root.")
|
||||
return 1
|
||||
|
||||
single_skill = bool(args.target) and not (args.all or args.update_baseline)
|
||||
if single_skill:
|
||||
slug = os.path.basename(args.target.rstrip("/\\"))
|
||||
violations = {slug: v} if (v := check_skill(slug, args.target.rstrip("/\\"))) else {}
|
||||
else:
|
||||
violations = collect(args.skills_dir)
|
||||
|
||||
if args.update_baseline:
|
||||
baseline = write_baseline(violations)
|
||||
total = sum(len(s) for s in baseline.values())
|
||||
print(f"Baseline written: {total} grandfathered violation(s) across "
|
||||
f"{len(baseline)} rule(s) -> {os.path.relpath(BASELINE_PATH)}")
|
||||
for rule in RULES:
|
||||
if baseline.get(rule):
|
||||
print(f" {rule:26s} {len(baseline[rule])}")
|
||||
return 0
|
||||
|
||||
baseline = load_baseline()
|
||||
|
||||
new_failures: list[tuple[str, str, str]] = []
|
||||
grandfathered = 0
|
||||
for slug, rule_map in sorted(violations.items()):
|
||||
for rule, message in sorted(rule_map.items()):
|
||||
if slug in baseline.get(rule, []):
|
||||
grandfathered += 1
|
||||
else:
|
||||
new_failures.append((slug, rule, message))
|
||||
|
||||
# Only meaningful over the whole tree: linting one skill says nothing about
|
||||
# whether the other 816 baselined violations still stand.
|
||||
stale = [] if single_skill else [
|
||||
(rule, slug) for rule, slugs in baseline.items() for slug in slugs
|
||||
if rule not in violations.get(slug, {})
|
||||
]
|
||||
|
||||
if args.stats:
|
||||
print("Violations by rule (grandfathered + new):")
|
||||
for rule in RULES:
|
||||
count = sum(1 for v in violations.values() if rule in v)
|
||||
remaining = len(baseline.get(rule, []))
|
||||
print(f" {rule:26s} {count:4d} baselined {remaining:4d}")
|
||||
print()
|
||||
|
||||
for slug, rule, message in new_failures:
|
||||
print(f"{RED}FAIL{RESET} {slug}: {YELLOW}{rule}{RESET} - {message}")
|
||||
|
||||
if stale:
|
||||
print(f"\n{GREEN}{len(stale)} baselined violation(s) now pass.{RESET} "
|
||||
f"Shrink the baseline: python tools/lint-descriptions.py --update-baseline")
|
||||
for rule, slug in stale[:10]:
|
||||
print(f" {DIM}fixed{RESET} {slug}: {rule}")
|
||||
|
||||
print(f"\n{'=' * 60}")
|
||||
checked = 1 if single_skill else sum(1 for _ in iter_skill_dirs(args.skills_dir))
|
||||
print(f"Skills: {checked} "
|
||||
f"{RED}New failures: {len(new_failures)}{RESET} "
|
||||
f"{YELLOW}Grandfathered: {grandfathered}{RESET}")
|
||||
|
||||
return 1 if new_failures else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Single source of truth for reading SKILL.md YAML frontmatter.
|
||||
|
||||
Every tool in this repository MUST read frontmatter through this module.
|
||||
|
||||
Why this exists
|
||||
---------------
|
||||
This repo previously carried three independent hand-rolled "YAML-ish" parsers
|
||||
(the index generator, validate-skill.py, validate-agentskills.py). Each handled
|
||||
a different subset of YAML scalar styles, and all of them silently truncated
|
||||
multi-line descriptions to their first line.
|
||||
|
||||
A census of the 817 skills shows why that was fatal:
|
||||
|
||||
block scalar (description: >-) 43
|
||||
single-quoted multiline 278
|
||||
plain unquoted multiline 496
|
||||
single-line 0
|
||||
|
||||
Only the 43 block-scalar files parsed correctly; 774 (94.7%) used a style the
|
||||
hand-rolled parsers mishandled, and 604 descriptions shipped truncated in
|
||||
index.json with no error and no warning.
|
||||
|
||||
PyYAML handles every scalar style, quoting form and escape correctly. Do not
|
||||
reintroduce a regex-based frontmatter parser -- CI greps for that.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
from typing import Dict, Iterator, Tuple
|
||||
|
||||
import yaml
|
||||
|
||||
# Frontmatter is the block between the opening '---' and the next '---' that
|
||||
# sits alone on its own line. Tolerates CRLF and a leading UTF-8 BOM.
|
||||
_FRONTMATTER_RE = re.compile(r"\A?---[ \t]*\r?\n(.*?)\r?\n---[ \t]*(?:\r?\n|\Z)", re.DOTALL)
|
||||
|
||||
BACKUP_SUFFIX = ".bak"
|
||||
|
||||
|
||||
class FrontmatterError(ValueError):
|
||||
"""Raised when a SKILL.md has missing or unparseable frontmatter."""
|
||||
|
||||
|
||||
def extract_block(text: str) -> str:
|
||||
"""Return the raw YAML frontmatter block from a SKILL.md's text."""
|
||||
match = _FRONTMATTER_RE.match(text)
|
||||
if not match:
|
||||
raise FrontmatterError("no YAML frontmatter block (file must start with '---')")
|
||||
return match.group(1)
|
||||
|
||||
|
||||
def parse(text: str) -> dict:
|
||||
"""Parse a SKILL.md's full text into a frontmatter dict."""
|
||||
try:
|
||||
data = yaml.safe_load(extract_block(text))
|
||||
except yaml.YAMLError as exc:
|
||||
raise FrontmatterError(f"invalid YAML in frontmatter: {exc}") from exc
|
||||
|
||||
if data is None:
|
||||
return {}
|
||||
if not isinstance(data, dict):
|
||||
raise FrontmatterError(f"frontmatter must be a mapping, got {type(data).__name__}")
|
||||
return data
|
||||
|
||||
|
||||
def load_frontmatter(skill_md_path: str) -> dict:
|
||||
"""Read one SKILL.md and return its frontmatter as a dict."""
|
||||
try:
|
||||
with open(skill_md_path, encoding="utf-8") as handle:
|
||||
text = handle.read()
|
||||
except UnicodeDecodeError as exc:
|
||||
raise FrontmatterError(f"not valid UTF-8: {exc}") from exc
|
||||
return parse(text)
|
||||
|
||||
|
||||
def description_of(frontmatter: dict) -> str:
|
||||
"""Return the description as a single normalized line.
|
||||
|
||||
YAML preserves the newlines of a literal ('|') scalar and folds a folded
|
||||
('>') one; collapsing whitespace here gives every style the same shape,
|
||||
which is what index.json and the linters want to compare.
|
||||
"""
|
||||
return " ".join(str(frontmatter.get("description", "")).split())
|
||||
|
||||
|
||||
def iter_skill_dirs(skills_dir: str = "skills") -> Iterator[Tuple[str, str]]:
|
||||
"""Yield (slug, skill_dir) for every real skill, in sorted order.
|
||||
|
||||
Skips '*.bak' backup directories and any directory lacking a SKILL.md.
|
||||
"""
|
||||
for slug in sorted(os.listdir(skills_dir)):
|
||||
if slug.endswith(BACKUP_SUFFIX):
|
||||
continue
|
||||
skill_dir = os.path.join(skills_dir, slug)
|
||||
if not os.path.isdir(skill_dir):
|
||||
continue
|
||||
if not os.path.isfile(os.path.join(skill_dir, "SKILL.md")):
|
||||
continue
|
||||
yield slug, skill_dir
|
||||
|
||||
|
||||
def load_all(skills_dir: str = "skills") -> Tuple[Dict[str, dict], Dict[str, str]]:
|
||||
"""Load frontmatter for every skill.
|
||||
|
||||
Returns (frontmatter_by_slug, errors_by_slug). Callers decide whether a
|
||||
parse failure is fatal; nothing is silently dropped.
|
||||
"""
|
||||
loaded: Dict[str, dict] = {}
|
||||
errors: Dict[str, str] = {}
|
||||
|
||||
for slug, skill_dir in iter_skill_dirs(skills_dir):
|
||||
try:
|
||||
loaded[slug] = load_frontmatter(os.path.join(skill_dir, "SKILL.md"))
|
||||
except FrontmatterError as exc:
|
||||
errors[slug] = str(exc)
|
||||
|
||||
return loaded, errors
|
||||
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regenerate the contributor avatar wall in README.md from the GitHub API.
|
||||
|
||||
Writes between the markers:
|
||||
|
||||
<!-- contributors:start -->
|
||||
...generated...
|
||||
<!-- contributors:end -->
|
||||
|
||||
Avatars are served from github.com/<login>.png rather than a third-party
|
||||
contributor-image service. That is deliberate: a README image is fetched on
|
||||
every page view, so an external host would be an uncontrolled dependency in the
|
||||
most-viewed file in the repository. GitHub's own avatar endpoint has neither
|
||||
problem, and GitHub proxies it through camo like any other image.
|
||||
|
||||
Usage:
|
||||
python tools/update-contributors.py # rewrite README.md
|
||||
python tools/update-contributors.py --check # exit 1 if out of date
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
REPO = os.environ.get("GITHUB_REPOSITORY", "mukul975/Anthropic-Cybersecurity-Skills")
|
||||
MAINTAINER = REPO.split("/")[0]
|
||||
README = "README.md"
|
||||
|
||||
START = "<!-- contributors:start -->"
|
||||
END = "<!-- contributors:end -->"
|
||||
|
||||
# Accounts that are bots or automation, excluded from the wall.
|
||||
EXCLUDE_SUFFIXES = ("[bot]",)
|
||||
EXCLUDE_LOGINS = {"github-actions", "dependabot", "pull"}
|
||||
|
||||
AVATAR_PX = 72
|
||||
|
||||
|
||||
def _get(path: str):
|
||||
req = urllib.request.Request(f"https://api.github.com/repos/{REPO}/{path}", headers={
|
||||
"Accept": "application/vnd.github+json",
|
||||
"User-Agent": "update-contributors",
|
||||
})
|
||||
token = os.environ.get("GITHUB_TOKEN")
|
||||
if token:
|
||||
req.add_header("Authorization", f"Bearer {token}")
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return json.load(resp)
|
||||
|
||||
|
||||
def _keep(login: str) -> bool:
|
||||
return bool(login) and not login.endswith(EXCLUDE_SUFFIXES) and login not in EXCLUDE_LOGINS
|
||||
|
||||
|
||||
def fetch_contributors() -> list[dict]:
|
||||
"""Every non-bot contributor, most contributions first.
|
||||
|
||||
Two endpoints, because one of them lies. /contributors carries the
|
||||
authoritative contribution counts but is heavily cached — a merge can take
|
||||
up to a day to show up there. /commits is live. So anyone whose commit is
|
||||
already linked to their account but has not yet surfaced in /contributors
|
||||
gets picked up from the commit list instead of waiting a day to be thanked.
|
||||
|
||||
Commits authored with an unlinkable email (a machine hostname such as
|
||||
user@HOST.localdomain) have no `author` object and are skipped by both
|
||||
paths. They never appear in GitHub's own contributor graph either, so the
|
||||
wall matches what GitHub itself shows.
|
||||
"""
|
||||
people: list[dict] = []
|
||||
page = 1
|
||||
while True:
|
||||
batch = _get(f"contributors?per_page=100&page={page}")
|
||||
if not batch:
|
||||
break
|
||||
people.extend(batch)
|
||||
if len(batch) < 100:
|
||||
break
|
||||
page += 1
|
||||
|
||||
known = {p.get("login") for p in people}
|
||||
ranked = [p for p in people if p.get("type") != "Bot" and _keep(p.get("login", ""))]
|
||||
|
||||
# Catch anyone the cached endpoint has not caught up with yet.
|
||||
recent: dict[str, int] = {}
|
||||
for page in (1, 2):
|
||||
for commit in _get(f"commits?per_page=100&page={page}") or []:
|
||||
author = commit.get("author")
|
||||
if not author or author.get("type") == "Bot":
|
||||
continue
|
||||
login = author.get("login", "")
|
||||
if login in known or not _keep(login):
|
||||
continue
|
||||
recent[login] = recent.get(login, 0) + 1
|
||||
|
||||
for login, count in sorted(recent.items(), key=lambda kv: (-kv[1], kv[0])):
|
||||
ranked.append({"login": login, "contributions": count})
|
||||
|
||||
return ranked
|
||||
|
||||
|
||||
def render(people: list[dict]) -> str:
|
||||
lines = ['<p align="center">']
|
||||
for person in people:
|
||||
login = person["login"]
|
||||
count = person.get("contributions", 0)
|
||||
plural = "" if count == 1 else "s"
|
||||
title = f"{login} — maintainer" if login == MAINTAINER else f"{login} — {count} contribution{plural}"
|
||||
lines.append(
|
||||
f'<a href="https://github.com/{login}" title="{title}">'
|
||||
f'<img src="https://github.com/{login}.png?size=100" '
|
||||
f'width="{AVATAR_PX}" height="{AVATAR_PX}" alt="@{login}"></a>'
|
||||
)
|
||||
lines.append("</p>")
|
||||
lines.append("")
|
||||
lines.append(
|
||||
f'<p align="center"><sub>{len(people)} contributors, ordered by contribution count · '
|
||||
f'see the full <a href="https://github.com/{REPO}/graphs/contributors">contributor graph</a>'
|
||||
"</sub></p>"
|
||||
)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def splice(readme: str, block: str) -> str:
|
||||
if START not in readme or END not in readme:
|
||||
raise SystemExit(
|
||||
f"ERROR: {README} is missing the {START} / {END} markers. "
|
||||
"Add them around the contributor wall."
|
||||
)
|
||||
head, rest = readme.split(START, 1)
|
||||
_, tail = rest.split(END, 1)
|
||||
return f"{head}{START}\n{block}\n{END}{tail}"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--check", action="store_true",
|
||||
help="exit 1 if README.md is out of date; do not write")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.isfile(README):
|
||||
print(f"ERROR: {README} not found. Run from the repository root.")
|
||||
return 1
|
||||
|
||||
people = fetch_contributors()
|
||||
if not people:
|
||||
print("ERROR: the API returned no contributors; refusing to blank the section.")
|
||||
return 1
|
||||
|
||||
current = open(README, encoding="utf-8").read()
|
||||
updated = splice(current, render(people))
|
||||
|
||||
if updated == current:
|
||||
print(f"OK: contributor wall is current ({len(people)} contributors)")
|
||||
return 0
|
||||
|
||||
if args.check:
|
||||
print(f"ERROR: contributor wall is out of date ({len(people)} contributors). "
|
||||
"Run: python tools/update-contributors.py")
|
||||
return 1
|
||||
|
||||
with open(README, "w", encoding="utf-8", newline="") as handle:
|
||||
handle.write(updated)
|
||||
print(f"Updated contributor wall: {len(people)} contributors")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate SKILL.md frontmatter against the strict agentskills.io standard.
|
||||
|
||||
Reports, per skill, any deviation from tools/agentskills-skill.schema.json plus
|
||||
the two constraints JSON Schema can't express (name == parent dir; no angle
|
||||
brackets in frontmatter). READ-ONLY; never edits files.
|
||||
|
||||
Usage:
|
||||
python3 tools/validate-agentskills.py # summary + report
|
||||
python3 tools/validate-agentskills.py --json # machine-readable JSON
|
||||
python3 tools/validate-agentskills.py --strict # exit 1 if any non-compliant
|
||||
"""
|
||||
import os, re, sys, json, glob
|
||||
from collections import Counter
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from skill_frontmatter import description_of, load_frontmatter, FrontmatterError
|
||||
|
||||
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
ALLOWED = {"name", "description", "license", "compatibility", "metadata", "allowed-tools"}
|
||||
NAME_RE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
|
||||
|
||||
# The agentskills.io standard forbids reserved vendor words in a skill name.
|
||||
# tools/agentskills-skill.schema.json names this script as the enforcement
|
||||
# point, but the check was never actually implemented until now.
|
||||
RESERVED_NAME_WORDS = ("anthropic", "claude")
|
||||
|
||||
|
||||
def validate(path):
|
||||
slug = os.path.basename(os.path.dirname(path))
|
||||
problems = []
|
||||
|
||||
try:
|
||||
fm = load_frontmatter(path)
|
||||
except FrontmatterError as exc:
|
||||
return slug, [str(exc)], []
|
||||
|
||||
keys = list(fm.keys())
|
||||
|
||||
if "name" not in keys:
|
||||
problems.append("missing required key: name")
|
||||
if "description" not in keys:
|
||||
problems.append("missing required key: description")
|
||||
|
||||
name = str(fm.get("name", "") or "")
|
||||
if name:
|
||||
if not NAME_RE.match(name):
|
||||
problems.append(f"name not lowercase-kebab-case: {name!r}")
|
||||
if not (1 <= len(name) <= 64):
|
||||
problems.append(f"name length {len(name)} out of 1..64")
|
||||
if name != slug:
|
||||
problems.append(f"name {name!r} != directory {slug!r}")
|
||||
for reserved in RESERVED_NAME_WORDS:
|
||||
if reserved in name.lower():
|
||||
problems.append(f"name contains reserved word {reserved!r}: {name!r}")
|
||||
|
||||
desc = description_of(fm)
|
||||
if desc:
|
||||
if not (1 <= len(desc) <= 1024):
|
||||
problems.append(f"description length {len(desc)} out of 1..1024")
|
||||
elif "description" in keys:
|
||||
problems.append("description empty")
|
||||
|
||||
# Angle brackets are an injection risk. Checking the PARSED values (rather
|
||||
# than the raw text) means YAML block-scalar indicators like `>-` are never
|
||||
# mistaken for content, so no indicator-stripping hack is needed.
|
||||
for key, value in fm.items():
|
||||
if isinstance(value, str) and ("<" in value or ">" in value):
|
||||
problems.append(f"frontmatter value for {key!r} contains angle brackets "
|
||||
"(injection risk / not allowed)")
|
||||
break
|
||||
|
||||
# Additional top-level keys are PERMITTED by the standard (name+description
|
||||
# are the only required fields). They are reported for information, not
|
||||
# counted as compliance failures.
|
||||
nonstd = [k for k in keys if k not in ALLOWED]
|
||||
return slug, problems, nonstd
|
||||
|
||||
def main():
|
||||
as_json = "--json" in sys.argv
|
||||
strict = "--strict" in sys.argv
|
||||
skills = sorted(glob.glob(os.path.join(REPO, "skills", "*", "SKILL.md")))
|
||||
results = []
|
||||
nonstd_hist = Counter()
|
||||
compliant = 0
|
||||
for p in skills:
|
||||
slug, problems, nonstd = validate(p)
|
||||
nonstd_hist.update(nonstd)
|
||||
if not problems:
|
||||
compliant += 1
|
||||
results.append({"skill": slug, "compliant": not problems, "problems": problems})
|
||||
noncompliant = [r for r in results if not r["compliant"]]
|
||||
summary = {
|
||||
"total": len(skills),
|
||||
"compliant": compliant,
|
||||
"noncompliant": len(noncompliant),
|
||||
"nonstandard_key_frequency": dict(nonstd_hist.most_common()),
|
||||
}
|
||||
if as_json:
|
||||
print(json.dumps({"summary": summary, "results": results}, indent=1))
|
||||
else:
|
||||
print(f"agentskills.io compliance: {compliant}/{len(skills)} compliant, "
|
||||
f"{len(noncompliant)} non-compliant")
|
||||
print("\nNon-standard top-level keys (count of skills carrying each):")
|
||||
for k, n in nonstd_hist.most_common():
|
||||
print(f" {k:20s} {n}")
|
||||
# distinct problem types (excluding the per-key nonstd noise)
|
||||
other = Counter()
|
||||
for r in noncompliant:
|
||||
for pr in r["problems"]:
|
||||
if not pr.startswith("non-standard top-level key:"):
|
||||
other[re.sub(r':.*$', '', pr)] += 1
|
||||
if other:
|
||||
print("\nOther (non-key) issues:")
|
||||
for k, n in other.most_common():
|
||||
print(f" {k}: {n}")
|
||||
if strict and noncompliant:
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,208 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate SKILL.md metadata for the Anthropic-Cybersecurity-Skills repository.
|
||||
|
||||
Usage:
|
||||
python tools/validate-skill.py skills/my-skill/
|
||||
python tools/validate-skill.py --all
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import glob
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from skill_frontmatter import load_frontmatter, FrontmatterError
|
||||
|
||||
# Kept in sync with the CI workflow (.github/workflows/validate-skills.yml),
|
||||
# which now delegates to this script so there is a single source of truth.
|
||||
REQUIRED_FIELDS = ["name", "description", "domain", "subdomain", "tags",
|
||||
"version", "author", "license"]
|
||||
|
||||
# Canonical subdomain → set of accepted aliases (including canonical itself).
|
||||
# When a skill uses an alias, the validator accepts it but the canonical form
|
||||
# is the first entry in each group below. New skills should use the canonical.
|
||||
_SUBDOMAIN_ALIASES = {
|
||||
# identity
|
||||
"identity-access-management": {"identity-access-management", "identity-and-access-management", "identity-security"},
|
||||
# zero-trust
|
||||
"zero-trust-architecture": {"zero-trust-architecture", "zero-trust"},
|
||||
# OT/ICS
|
||||
"ot-ics-security": {"ot-ics-security", "ot-security"},
|
||||
# SOC / security ops
|
||||
"soc-operations": {"soc-operations", "security-operations"},
|
||||
# red team
|
||||
"red-teaming": {"red-teaming", "red-team"},
|
||||
# standalone (no aliases)
|
||||
"web-application-security": {"web-application-security", "application-security"},
|
||||
"network-security": {"network-security"},
|
||||
"penetration-testing": {"penetration-testing", "offensive-security"},
|
||||
"digital-forensics": {"digital-forensics"},
|
||||
"malware-analysis": {"malware-analysis"},
|
||||
"threat-intelligence": {"threat-intelligence"},
|
||||
"cloud-security": {"cloud-security"},
|
||||
"container-security": {"container-security"},
|
||||
"cryptography": {"cryptography"},
|
||||
"vulnerability-management": {"vulnerability-management"},
|
||||
"compliance-governance": {"compliance-governance", "governance-risk-compliance"},
|
||||
"devsecops": {"devsecops"},
|
||||
"threat-hunting": {"threat-hunting"},
|
||||
"incident-response": {"incident-response"},
|
||||
"endpoint-security": {"endpoint-security"},
|
||||
"phishing-defense": {"phishing-defense", "social-engineering-defense"},
|
||||
"api-security": {"api-security"},
|
||||
"mobile-security": {"mobile-security"},
|
||||
"ransomware-defense": {"ransomware-defense"},
|
||||
"threat-detection": {"threat-detection"},
|
||||
"blockchain-security": {"blockchain-security"},
|
||||
"data-protection": {"data-protection"},
|
||||
"deception-technology": {"deception-technology"},
|
||||
"hardware-firmware-security": {"hardware-firmware-security", "firmware-analysis", "firmware-security"},
|
||||
"privacy-compliance": {"privacy-compliance"},
|
||||
"purple-team": {"purple-team"},
|
||||
"supply-chain-security": {"supply-chain-security"},
|
||||
"wireless-security": {"wireless-security"},
|
||||
"ai-security": {"ai-security"},
|
||||
}
|
||||
|
||||
# Flat set of all accepted subdomain values (canonical + aliases).
|
||||
ALLOWED_SUBDOMAINS: set = {v for group in _SUBDOMAIN_ALIASES.values() for v in group}
|
||||
|
||||
# Reverse map: alias → canonical (for warning messages).
|
||||
_ALIAS_TO_CANONICAL: dict = {}
|
||||
for canonical, aliases in _SUBDOMAIN_ALIASES.items():
|
||||
for alias in aliases:
|
||||
_ALIAS_TO_CANONICAL[alias] = canonical
|
||||
|
||||
KEBAB_RE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
|
||||
|
||||
# Minimum description length. Other repo tooling uses 50 chars; align here.
|
||||
DESCRIPTION_MIN_CHARS = 50
|
||||
|
||||
RED = "\033[91m"
|
||||
GREEN = "\033[92m"
|
||||
YELLOW = "\033[93m"
|
||||
RESET = "\033[0m"
|
||||
|
||||
|
||||
def validate_skill(skill_dir):
|
||||
"""Validate a single skill directory. Returns list of error strings."""
|
||||
errors = []
|
||||
skill_md = os.path.join(skill_dir, "SKILL.md")
|
||||
|
||||
if not os.path.isfile(skill_md):
|
||||
return [f"SKILL.md not found in {skill_dir}"]
|
||||
|
||||
try:
|
||||
fm = load_frontmatter(skill_md)
|
||||
except FrontmatterError as e:
|
||||
return [str(e)]
|
||||
except IOError as e:
|
||||
return [f"Could not read SKILL.md: {e}"]
|
||||
|
||||
# Check required fields.
|
||||
for field in REQUIRED_FIELDS:
|
||||
if field not in fm:
|
||||
errors.append(f"Missing required field: {field}")
|
||||
|
||||
# Validate name.
|
||||
name = fm.get("name", "")
|
||||
if name:
|
||||
if not KEBAB_RE.match(name):
|
||||
errors.append(
|
||||
f"Name '{name}' is not valid kebab-case (lowercase letters, digits, hyphens only)"
|
||||
)
|
||||
if len(name) > 64:
|
||||
errors.append(f"Name too long ({len(name)} chars, max 64)")
|
||||
|
||||
# Validate description.
|
||||
desc = fm.get("description", "")
|
||||
if isinstance(desc, list):
|
||||
errors.append("Description must be a string value, not a list")
|
||||
elif isinstance(desc, str):
|
||||
if len(desc) < DESCRIPTION_MIN_CHARS:
|
||||
errors.append(
|
||||
f"Description too short ({len(desc)} chars, min {DESCRIPTION_MIN_CHARS})"
|
||||
)
|
||||
# No hard upper-limit enforced; multi-line folded scalars (>-) produce
|
||||
# long strings that are valid and common in this repo.
|
||||
|
||||
# Validate domain.
|
||||
domain = fm.get("domain", "")
|
||||
if domain and domain != "cybersecurity":
|
||||
errors.append(f"Domain must be 'cybersecurity', got '{domain}'")
|
||||
|
||||
# Validate subdomain.
|
||||
subdomain = fm.get("subdomain", "")
|
||||
if subdomain:
|
||||
if subdomain not in ALLOWED_SUBDOMAINS:
|
||||
errors.append(
|
||||
f"Unknown subdomain '{subdomain}'. Allowed: {', '.join(sorted(ALLOWED_SUBDOMAINS))}"
|
||||
)
|
||||
else:
|
||||
canonical = _ALIAS_TO_CANONICAL.get(subdomain, subdomain)
|
||||
if subdomain != canonical:
|
||||
# Warn (non-blocking) — alias is accepted but canonical is preferred
|
||||
print(
|
||||
f"{YELLOW}WARN{RESET} subdomain '{subdomain}' is an alias;"
|
||||
f" canonical form is '{canonical}'"
|
||||
)
|
||||
|
||||
# Validate tags.
|
||||
tags = fm.get("tags", [])
|
||||
if isinstance(tags, str):
|
||||
tags = [tags]
|
||||
if len(tags) < 2:
|
||||
errors.append(f"Need at least 2 tags, got {len(tags)}")
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(f"Usage: {sys.argv[0]} <skill-dir> | --all")
|
||||
sys.exit(1)
|
||||
|
||||
if sys.argv[1] == "--all":
|
||||
# Skip .bak backup directories — they are stale copies without a SKILL.md.
|
||||
# glob may return OS-native separators, so normalize before checking.
|
||||
skill_dirs = sorted(
|
||||
d for d in glob.glob("skills/*/")
|
||||
if not d.rstrip("/\\").endswith(".bak")
|
||||
)
|
||||
if not skill_dirs:
|
||||
print("ERROR: No skill directories found. Run from the repository root.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
skill_dirs = [sys.argv[1].rstrip("/") + "/"]
|
||||
|
||||
total = 0
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
for skill_dir in skill_dirs:
|
||||
if not os.path.isdir(skill_dir.rstrip("/")):
|
||||
print(f"{RED}SKIP{RESET} {skill_dir} — not a directory")
|
||||
continue
|
||||
|
||||
total += 1
|
||||
errors = validate_skill(skill_dir.rstrip("/"))
|
||||
|
||||
name = os.path.basename(skill_dir.rstrip("/"))
|
||||
if errors:
|
||||
failed += 1
|
||||
print(f"{RED}FAIL{RESET} {name}")
|
||||
for e in errors:
|
||||
print(f" {YELLOW}→ {e}{RESET}")
|
||||
else:
|
||||
passed += 1
|
||||
print(f"{GREEN}PASS{RESET} {name}")
|
||||
|
||||
print(f"\n{'='*50}")
|
||||
print(f"Total: {total} {GREEN}Passed: {passed}{RESET} {RED}Failed: {failed}{RESET}")
|
||||
|
||||
sys.exit(0 if failed == 0 else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user