[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6AEHQPZ43S5628DEMWS2352":3,"skill-same:01M6AEHQPZ43S5628DEMWS2352":108},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":28,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":67,"commitSha":68,"skillDirectory":69,"forksCount":70,"repositoryHtmlUrl":71,"repositoryHomepageUrl":72,"ownerHtmlUrl":73,"ownerBlogUrl":74,"ownerDescription":75,"updatedAt":76,"install":77,"resources":90},"01M6AEHQPZ43S5628DEMWS2352","detecting-shadow-api-endpoints",null,"Discover and inventory shadow API endpoints that operate outside documented OpenAPI\u002FSwagger specs, using traffic analysis against API gateways (Kong, AWS API Gateway, Envoy), cloud configuration scanning, and source code repository mining for undocumented routes. Use when assessing API attack surface, auditing for forgotten test environments or deprecated API versions still running, or building an API registration governance policy.","mukul975\u002FAnthropic-Cybersecurity-Skills","mukul975","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F42860185?v=4","01M6ADEQR8T5M4EEG6C67BQCWF","Mahipal","01M6ADJPMYWG1E5GM1FWBE3128",35990,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fdetecting-shadow-api-endpoints\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"detecting shadow api endpoints","kong","api-governance","attack-surface-management","api-inventory","zombie-apis","undocumented-apis","api-discovery","shadow-apis","attack-surface","policy","api-security","repository","gateways","gateway","code","pr","governance","test","cybersecurity","2026-11-10 10:12:30",false,"---\nname: detecting-shadow-api-endpoints\ndescription: Discover and inventory shadow API endpoints that operate outside\n  documented OpenAPI\u002FSwagger specs, using traffic analysis against API gateways\n  (Kong, AWS API Gateway, Envoy), cloud configuration scanning, and source code\n  repository mining for undocumented routes. Use when assessing API attack surface,\n  auditing for forgotten test environments or deprecated API versions still\n  running, or building an API registration governance policy.\ndomain: cybersecurity\nsubdomain: api-security\ntags:\n- api-security\n- shadow-apis\n- api-discovery\n- undocumented-apis\n- zombie-apis\n- api-inventory\n- attack-surface-management\n- api-governance\nversion: '1.0'\nauthor: mahipal\nlicense: Apache-2.0\nnist_csf:\n- PR.PS-01\n- ID.RA-01\n- PR.DS-10\n- DE.CM-01\nmitre_attack:\n- T1190\n- T1133\n- T1526\n- T1213\n---\n\n# Detecting Shadow API Endpoints\n\n## Overview\n\nShadow APIs are API endpoints operating within an organization's environment that are not tracked, documented, or secured. They emerge from rapid development cycles, forgotten test environments, deprecated API versions left running, third-party integrations, or developer side projects deployed without governance. Shadow APIs bypass authentication and monitoring controls, creating hidden entry points for attackers. Studies show that up to 30% of API endpoints in large organizations are undocumented, making shadow API detection a critical component of API security posture management.\n\n\n## When to Use\n\n- When investigating security incidents that require detecting shadow api endpoints\n- When building detection rules or threat hunting queries for this domain\n- When SOC analysts need structured procedures for this analysis type\n- When validating security monitoring coverage for related attack techniques\n\n## Prerequisites\n\n- API gateway or reverse proxy with traffic logging (Kong, AWS API Gateway, Envoy)\n- Network traffic capture capability (packet broker, port mirroring)\n- Access to source code repositories and CI\u002FCD pipeline configurations\n- Cloud provider access for configuration scanning (AWS, GCP, Azure)\n- API documentation inventory (OpenAPI specs, Swagger docs)\n- Python 3.8+ for custom discovery tooling\n\n## Detection Methods\n\n### 1. Traffic Analysis and Comparison\n\nCompare live API traffic against documented OpenAPI specifications to identify undocumented endpoints:\n\n```python\n#!\u002Fusr\u002Fbin\u002Fenv python3\n\"\"\"Shadow API Endpoint Detector\n\nCompares observed API traffic patterns against documented\nOpenAPI specifications to identify undocumented (shadow) endpoints.\n\"\"\"\n\nimport json\nimport re\nimport yaml\nimport sys\nfrom collections import defaultdict\nfrom datetime import datetime\nfrom typing import Dict, List, Set, Tuple, Optional\nfrom dataclasses import dataclass, field\n\n@dataclass\nclass DiscoveredEndpoint:\n    method: str\n    path_pattern: str\n    first_seen: str\n    last_seen: str\n    request_count: int\n    source_ips: Set[str] = field(default_factory=set)\n    status_codes: Set[int] = field(default_factory=set)\n    has_auth_header: bool = False\n    documented: bool = False\n\nclass ShadowAPIDetector:\n    # Common patterns for parameterized path segments\n    PARAM_PATTERNS = [\n        (re.compile(r'\u002F\\d+'), '\u002F{id}'),\n        (re.compile(r'\u002F[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'), '\u002F{uuid}'),\n        (re.compile(r'\u002F[a-zA-Z0-9]{20,40}'), '\u002F{token}'),\n    ]\n\n    def __init__(self):\n        self.documented_endpoints: Set[Tuple[str, str]] = set()\n        self.discovered_endpoints: Dict[Tuple[str, str], DiscoveredEndpoint] = {}\n\n    def load_openapi_spec(self, spec_path: str):\n        \"\"\"Load documented endpoints from OpenAPI specification.\"\"\"\n        with open(spec_path, 'r') as f:\n            if spec_path.endswith('.json'):\n                spec = json.load(f)\n            else:\n                spec = yaml.safe_load(f)\n\n        paths = spec.get('paths', {})\n        for path, methods in paths.items():\n            # Normalize OpenAPI path parameters\n            normalized_path = re.sub(r'\\{[^}]+\\}', '{id}', path)\n            for method in methods:\n                if method.upper() in ('GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'):\n                    self.documented_endpoints.add((method.upper(), normalized_path))\n\n        print(f\"Loaded {len(self.documented_endpoints)} documented endpoints from {spec_path}\")\n\n    def normalize_path(self, path: str) -> str:\n        \"\"\"Normalize an observed path by replacing dynamic segments with placeholders.\"\"\"\n        # Remove query string\n        path = path.split('?')[0]\n\n        for pattern, replacement in self.PARAM_PATTERNS:\n            path = pattern.sub(replacement, path)\n\n        return path\n\n    def process_access_log(self, log_file: str, log_format: str = \"common\"):\n        \"\"\"Process API access logs to discover endpoints.\"\"\"\n        patterns = {\n            \"common\": re.compile(\n                r'(?P\u003Cip>[\\d.]+)\\s+\\S+\\s+\\S+\\s+\\[(?P\u003Ctime>[^\\]]+)\\]\\s+'\n                r'\"(?P\u003Cmethod>\\w+)\\s+(?P\u003Cpath>\\S+)\\s+\\S+\"\\s+(?P\u003Cstatus>\\d+)'\n            ),\n            \"json\": None  # Handle JSON logs separately\n        }\n\n        with open(log_file, 'r') as f:\n            for line in f:\n                if log_format == \"json\":\n                    try:\n                        entry = json.loads(line)\n                        method = entry.get('method', entry.get('http_method', ''))\n                        path = entry.get('path', entry.get('uri', ''))\n                        status = int(entry.get('status', entry.get('status_code', 0)))\n                        ip = entry.get('remote_addr', entry.get('client_ip', ''))\n                        timestamp = entry.get('timestamp', entry.get('@timestamp', ''))\n                        has_auth = bool(entry.get('authorization', entry.get('auth_header', '')))\n                    except json.JSONDecodeError:\n                        continue\n                else:\n                    match = patterns[log_format].match(line)\n                    if not match:\n                        continue\n                    method = match.group('method')\n                    path = match.group('path')\n                    status = int(match.group('status'))\n                    ip = match.group('ip')\n                    timestamp = match.group('time')\n                    has_auth = 'Authorization' in line\n\n                # Only process API paths\n                if not path.startswith('\u002Fapi') and not path.startswith('\u002Fv'):\n                    continue\n\n                normalized = self.normalize_path(path)\n                key = (method.upper(), normalized)\n\n                if key not in self.discovered_endpoints:\n                    self.discovered_endpoints[key] = DiscoveredEndpoint(\n                        method=method.upper(),\n                        path_pattern=normalized,\n                        first_seen=timestamp,\n                        last_seen=timestamp,\n                        request_count=0,\n                        documented=(key in self.documented_endpoints)\n                    )\n\n                endpoint = self.discovered_endpoints[key]\n                endpoint.request_count += 1\n                endpoint.last_seen = timestamp\n                endpoint.source_ips.add(ip)\n                endpoint.status_codes.add(status)\n                if has_auth:\n                    endpoint.has_auth_header = True\n\n    def identify_shadow_apis(self) -> List[DiscoveredEndpoint]:\n        \"\"\"Identify endpoints that are not in the documented specification.\"\"\"\n        shadows = []\n        for key, endpoint in self.discovered_endpoints.items():\n            if not endpoint.documented:\n                shadows.append(endpoint)\n\n        # Sort by request count descending (most active shadows first)\n        shadows.sort(key=lambda e: e.request_count, reverse=True)\n        return shadows\n\n    def classify_risk(self, endpoint: DiscoveredEndpoint) -> str:\n        \"\"\"Classify the risk level of a shadow endpoint.\"\"\"\n        risk_score = 0\n\n        # No authentication observed\n        if not endpoint.has_auth_header:\n            risk_score += 3\n\n        # High traffic volume\n        if endpoint.request_count > 1000:\n            risk_score += 2\n        elif endpoint.request_count > 100:\n            risk_score += 1\n\n        # Multiple source IPs (wider exposure)\n        if len(endpoint.source_ips) > 10:\n            risk_score += 2\n\n        # Successful responses (endpoint is functional)\n        if 200 in endpoint.status_codes or 201 in endpoint.status_codes:\n            risk_score += 1\n\n        # Write operations are higher risk\n        if endpoint.method in ('POST', 'PUT', 'DELETE', 'PATCH'):\n            risk_score += 2\n\n        # Sensitive path patterns\n        sensitive_patterns = ['admin', 'internal', 'debug', 'test', 'backup',\n                            'config', 'health', 'metrics', 'graphql', 'console']\n        for pattern in sensitive_patterns:\n            if pattern in endpoint.path_pattern.lower():\n                risk_score += 3\n                break\n\n        if risk_score >= 8:\n            return \"CRITICAL\"\n        elif risk_score >= 5:\n            return \"HIGH\"\n        elif risk_score >= 3:\n            return \"MEDIUM\"\n        return \"LOW\"\n\n    def generate_report(self) -> dict:\n        \"\"\"Generate a comprehensive shadow API discovery report.\"\"\"\n        shadows = self.identify_shadow_apis()\n        total_documented = len(self.documented_endpoints)\n        total_discovered = len(self.discovered_endpoints)\n\n        report = {\n            \"scan_date\": datetime.now().isoformat(),\n            \"summary\": {\n                \"documented_endpoints\": total_documented,\n                \"total_discovered_endpoints\": total_discovered,\n                \"shadow_endpoints\": len(shadows),\n                \"shadow_ratio\": f\"{len(shadows)\u002Fmax(total_discovered,1)*100:.1f}%\",\n            },\n            \"shadow_endpoints\": []\n        }\n\n        for endpoint in shadows:\n            risk = self.classify_risk(endpoint)\n            report[\"shadow_endpoints\"].append({\n                \"method\": endpoint.method,\n                \"path\": endpoint.path_pattern,\n                \"risk_level\": risk,\n                \"request_count\": endpoint.request_count,\n                \"unique_sources\": len(endpoint.source_ips),\n                \"authenticated\": endpoint.has_auth_header,\n                \"status_codes\": sorted(endpoint.status_codes),\n                \"first_seen\": endpoint.first_seen,\n                \"last_seen\": endpoint.last_seen,\n            })\n\n        return report\n\n\ndef main():\n    detector = ShadowAPIDetector()\n\n    # Load documented API specifications\n    spec_files = sys.argv[1:] if len(sys.argv) > 1 else [\"openapi.yaml\"]\n    for spec in spec_files:\n        if spec.endswith(('.yaml', '.yml', '.json')):\n            detector.load_openapi_spec(spec)\n\n    # Process access logs\n    detector.process_access_log(\"\u002Fvar\u002Flog\u002Fapi\u002Faccess.log\")\n\n    report = detector.generate_report()\n\n    print(f\"\\n{'='*60}\")\n    print(f\"SHADOW API DISCOVERY REPORT\")\n    print(f\"{'='*60}\")\n    print(f\"Documented: {report['summary']['documented_endpoints']}\")\n    print(f\"Discovered: {report['summary']['total_discovered_endpoints']}\")\n    print(f\"Shadow: {report['summary']['shadow_endpoints']} ({report['summary']['shadow_ratio']})\")\n    print()\n\n    for ep in report[\"shadow_endpoints\"]:\n        risk_marker = {\"CRITICAL\": \"[!!!]\", \"HIGH\": \"[!!]\", \"MEDIUM\": \"[!]\", \"LOW\": \"[.]\"}\n        print(f\"  {risk_marker.get(ep['risk_level'], '[?]')} {ep['method']} {ep['path']}\")\n        print(f\"      Risk: {ep['risk_level']} | Requests: {ep['request_count']} | Auth: {ep['authenticated']}\")\n\n    # Save full report\n    with open(\"shadow_api_report.json\", \"w\") as f:\n        json.dump(report, f, indent=2, default=str)\n    print(f\"\\nFull report saved to shadow_api_report.json\")\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### 2. Cloud Configuration Scanning\n\n```bash\n# AWS: Discover API Gateway endpoints not in documentation\naws apigateway get-rest-apis --query 'items[*].[name,id]' --output table\n\n# List all routes for each API\naws apigatewayv2 get-apis --query 'Items[*].[Name,ApiId,ProtocolType]' --output table\n\n# AWS Lambda function URLs (potential shadow APIs)\naws lambda list-function-url-configs --function-name \"*\" 2>\u002Fdev\u002Fnull\n\n# Find ALB listener rules routing to undocumented backends\naws elbv2 describe-rules --listener-arn $LISTENER_ARN \\\n  --query 'Rules[*].[Priority,Conditions[0].Values[0],Actions[0].TargetGroupArn]'\n```\n\n### 3. Source Code Repository Mining\n\n```bash\n# Search for undocumented route definitions in source code\n# Express.js routes\ngrep -rn \"app\\.\\(get\\|post\\|put\\|delete\\|patch\\)\" --include=\"*.js\" --include=\"*.ts\" src\u002F\n\n# Flask\u002FDjango routes\ngrep -rn \"@app\\.route\\|@api\\.route\\|path(\" --include=\"*.py\" src\u002F\n\n# Spring Boot endpoints\ngrep -rn \"@\\(Get\\|Post\\|Put\\|Delete\\|Patch\\)Mapping\\|@RequestMapping\" --include=\"*.java\" src\u002F\n\n# Compare found routes against OpenAPI specification\ndiff \u003C(grep -roh \"'\u002Fapi\u002F[^']*'\" src\u002F | sort -u) \\\n     \u003C(yq '.paths | keys[]' openapi.yaml | sort -u)\n```\n\n## Prevention and Governance\n\n### API Registration Gateway Policy\n\n```yaml\n# Kong plugin configuration - reject unregistered routes\nplugins:\n  - name: request-validator\n    config:\n      allowed_content_types:\n        - application\u002Fjson\n      body_schema: null\n  - name: pre-function\n    config:\n      access:\n        - |\n          -- Block requests to unregistered endpoints\n          local registered = kong.cache:get(\"registered_endpoints\")\n          local path = kong.request.get_path()\n          local method = kong.request.get_method()\n          local key = method .. \":\" .. path\n          if not registered[key] then\n            kong.log.warn(\"Shadow API access attempt: \", key)\n            return kong.response.exit(404, {error = \"Endpoint not registered\"})\n          end\n```\n\n## References\n\n- APIsec Shadow API Best Practices: https:\u002F\u002Fwww.apisec.ai\u002Fblog\u002Fsecure-your-shadow-apis-best-practices-for-api-discovery\n- Wiz Shadow API Guide: https:\u002F\u002Fwww.wiz.io\u002Facademy\u002Fapi-security\u002Fshadow-api\n- Checkmarx Shadow and Zombie APIs: https:\u002F\u002Fcheckmarx.com\u002Flearn\u002Fapi-security\u002Fshadow-zombie-apis-undocumented-api-vulnerabilities-threaten-security-posture\u002F\n- Treblle Shadow API Tools: https:\u002F\u002Ftreblle.com\u002Fblog\u002Ftop-tools-for-detecting-shadow-apis-and-how-treblle-differs\n- SecureLayer7 Shadow APIs: https:\u002F\u002Fblog.securelayer7.net\u002Fshadow-apis-explained-risks-detection-and-prevention\u002F\n","# Detecting Shadow API Endpoints\n\n## Overview\n\nShadow APIs are API endpoints operating within an organization's environment that are not tracked, documented, or secured. They emerge from rapid development cycles, forgotten test environments, deprecated API versions left running, third-party integrations, or developer side projects deployed without governance. Shadow APIs bypass authentication and monitoring controls, creating hidden entry points for attackers. Studies show that up to 30% of API endpoints in large organizations are undocumented, making shadow API detection a critical component of API security posture management.\n\n\n## When to Use\n\n- When investigating security incidents that require detecting shadow api endpoints\n- When building detection rules or threat hunting queries for this domain\n- When SOC analysts need structured procedures for this analysis type\n- When validating security monitoring coverage for related attack techniques\n\n## Prerequisites\n\n- API gateway or reverse proxy with traffic logging (Kong, AWS API Gateway, Envoy)\n- Network traffic capture capability (packet broker, port mirroring)\n- Access to source code repositories and CI\u002FCD pipeline configurations\n- Cloud provider access for configuration scanning (AWS, GCP, Azure)\n- API documentation inventory (OpenAPI specs, Swagger docs)\n- Python 3.8+ for custom discovery tooling\n\n## Detection Methods\n\n### 1. Traffic Analysis and Comparison\n\nCompare live API traffic against documented OpenAPI specifications to identify undocumented endpoints:\n\n```python\n#!\u002Fusr\u002Fbin\u002Fenv python3\n\"\"\"Shadow API Endpoint Detector\n\nCompares observed API traffic patterns against documented\nOpenAPI specifications to identify undocumented (shadow) endpoints.\n\"\"\"\n\nimport json\nimport re\nimport yaml\nimport sys\nfrom collections import defaultdict\nfrom datetime import datetime\nfrom typing import Dict, List, Set, Tuple, Optional\nfrom dataclasses import dataclass, field\n\n@dataclass\nclass DiscoveredEndpoint:\n    method: str\n    path_pattern: str\n    first_seen: str\n    last_seen: str\n    request_count: int\n    source_ips: Set[str] = field(default_factory=set)\n    status_codes: Set[int] = field(default_factory=set)\n    has_auth_header: bool = False\n    documented: bool = False\n\nclass ShadowAPIDetector:\n    # Common patterns for parameterized path segments\n    PARAM_PATTERNS = [\n        (re.compile(r'\u002F\\d+'), '\u002F{id}'),\n        (re.compile(r'\u002F[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'), '\u002F{uuid}'),\n        (re.compile(r'\u002F[a-zA-Z0-9]{20,40}'), '\u002F{token}'),\n    ]\n\n    def __init__(self):\n        self.documented_endpoints: Set[Tuple[str, str]] = set()\n        self.discovered_endpoints: Dict[Tuple[str, str], DiscoveredEndpoint] = {}\n\n    def load_openapi_spec(self, spec_path: str):\n        \"\"\"Load documented endpoints from OpenAPI specification.\"\"\"\n        with open(spec_path, 'r') as f:\n            if spec_path.endswith('.json'):\n                spec = json.load(f)\n            else:\n                spec = yaml.safe_load(f)\n\n        paths = spec.get('paths', {})\n        for path, methods in paths.items():\n            # Normalize OpenAPI path parameters\n            normalized_path = re.sub(r'\\{[^}]+\\}', '{id}', path)\n            for method in methods:\n                if method.upper() in ('GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'):\n                    self.documented_endpoints.add((method.upper(), normalized_path))\n\n        print(f\"Loaded {len(self.documented_endpoints)} documented endpoints from {spec_path}\")\n\n    def normalize_path(self, path: str) -> str:\n        \"\"\"Normalize an observed path by replacing dynamic segments with placeholders.\"\"\"\n        # Remove query string\n        path = path.split('?')[0]\n\n        for pattern, replacement in self.PARAM_PATTERNS:\n            path = pattern.sub(replacement, path)\n\n        return path\n\n    def process_access_log(self, log_file: str, log_format: str = \"common\"):\n        \"\"\"Process API access logs to discover endpoints.\"\"\"\n        patterns = {\n            \"common\": re.compile(\n                r'(?P\u003Cip>[\\d.]+)\\s+\\S+\\s+\\S+\\s+\\[(?P\u003Ctime>[^\\]]+)\\]\\s+'\n                r'\"(?P\u003Cmethod>\\w+)\\s+(?P\u003Cpath>\\S+)\\s+\\S+\"\\s+(?P\u003Cstatus>\\d+)'\n            ),\n            \"json\": None  # Handle JSON logs separately\n        }\n\n        with open(log_file, 'r') as f:\n            for line in f:\n                if log_format == \"json\":\n                    try:\n                        entry = json.loads(line)\n                        method = entry.get('method', entry.get('http_method', ''))\n                        path = entry.get('path', entry.get('uri', ''))\n                        status = int(entry.get('status', entry.get('status_code', 0)))\n                        ip = entry.get('remote_addr', entry.get('client_ip', ''))\n                        timestamp = entry.get('timestamp', entry.get('@timestamp', ''))\n                        has_auth = bool(entry.get('authorization', entry.get('auth_header', '')))\n                    except json.JSONDecodeError:\n                        continue\n                else:\n                    match = patterns[log_format].match(line)\n                    if not match:\n                        continue\n                    method = match.group('method')\n                    path = match.group('path')\n                    status = int(match.group('status'))\n                    ip = match.group('ip')\n                    timestamp = match.group('time')\n                    has_auth = 'Authorization' in line\n\n                # Only process API paths\n                if not path.startswith('\u002Fapi') and not path.startswith('\u002Fv'):\n                    continue\n\n                normalized = self.normalize_path(path)\n                key = (method.upper(), normalized)\n\n                if key not in self.discovered_endpoints:\n                    self.discovered_endpoints[key] = DiscoveredEndpoint(\n                        method=method.upper(),\n                        path_pattern=normalized,\n                        first_seen=timestamp,\n                        last_seen=timestamp,\n                        request_count=0,\n                        documented=(key in self.documented_endpoints)\n                    )\n\n                endpoint = self.discovered_endpoints[key]\n                endpoint.request_count += 1\n                endpoint.last_seen = timestamp\n                endpoint.source_ips.add(ip)\n                endpoint.status_codes.add(status)\n                if has_auth:\n                    endpoint.has_auth_header = True\n\n    def identify_shadow_apis(self) -> List[DiscoveredEndpoint]:\n        \"\"\"Identify endpoints that are not in the documented specification.\"\"\"\n        shadows = []\n        for key, endpoint in self.discovered_endpoints.items():\n            if not endpoint.documented:\n                shadows.append(endpoint)\n\n        # Sort by request count descending (most active shadows first)\n        shadows.sort(key=lambda e: e.request_count, reverse=True)\n        return shadows\n\n    def classify_risk(self, endpoint: DiscoveredEndpoint) -> str:\n        \"\"\"Classify the risk level of a shadow endpoint.\"\"\"\n        risk_score = 0\n\n        # No authentication observed\n        if not endpoint.has_auth_header:\n            risk_score += 3\n\n        # High traffic volume\n        if endpoint.request_count > 1000:\n            risk_score += 2\n        elif endpoint.request_count > 100:\n            risk_score += 1\n\n        # Multiple source IPs (wider exposure)\n        if len(endpoint.source_ips) > 10:\n            risk_score += 2\n\n        # Successful responses (endpoint is functional)\n        if 200 in endpoint.status_codes or 201 in endpoint.status_codes:\n            risk_score += 1\n\n        # Write operations are higher risk\n        if endpoint.method in ('POST', 'PUT', 'DELETE', 'PATCH'):\n            risk_score += 2\n\n        # Sensitive path patterns\n        sensitive_patterns = ['admin', 'internal', 'debug', 'test', 'backup',\n                            'config', 'health', 'metrics', 'graphql', 'console']\n        for pattern in sensitive_patterns:\n            if pattern in endpoint.path_pattern.lower():\n                risk_score += 3\n                break\n\n        if risk_score >= 8:\n            return \"CRITICAL\"\n        elif risk_score >= 5:\n            return \"HIGH\"\n        elif risk_score >= 3:\n            return \"MEDIUM\"\n        return \"LOW\"\n\n    def generate_report(self) -> dict:\n        \"\"\"Generate a comprehensive shadow API discovery report.\"\"\"\n        shadows = self.identify_shadow_apis()\n        total_documented = len(self.documented_endpoints)\n        total_discovered = len(self.discovered_endpoints)\n\n        report = {\n            \"scan_date\": datetime.now().isoformat(),\n            \"summary\": {\n                \"documented_endpoints\": total_documented,\n                \"total_discovered_endpoints\": total_discovered,\n                \"shadow_endpoints\": len(shadows),\n                \"shadow_ratio\": f\"{len(shadows)\u002Fmax(total_discovered,1)*100:.1f}%\",\n            },\n            \"shadow_endpoints\": []\n        }\n\n        for endpoint in shadows:\n            risk = self.classify_risk(endpoint)\n            report[\"shadow_endpoints\"].append({\n                \"method\": endpoint.method,\n                \"path\": endpoint.path_pattern,\n                \"risk_level\": risk,\n                \"request_count\": endpoint.request_count,\n                \"unique_sources\": len(endpoint.source_ips),\n                \"authenticated\": endpoint.has_auth_header,\n                \"status_codes\": sorted(endpoint.status_codes),\n                \"first_seen\": endpoint.first_seen,\n                \"last_seen\": endpoint.last_seen,\n            })\n\n        return report\n\n\ndef main():\n    detector = ShadowAPIDetector()\n\n    # Load documented API specifications\n    spec_files = sys.argv[1:] if len(sys.argv) > 1 else [\"openapi.yaml\"]\n    for spec in spec_files:\n        if spec.endswith(('.yaml', '.yml', '.json')):\n            detector.load_openapi_spec(spec)\n\n    # Process access logs\n    detector.process_access_log(\"\u002Fvar\u002Flog\u002Fapi\u002Faccess.log\")\n\n    report = detector.generate_report()\n\n    print(f\"\\n{'='*60}\")\n    print(f\"SHADOW API DISCOVERY REPORT\")\n    print(f\"{'='*60}\")\n    print(f\"Documented: {report['summary']['documented_endpoints']}\")\n    print(f\"Discovered: {report['summary']['total_discovered_endpoints']}\")\n    print(f\"Shadow: {report['summary']['shadow_endpoints']} ({report['summary']['shadow_ratio']})\")\n    print()\n\n    for ep in report[\"shadow_endpoints\"]:\n        risk_marker = {\"CRITICAL\": \"[!!!]\", \"HIGH\": \"[!!]\", \"MEDIUM\": \"[!]\", \"LOW\": \"[.]\"}\n        print(f\"  {risk_marker.get(ep['risk_level'], '[?]')} {ep['method']} {ep['path']}\")\n        print(f\"      Risk: {ep['risk_level']} | Requests: {ep['request_count']} | Auth: {ep['authenticated']}\")\n\n    # Save full report\n    with open(\"shadow_api_report.json\", \"w\") as f:\n        json.dump(report, f, indent=2, default=str)\n    print(f\"\\nFull report saved to shadow_api_report.json\")\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### 2. Cloud Configuration Scanning\n\n```bash\n# AWS: Discover API Gateway endpoints not in documentation\naws apigateway get-rest-apis --query 'items[*].[name,id]' --output table\n\n# List all routes for each API\naws apigatewayv2 get-apis --query 'Items[*].[Name,ApiId,ProtocolType]' --output table\n\n# AWS Lambda function URLs (potential shadow APIs)\naws lambda list-function-url-configs --function-name \"*\" 2>\u002Fdev\u002Fnull\n\n# Find ALB listener rules routing to undocumented backends\naws elbv2 describe-rules --listener-arn $LISTENER_ARN \\\n  --query 'Rules[*].[Priority,Conditions[0].Values[0],Actions[0].TargetGroupArn]'\n```\n\n### 3. Source Code Repository Mining\n\n```bash\n# Search for undocumented route definitions in source code\n# Express.js routes\ngrep -rn \"app\\.\\(get\\|post\\|put\\|delete\\|patch\\)\" --include=\"*.js\" --include=\"*.ts\" src\u002F\n\n# Flask\u002FDjango routes\ngrep -rn \"@app\\.route\\|@api\\.route\\|path(\" --include=\"*.py\" src\u002F\n\n# Spring Boot endpoints\ngrep -rn \"@\\(Get\\|Post\\|Put\\|Delete\\|Patch\\)Mapping\\|@RequestMapping\" --include=\"*.java\" src\u002F\n\n# Compare found routes against OpenAPI specification\ndiff \u003C(grep -roh \"'\u002Fapi\u002F[^']*'\" src\u002F | sort -u) \\\n     \u003C(yq '.paths | keys[]' openapi.yaml | sort -u)\n```\n\n## Prevention and Governance\n\n### API Registration Gateway Policy\n\n```yaml\n# Kong plugin configuration - reject unregistered routes\nplugins:\n  - name: request-validator\n    config:\n      allowed_content_types:\n        - application\u002Fjson\n      body_schema: null\n  - name: pre-function\n    config:\n      access:\n        - |\n          -- Block requests to unregistered endpoints\n          local registered = kong.cache:get(\"registered_endpoints\")\n          local path = kong.request.get_path()\n          local method = kong.request.get_method()\n          local key = method .. \":\" .. path\n          if not registered[key] then\n            kong.log.warn(\"Shadow API access attempt: \", key)\n            return kong.response.exit(404, {error = \"Endpoint not registered\"})\n          end\n```\n\n## References\n\n- APIsec Shadow API Best Practices: https:\u002F\u002Fwww.apisec.ai\u002Fblog\u002Fsecure-your-shadow-apis-best-practices-for-api-discovery\n- Wiz Shadow API Guide: https:\u002F\u002Fwww.wiz.io\u002Facademy\u002Fapi-security\u002Fshadow-api\n- Checkmarx Shadow and Zombie APIs: https:\u002F\u002Fcheckmarx.com\u002Flearn\u002Fapi-security\u002Fshadow-zombie-apis-undocumented-api-vulnerabilities-threaten-security-posture\u002F\n- Treblle Shadow API Tools: https:\u002F\u002Ftreblle.com\u002Fblog\u002Ftop-tools-for-detecting-shadow-apis-and-how-treblle-differs\n- SecureLayer7 Shadow APIs: https:\u002F\u002Fblog.securelayer7.net\u002Fshadow-apis-explained-risks-detection-and-prevention\u002F\n",{"name":5,"tags":54,"author":55,"domain":48,"license":15,"version":56,"nist_csf":57,"subdomain":40,"description":7,"mitre_attack":62},[40,37,36,35,34,33,32,31],"mahipal","1.0",[58,59,60,61],"PR.PS-01","ID.RA-01","PR.DS-10","DE.CM-01",[63,64,65,66],"T1190","T1133","T1526","T1213","main","54a798831d2266a3ca61ce68a7acb80b81160d57","skills\u002Fdetecting-shadow-api-endpoints",4395,"https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills","https:\u002F\u002Fmahipal.engineer\u002FAnthropic-Cybersecurity-Skills\u002F","https:\u002F\u002Fgithub.com\u002Fmukul975","Mahipal.engineer","🔐 Cybersecurity | 💻 Dev | 📷 Street Photographer\r\n🎓 MSc | 📚 Research & AI Security","2026-08-31 12:32:44",{"source":8,"skillName":5,"commands":78,"prompt":88,"zipAvailable":89},[79,82,85],{"tool":80,"command":81},"npx","npx skills add mukul975\u002FAnthropic-Cybersecurity-Skills --skill detecting-shadow-api-endpoints",{"tool":83,"command":84},"bunx","bunx skills add mukul975\u002FAnthropic-Cybersecurity-Skills --skill detecting-shadow-api-endpoints",{"tool":86,"command":87},"pnpm","pnpm dlx skills add mukul975\u002FAnthropic-Cybersecurity-Skills --skill detecting-shadow-api-endpoints","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fdetecting-shadow-api-endpoints\u002FSKILL.md",true,[91,96,102],{"path":92,"resourceType":92,"resourceTypeDesc":93,"sizeBytes":94,"sourceHtmlUrl":95},"LICENSE","许可证",11255,"https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fdetecting-shadow-api-endpoints\u002FLICENSE",{"path":97,"resourceType":98,"resourceTypeDesc":99,"sizeBytes":100,"sourceHtmlUrl":101},"references\u002Fapi-reference.md","REFERENCE","参考文档",2212,"https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fdetecting-shadow-api-endpoints\u002Freferences\u002Fapi-reference.md",{"path":103,"resourceType":104,"resourceTypeDesc":105,"sizeBytes":106,"sourceHtmlUrl":107},"scripts\u002Fagent.py","SCRIPT","脚本",5723,"https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fdetecting-shadow-api-endpoints\u002Fscripts\u002Fagent.py",[109,136,159,187,213,240],{"skillNo":110,"name":111,"nameZh":6,"description":112,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":113,"categories":114,"keywords":118,"publishedAt":49},"01M6AGDM3HJXTKXX15ZZ0ZVCJ3","verifying-build-provenance-with-slsa-sigstore","Verifies artifact signatures and SLSA provenance using Sigstore's cosign (verify, verify-attestation, verify-blob-attestation) and slsa-verifier (verify-artifact), enforcing keyless OIDC builder identity and source repo against SLSA Build levels. Use in CI\u002FCD before deploying artifacts, when consuming third-party attestations, establishing a SLSA Build L3 pipeline, or confirming provenance during incident response or admission control.","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fverifying-build-provenance-with-slsa-sigstore\u002FSKILL.md",[115],{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[119,120,121,122,123,48,124,125,45,44,126,127,128,129,130,131,132,133,134,135],"sigstore","provenance","slsa","verifying build provenance with slsa sigstore","security","incident response","identity","artifacts","ci","supply-chain","pipeline","supply-chain-security","t1195","attestation","code-signing","cosign","keyless",{"skillNo":137,"name":138,"nameZh":6,"description":139,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":140,"categories":141,"keywords":143,"publishedAt":49},"01M6AGDGBPWYVZ2F11GFKJ4HZ9","validating-tpm-measured-boot-attestation","Verifies TPM 2.0 measured-boot integrity and remote attestation with tpm2-tools -- reading PCRs (tpm2_pcrread), replaying the boot event log, generating and checking signed quotes (tpm2_quote\u002Ftpm2_checkquote), and sealing secrets to a PCR policy. Use to confirm a system booted trusted firmware\u002Fkernel for Zero Trust device posture, detect boot-chain tampering via PCR drift, or build a golden-value baseline for fleet attestation.","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fvalidating-tpm-measured-boot-attestation\u002FSKILL.md",[142],{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},[144,132,145,146,123,39,48,45,147,148,149,150,151,152,153,154,155,156,157,158],"boot","TPM","validating tpm measured boot attestation","verification","ZeRO","firmware","Tools","measured-boot","PCR","integrity","secrets","remote-attestation","tpm2-tools","integrity-verification","trusted-computing",{"skillNo":160,"name":161,"nameZh":6,"description":162,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":163,"categories":164,"keywords":169,"publishedAt":49},"01M6AGDCW0Y4FXZ3362GT1941M","validating-backup-integrity-for-recovery","Validates backup integrity through cryptographic hash verification, automated restore testing, corruption detection, and recoverability checks to confirm backups are reliable for disaster recovery and ransomware response. Use before relying on backups for recovery, when building post-backup validation pipelines, auditing backup infrastructure for compliance, or checking immutable\u002Fair-gapped backups for silent corruption or tampering.","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fvalidating-backup-integrity-for-recovery\u002FSKILL.md",[165,168],{"categoryNo":166,"categoryName":167,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},[153,170,171,172,173,48,174,175,147,176,177,178,179,180,181,182,183,184,185,186],"backup","recovery","validating backup integrity for recovery","testing","infrastructure","validation","compliance","hash-verification","incident-response","ransomware","cryptocurrency","detection","t1078","t1490","immutable","disaster-recovery","restore-testing",{"skillNo":188,"name":189,"nameZh":6,"description":190,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":191,"categories":192,"keywords":194,"publishedAt":49},"01M6AGD9GBG2KZ336AE763J3WH","triaging-windows-with-kape","Runs KAPE (Kroll Artifact Parser and Extractor) to collect targeted forensic artifacts (registry hives, $MFT, event logs, prefetch, browser data) via Targets and parse them with Modules wrapping Eric Zimmerman's EZ Tools (PECmd, MFTECmd, RECmd). Use during early incident containment\u002Ftriage when full disk imaging is impractical but a defensible, parseable Windows artifact set is needed quickly, including at-scale remote collection.","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Ftriaging-windows-with-kape\u002FSKILL.md",[193],{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},[195,196,197,48,198,199,200,201,202,203,204,205,206,207,208,209,150,210,211,212],"windows","kape","triaging windows with kape","registry","browser","logs","windows-forensics","dfir","mft","mftecmd","data","prefetch","eric-zimmerman","digital-forensics","SET","collection","pecmd","artifact-collection",{"skillNo":214,"name":215,"nameZh":6,"description":216,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":217,"categories":218,"keywords":220,"publishedAt":49},"01M6AGD5PW5J4EVZM958WD74ZQ","triaging-vulnerabilities-with-ssvc-framework","Triages and prioritizes vulnerabilities with CISA's Stakeholder-Specific Vulnerability Categorization (SSVC) decision tree, weighing exploitation status (via the CISA KEV catalog and FIRST EPSS API), technical impact, automatability, and mission prevalence to output Track\u002FTrack*\u002FAttend\u002FAct decisions. Use when prioritizing vulnerability scan results (OpenVAS, Nessus, Qualys) for remediation planning beyond raw CVSS scores.","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Ftriaging-vulnerabilities-with-ssvc-framework\u002FSKILL.md",[219],{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},[221,222,223,48,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239],"vulnerabilities","framework","triaging vulnerabilities with ssvc framework","status","planning","CISA","vulnerability-management","risk-management","nessus","qualys","openvas","cvss","remediation","risk","vulnerability","api","prioritization","ssvc","decision-tree",{"skillNo":241,"name":242,"nameZh":6,"description":243,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":244,"categories":245,"keywords":247,"publishedAt":49},"01M6AGD24TTQQM4CXG69E3B9TJ","triaging-security-incident","Performs initial triage of security incidents using the NIST SP 800-61r3 and SANS PICERL frameworks, classifying incident type, assigning priority by business impact, and routing to the appropriate response team. Use when a SIEM\u002FEDR alert needs human classification, concurrent alerts must be prioritized, or a user report or threat-intel IOC match requires initial incident categorization.","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\u002Fblob\u002Fmain\u002Fskills\u002Ftriaging-security-incident\u002FSKILL.md",[246],{"categoryNo":116,"categoryName":117,"taxonomyType":21,"taxonomyTypeDesc":22},[123,248,48,249,250,251,252,253,254,255,256,182,257,183,258,259,260,261,262,263],"triaging security incident","siem","file","ioc","soc","NIST-800-61","NIST","severity","edr","execution","analysis","classification","severity-classification","incident-triage","SANS-PICERL","SOC-operations"]