mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-20 14:30:59 +03:00
Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Anthropic Agent Skills Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,50 @@
|
||||
# API Reference: GraphQL Security Assessment
|
||||
|
||||
## GraphQL Introspection Query
|
||||
|
||||
```graphql
|
||||
{
|
||||
__schema {
|
||||
queryType { name }
|
||||
mutationType { name }
|
||||
types { name kind fields { name type { name kind } } }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Security Test Endpoints
|
||||
|
||||
| Test | Query | Expected Secure Response |
|
||||
|------|-------|-------------------------|
|
||||
| Introspection | `{ __schema { types { name } } }` | Error: introspection disabled |
|
||||
| Depth limit | Nested `{ users { friends { ... } } }` | Error: max depth exceeded |
|
||||
| Batch queries | `[{query: "..."}, {query: "..."}]` | Error or single-query only |
|
||||
| Aliases | `{ a1: __typename a2: __typename ... }` | Error: alias limit exceeded |
|
||||
|
||||
## Python Libraries
|
||||
|
||||
| Library | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| `requests` | >=2.28 | HTTP client for GraphQL POST requests |
|
||||
| `gql` | >=3.4 | Python GraphQL client with transport support |
|
||||
|
||||
## graphql-cop CLI
|
||||
|
||||
```bash
|
||||
pip install graphql-cop
|
||||
graphql-cop -t https://target.example.com/graphql
|
||||
```
|
||||
|
||||
## clairvoyance (Schema Enumeration)
|
||||
|
||||
```bash
|
||||
python3 -m clairvoyance -u <url> -w <wordlist> -o schema.json
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- GraphQL specification: https://spec.graphql.org/
|
||||
- InQL Burp extension: https://github.com/doyensec/inql
|
||||
- clairvoyance: https://github.com/nikitastupin/clairvoyance
|
||||
- graphql-cop: https://github.com/dolevf/graphql-cop
|
||||
- CSP Evaluator: https://csp-evaluator.withgoogle.com/
|
||||
@@ -0,0 +1,197 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for performing GraphQL security assessment.
|
||||
|
||||
Tests GraphQL endpoints for introspection leaks, authorization flaws,
|
||||
query depth/complexity DoS, and injection vulnerabilities.
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import sys
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
class GraphQLSecurityAgent:
|
||||
"""Performs authorized security assessments on GraphQL endpoints."""
|
||||
|
||||
def __init__(self, target_url, auth_token=None):
|
||||
self.target_url = target_url
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({"Content-Type": "application/json"})
|
||||
if auth_token:
|
||||
self.session.headers["Authorization"] = f"Bearer {auth_token}"
|
||||
|
||||
def _query(self, query, variables=None):
|
||||
"""Send a GraphQL query and return the response."""
|
||||
payload = {"query": query}
|
||||
if variables:
|
||||
payload["variables"] = variables
|
||||
try:
|
||||
resp = self.session.post(self.target_url, json=payload, timeout=10)
|
||||
return {"status": resp.status_code, "body": resp.json()}
|
||||
except requests.RequestException as e:
|
||||
return {"status": 0, "error": str(e)}
|
||||
|
||||
def test_introspection(self):
|
||||
"""Test if introspection is enabled in production."""
|
||||
query = """{
|
||||
__schema {
|
||||
queryType { name }
|
||||
mutationType { name }
|
||||
types { name kind }
|
||||
}
|
||||
}"""
|
||||
result = self._query(query)
|
||||
has_schema = "data" in result.get("body", {}) and "__schema" in result.get("body", {}).get("data", {})
|
||||
types = []
|
||||
if has_schema:
|
||||
types = [t["name"] for t in result["body"]["data"]["__schema"].get("types", [])
|
||||
if not t["name"].startswith("__")]
|
||||
return {
|
||||
"vulnerable": has_schema,
|
||||
"severity": "Medium",
|
||||
"finding": "Introspection enabled" if has_schema else "Introspection disabled",
|
||||
"types_exposed": len(types),
|
||||
"type_names": types[:20],
|
||||
}
|
||||
|
||||
def test_query_depth(self, max_depth=10):
|
||||
"""Test for query depth limiting."""
|
||||
nested = "{ __typename }"
|
||||
for i in range(max_depth):
|
||||
nested = f"{{ users {nested} }}"
|
||||
query = nested
|
||||
result = self._query(query)
|
||||
has_error = "errors" in result.get("body", {})
|
||||
return {
|
||||
"vulnerable": not has_error,
|
||||
"severity": "High" if not has_error else "Info",
|
||||
"depth_tested": max_depth,
|
||||
"finding": "No query depth limit" if not has_error else "Query depth limited",
|
||||
}
|
||||
|
||||
def test_batch_queries(self):
|
||||
"""Test if batch queries are accepted (rate limit bypass risk)."""
|
||||
batch = [
|
||||
{"query": "{ __typename }"},
|
||||
{"query": "{ __typename }"},
|
||||
{"query": "{ __typename }"},
|
||||
]
|
||||
try:
|
||||
resp = self.session.post(self.target_url, json=batch, timeout=10)
|
||||
body = resp.json()
|
||||
is_array = isinstance(body, list)
|
||||
return {
|
||||
"vulnerable": is_array,
|
||||
"severity": "High" if is_array else "Info",
|
||||
"finding": "Batch queries accepted" if is_array else "Batch queries rejected",
|
||||
"response_count": len(body) if is_array else 0,
|
||||
}
|
||||
except Exception as e:
|
||||
return {"vulnerable": False, "error": str(e)}
|
||||
|
||||
def test_field_suggestions(self):
|
||||
"""Test if field suggestions leak schema information."""
|
||||
query = "{ userzzzz }"
|
||||
result = self._query(query)
|
||||
errors = result.get("body", {}).get("errors", [])
|
||||
suggestions = []
|
||||
for err in errors:
|
||||
msg = err.get("message", "")
|
||||
if "did you mean" in msg.lower() or "suggest" in msg.lower():
|
||||
suggestions.append(msg)
|
||||
return {
|
||||
"vulnerable": len(suggestions) > 0,
|
||||
"severity": "Low",
|
||||
"finding": "Field suggestions enabled" if suggestions else "No field suggestions",
|
||||
"suggestions": suggestions,
|
||||
}
|
||||
|
||||
def test_unauthorized_access(self):
|
||||
"""Test queries without authentication token."""
|
||||
saved_auth = self.session.headers.pop("Authorization", None)
|
||||
queries = [
|
||||
("{ __typename }", "basic_access"),
|
||||
("{ users { id email } }", "user_listing"),
|
||||
('{ user(id: "1") { id email role } }', "user_detail"),
|
||||
]
|
||||
results = []
|
||||
for query, test_name in queries:
|
||||
result = self._query(query)
|
||||
has_data = "data" in result.get("body", {})
|
||||
has_null_data = has_data and all(
|
||||
v is None for v in result["body"]["data"].values()
|
||||
) if has_data else False
|
||||
results.append({
|
||||
"test": test_name,
|
||||
"accessible": has_data and not has_null_data,
|
||||
"status": result.get("status"),
|
||||
})
|
||||
if saved_auth:
|
||||
self.session.headers["Authorization"] = saved_auth
|
||||
accessible_count = sum(1 for r in results if r["accessible"])
|
||||
return {
|
||||
"vulnerable": accessible_count > 0,
|
||||
"severity": "High" if accessible_count > 0 else "Info",
|
||||
"finding": f"{accessible_count} queries accessible without auth",
|
||||
"details": results,
|
||||
}
|
||||
|
||||
def test_alias_overloading(self, count=50):
|
||||
"""Test for alias-based resource exhaustion."""
|
||||
aliases = " ".join(f'a{i}: __typename' for i in range(count))
|
||||
query = f"{{ {aliases} }}"
|
||||
result = self._query(query)
|
||||
has_error = "errors" in result.get("body", {})
|
||||
return {
|
||||
"vulnerable": not has_error,
|
||||
"severity": "Medium" if not has_error else "Info",
|
||||
"aliases_tested": count,
|
||||
"finding": f"Accepted {count} aliases" if not has_error else "Alias limit enforced",
|
||||
}
|
||||
|
||||
def run_full_assessment(self):
|
||||
"""Run all security tests and generate a report."""
|
||||
report = {
|
||||
"target": self.target_url,
|
||||
"findings": [],
|
||||
}
|
||||
tests = [
|
||||
("Introspection", self.test_introspection),
|
||||
("Query Depth", self.test_query_depth),
|
||||
("Batch Queries", self.test_batch_queries),
|
||||
("Field Suggestions", self.test_field_suggestions),
|
||||
("Unauthorized Access", self.test_unauthorized_access),
|
||||
("Alias Overloading", self.test_alias_overloading),
|
||||
]
|
||||
for test_name, test_fn in tests:
|
||||
result = test_fn()
|
||||
result["test_name"] = test_name
|
||||
report["findings"].append(result)
|
||||
|
||||
vulnerable_count = sum(1 for f in report["findings"] if f.get("vulnerable"))
|
||||
report["summary"] = {
|
||||
"total_tests": len(report["findings"]),
|
||||
"vulnerabilities_found": vulnerable_count,
|
||||
"critical": sum(1 for f in report["findings"] if f.get("severity") == "Critical" and f.get("vulnerable")),
|
||||
"high": sum(1 for f in report["findings"] if f.get("severity") == "High" and f.get("vulnerable")),
|
||||
"medium": sum(1 for f in report["findings"] if f.get("severity") == "Medium" and f.get("vulnerable")),
|
||||
"low": sum(1 for f in report["findings"] if f.get("severity") == "Low" and f.get("vulnerable")),
|
||||
}
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: agent.py <graphql_url> [auth_token]")
|
||||
sys.exit(1)
|
||||
|
||||
target_url = sys.argv[1]
|
||||
auth_token = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
agent = GraphQLSecurityAgent(target_url, auth_token)
|
||||
report = agent.run_full_assessment()
|
||||
print(json.dumps(report, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user