feat: add AI skills pack for Claude Code and OpenCode (#9728)

Co-authored-by: Rubén De la Torre Vico <ruben@prowler.com>
Co-authored-by: Adrián Jesús Peña Rodríguez <adrianjpr@gmail.com>
Co-authored-by: Pepe Fagoaga <pepe@prowler.com>
This commit is contained in:
Alan Buscaglia
2026-01-09 15:01:18 +01:00
committed by GitHub
co-authored by Rubén De la Torre Vico Adrián Jesús Peña Rodríguez Pepe Fagoaga
parent 42ae5b6e3e
commit 7f2fa275c6
67 changed files with 6486 additions and 808 deletions
+143
View File
@@ -0,0 +1,143 @@
---
name: prowler-provider
description: >
Creates new Prowler cloud providers or adds services to existing providers.
Trigger: When adding a new cloud provider or service to Prowler SDK.
license: Apache-2.0
metadata:
author: prowler-cloud
version: "1.0"
allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task
---
## When to Use
Use this skill when:
- Adding a new cloud provider to Prowler
- Adding a new service to an existing provider
- Understanding the provider architecture pattern
## Provider Architecture Pattern
Every provider MUST follow this structure:
```
prowler/providers/{provider}/
├── __init__.py
├── {provider}_provider.py # Main provider class
├── models.py # Provider-specific models
├── config.py # Provider configuration
├── exceptions/ # Provider-specific exceptions
├── lib/
│ ├── service/ # Base service class
│ ├── arguments/ # CLI arguments parser
│ └── mutelist/ # Mutelist functionality
└── services/
└── {service}/
├── {service}_service.py # Resource fetcher
├── {service}_client.py # Python singleton instance
└── {check_name}/ # Individual checks
├── {check_name}.py
└── {check_name}.metadata.json
```
## Provider Class Template
```python
from prowler.providers.common.provider import Provider
class {Provider}Provider(Provider):
"""Provider class for {Provider} cloud platform."""
def __init__(self, arguments):
super().__init__(arguments)
self.session = self._setup_session(arguments)
self.regions = self._get_regions()
def _setup_session(self, arguments):
"""Provider-specific authentication."""
# Implement credential handling
pass
def _get_regions(self):
"""Get available regions for provider."""
# Return list of regions
pass
```
## Service Class Template
```python
from prowler.providers.{provider}.lib.service.service import {Provider}Service
class {Service}({Provider}Service):
"""Service class for {service} resources."""
def __init__(self, provider):
super().__init__(provider)
self.{resources} = []
self._fetch_{resources}()
def _fetch_{resources}(self):
"""Fetch {resource} data from API."""
try:
response = self.client.list_{resources}()
for item in response:
self.{resources}.append(
{Resource}(
id=item["id"],
name=item["name"],
region=item.get("region"),
)
)
except Exception as e:
logger.error(f"Error fetching {resources}: {e}")
```
## Service Client Template
```python
from prowler.providers.{provider}.services.{service}.{service}_service import {Service}
{service}_client = {Service}
```
## Supported Providers
Current providers:
- AWS (Amazon Web Services)
- Azure (Microsoft Azure)
- GCP (Google Cloud Platform)
- Kubernetes
- GitHub
- M365 (Microsoft 365)
- OracleCloud (Oracle Cloud Infrastructure)
- AlibabaCloud
- MongoDB Atlas
- NHN (NHN Cloud)
- LLM (Language Model providers)
- IaC (Infrastructure as Code)
## Commands
```bash
# Run provider
poetry run python prowler-cli.py {provider}
# List services for provider
poetry run python prowler-cli.py {provider} --list-services
# List checks for provider
poetry run python prowler-cli.py {provider} --list-checks
# Run specific service
poetry run python prowler-cli.py {provider} --services {service}
# Debug mode
poetry run python prowler-cli.py {provider} --log-level DEBUG
```
## Resources
- **Templates**: See [assets/](assets/) for Provider, Service, and Client singleton templates
- **Documentation**: See [references/provider-docs.md](references/provider-docs.md) for official Prowler Developer Guide links
+56
View File
@@ -0,0 +1,56 @@
# Example: Singleton Client Pattern
# Source: prowler/providers/github/services/repository/repository_client.py
"""
Singleton Client Pattern
This pattern is CRITICAL for how Prowler checks access service data.
How it works:
1. When this module is imported, the service is instantiated ONCE
2. The service fetches all data during __init__ (eager loading)
3. All checks import this singleton and access pre-fetched data
4. No additional API calls needed during check execution
File: prowler/providers/github/services/repository/repository_client.py
"""
from prowler.providers.common.provider import Provider
from prowler.providers.github.services.repository.repository_service import Repository
# SINGLETON: Instantiated once when module is first imported
# Provider.get_global_provider() returns the provider set in __init__
repository_client = Repository(Provider.get_global_provider())
"""
Usage in checks:
from prowler.providers.github.services.repository.repository_client import (
repository_client,
)
class repository_secret_scanning_enabled(Check):
def execute(self):
findings = []
for repo in repository_client.repositories.values():
# Access pre-fetched repository data
report = CheckReportGithub(metadata=self.metadata(), resource=repo)
if repo.secret_scanning_enabled:
report.status = "PASS"
else:
report.status = "FAIL"
findings.append(report)
return findings
"""
# Another example for organization service
# File: prowler/providers/github/services/organization/organization_client.py
# from prowler.providers.common.provider import Provider
# from prowler.providers.github.services.organization.organization_service import (
# Organization,
# )
#
# organization_client = Organization(Provider.get_global_provider())
+143
View File
@@ -0,0 +1,143 @@
# Example: Provider Class Template (GitHub Provider)
# Source: prowler/providers/github/github_provider.py
from prowler.config.config import (
default_config_file_path,
get_default_mute_file_path,
load_and_validate_config_file,
)
from prowler.lib.logger import logger
from prowler.lib.mutelist.mutelist import Mutelist
from prowler.providers.common.models import Audit_Metadata, Connection
from prowler.providers.common.provider import Provider
class GithubProvider(Provider):
"""
GitHub Provider - Template for creating new providers.
Required attributes (from abstract Provider):
- _type: str - Provider identifier
- _session: Session model - Authentication credentials
- _identity: Identity model - Authenticated user info
- _audit_config: dict - Check configuration
- _mutelist: Mutelist - Finding filtering
"""
_type: str = "github"
_auth_method: str = None
_session: "GithubSession"
_identity: "GithubIdentityInfo"
_audit_config: dict
_mutelist: Mutelist
audit_metadata: Audit_Metadata
def __init__(
self,
# Authentication credentials
personal_access_token: str = "",
# Provider configuration
config_path: str = None,
config_content: dict = None,
fixer_config: dict = {},
mutelist_path: str = None,
mutelist_content: dict = None,
# Provider scoping
repositories: list = None,
organizations: list = None,
):
logger.info("Instantiating GitHub Provider...")
# Store scoping configuration
self._repositories = repositories or []
self._organizations = organizations or []
# Step 1: Setup session (authentication)
self._session = self.setup_session(personal_access_token)
self._auth_method = "Personal Access Token"
# Step 2: Setup identity (who is authenticated)
self._identity = self.setup_identity(self._session)
# Step 3: Load audit config
if config_content:
self._audit_config = config_content
else:
if not config_path:
config_path = default_config_file_path
self._audit_config = load_and_validate_config_file(self._type, config_path)
# Step 4: Load fixer config
self._fixer_config = fixer_config
# Step 5: Load mutelist
if mutelist_content:
self._mutelist = GithubMutelist(mutelist_content=mutelist_content)
else:
if not mutelist_path:
mutelist_path = get_default_mute_file_path(self.type)
self._mutelist = GithubMutelist(mutelist_path=mutelist_path)
# CRITICAL: Register as global provider
Provider.set_global_provider(self)
# Required property implementations
@property
def type(self) -> str:
return self._type
@property
def session(self) -> "GithubSession":
return self._session
@property
def identity(self) -> "GithubIdentityInfo":
return self._identity
@property
def audit_config(self) -> dict:
return self._audit_config
@property
def mutelist(self) -> Mutelist:
return self._mutelist
@staticmethod
def setup_session(personal_access_token: str) -> "GithubSession":
"""Create authenticated session from credentials."""
if not personal_access_token:
raise ValueError("Personal access token required")
return GithubSession(token=personal_access_token)
@staticmethod
def setup_identity(session: "GithubSession") -> "GithubIdentityInfo":
"""Get identity info for authenticated user."""
# Make API call to get user info
# g = Github(auth=Auth.Token(session.token))
# user = g.get_user()
return GithubIdentityInfo(
account_id="user-id",
account_name="username",
account_url="https://github.com/username",
)
def print_credentials(self):
"""Display credentials in CLI output."""
print(f"GitHub Account: {self.identity.account_name}")
print(f"Auth Method: {self._auth_method}")
@staticmethod
def test_connection(
personal_access_token: str = None,
raise_on_exception: bool = True,
) -> Connection:
"""Test if credentials can connect to the provider."""
try:
session = GithubProvider.setup_session(personal_access_token)
GithubProvider.setup_identity(session)
return Connection(is_connected=True)
except Exception as e:
if raise_on_exception:
raise
return Connection(is_connected=False, error=str(e))
+119
View File
@@ -0,0 +1,119 @@
# Example: Service Base Class and Implementation
# Source: prowler/providers/github/lib/service/service.py
# Source: prowler/providers/github/services/repository/repository_service.py
from typing import Optional
from pydantic.v1 import BaseModel
from prowler.lib.logger import logger
# ============================================================
# Base Service Class
# ============================================================
class GithubService:
"""
Base service class for all GitHub services.
Key patterns:
1. Receives provider in __init__
2. Creates API clients in __set_clients__
3. Stores audit_config and fixer_config for check access
"""
def __init__(self, service: str, provider: "GithubProvider"):
self.provider = provider
self.clients = self.__set_clients__(provider.session)
self.audit_config = provider.audit_config
self.fixer_config = provider.fixer_config
def __set_clients__(self, session: "GithubSession") -> list:
"""Create API clients based on authentication type."""
clients = []
try:
# Create client(s) based on session credentials
# For token auth: single client
# For GitHub App: multiple clients (one per installation)
pass
except Exception as error:
logger.error(f"{error.__class__.__name__}: {error}")
return clients
# ============================================================
# Service Implementation
# ============================================================
class Repository(GithubService):
"""
Repository service - fetches and stores repository data.
Key patterns:
1. Inherits from GithubService
2. Fetches all data in __init__ (eager loading)
3. Stores data in attributes for check access
4. Defines Pydantic models for data structures
"""
def __init__(self, provider: "GithubProvider"):
super().__init__(__class__.__name__, provider)
# Fetch and store data during initialization
self.repositories = self._list_repositories()
def _list_repositories(self) -> dict:
"""List repositories based on provider scoping."""
logger.info("Repository - Listing Repositories...")
repos = {}
try:
for client in self.clients:
# Get repos from specified repositories
for repo_name in self.provider.repositories:
repo = client.get_repo(repo_name)
self._process_repository(repo, repos)
# Get repos from specified organizations
for org_name in self.provider.organizations:
org = client.get_organization(org_name)
for repo in org.get_repos():
self._process_repository(repo, repos)
except Exception as error:
logger.error(f"{error.__class__.__name__}: {error}")
return repos
def _process_repository(self, repo, repos: dict):
"""Process a single repository and add to repos dict."""
repos[repo.id] = Repo(
id=repo.id,
name=repo.name,
owner=repo.owner.login,
full_name=repo.full_name,
private=repo.private,
archived=repo.archived,
)
# ============================================================
# Pydantic Models for Service Data
# ============================================================
class Repo(BaseModel):
"""Model for GitHub Repository."""
id: int
name: str
owner: str
full_name: str
private: bool
archived: bool
secret_scanning_enabled: Optional[bool] = None
dependabot_enabled: Optional[bool] = None
class Config:
# Make model hashable for use as dict key
frozen = True
@@ -0,0 +1,28 @@
# Provider Documentation
## Local Documentation
For detailed provider development patterns, see:
### Core Documentation
- `docs/developer-guide/provider.mdx` - Provider architecture and creation guide
- `docs/developer-guide/services.mdx` - Adding services to existing providers
### Provider-Specific Details
- `docs/developer-guide/aws-details.mdx` - AWS provider implementation
- `docs/developer-guide/azure-details.mdx` - Azure provider implementation
- `docs/developer-guide/gcp-details.mdx` - GCP provider implementation
- `docs/developer-guide/kubernetes-details.mdx` - Kubernetes provider implementation
- `docs/developer-guide/github-details.mdx` - GitHub provider implementation
- `docs/developer-guide/m365-details.mdx` - Microsoft 365 provider implementation
- `docs/developer-guide/alibabacloud-details.mdx` - Alibaba Cloud provider implementation
- `docs/developer-guide/llm-details.mdx` - LLM provider implementation
## Contents
The documentation covers:
- Provider types (SDK, API, Tool/Wrapper)
- Provider class structure and identity
- Service creation patterns
- Client singleton implementation
- Provider-specific authentication and API patterns