mcpField
B
browser-automation

browser-automation

github.com/martinholovsky開発48232 回インストール47 週間前に更新
リポジトリ

browser-automation について

CDP、WebDriver、Selenium、Playwright、Puppeteer を使ったブラウザー自動化の実装ガイドです。テスト駆動の手順に加え、ドメイン制限、認証情報の保護、監査ログ、性能改善の方法を扱います。

主な機能

  1. 01CDP のネットワーク傍受と DOM 操作
  2. 02WebDriver の要素操作と待機
  3. 03ドメイン許可リストと URL 検証
  4. 04認証情報の保護と監査ログ
  5. 05コンテキスト再利用と並列実行

活用シーン

  1. 01テスト駆動のブラウザー自動化の計画
  2. 02自動化対象を許可ドメインに制限
  3. 03複数 URL 処理の効率改善

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/martinholovsky/claude-skills-generator --skill browser-automation

インストール後、Claude Code で/browser-automationを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
advanced-patterns.md
security-examples.md
threat-model.md

詳細

1. 概要

リスクレベル:高 — Web アクセス、認証情報の取り扱い、データ抽出、ネットワークリクエスト

ブラウザー自動化のエキスパートとして、以下の分野に関する深い専門知識を備えています。

  • Chrome DevTools Protocol:Chrome/Chromium の直接制御
  • WebDriver/Selenium:クロスブラウザー自動化の標準
  • Playwright/Puppeteer:最新の自動化フレームワーク
  • セキュリティ制御:ドメイン制限、認証情報の保護
基本原則
  1. まず TDD — pytest-playwright を使用し、実装前にテストを作成します
  2. パフォーマンスを意識 — コンテキストを再利用し、並列化し、不要なリソースをブロックします
  3. セキュリティを最優先 — ドメインの許可リスト、認証情報の保護、監査ログ
  4. 信頼性の高い自動化 — タイムアウトの適用、適切な待機、エラー処理
主な専門分野
  1. CDP プロトコル:ネットワークインターセプト、DOM 操作、JavaScript 実行
  2. WebDriver API:要素の操作、ナビゲーション、待機
  3. セキュリティ:ドメインの許可リスト、認証情報の取り扱い、監査ログ
  4. パフォーマンス:リソース管理、並列実行

2. 実装ワークフロー(TDD)

ステップ 1:まず失敗するテストを書く
# tests/test_browser_automation.py
import pytest
from playwright.sync_api import Page, expect

class TestSecureBrowserAutomation:
    """Test secure browser automation with pytest-playwright."""

    def test_blocks_banking_domains(self, automation):
        """Test that banking domains are blocked."""
        with pytest.raises(SecurityError, match="URL blocked"):
            automation.navigate("https://chase.com")

    def test_allows_permitted_domains(self, automation):
        """Test navigation to allowed domains."""
        automation.navigate("https://example.com")
        assert "Example" in automation.page.title()

    def test_blocks_password_fields(self, automation):
        """Test that password field filling is blocked."""
        automation.navigate("https://example.com/form")
        with pytest.raises(SecurityError, match="password"):
            automation.fill('input[type="password"]', "secret")

    def test_rate_limiting_enforced(self, automation):
        """Test rate limiting prevents abuse."""
        for _ in range(60):
            automation.check_request()
        with pytest.raises(RateLimitError):
            automation.check_request()

@pytest.fixture
def automation():
    """Provide configured SecureBrowserAutomation instance."""
    auto = SecureBrowserAutomation(
        domain_allowlist=['example.com'],
        permission_tier='standard'
    )
    auto.start_session()
    yield auto
    auto.close()
ステップ 2:テストに合格するための最小限の実装を行う
# Implement just enough to pass tests
class SecureBrowserAutomation:
    def navigate(self, url: str):
        if not self._validate_url(url):
            raise SecurityError(f"URL blocked: {url}")
        self.page.goto(url)
ステップ 3:パターンに従ってリファクタリングする

テストに合格したら、以下を追加するためにリファクタリングします。

  • 適切なエラー処理
  • 監査ログ
  • パフォーマンスの最適化
ステップ 4:完全な検証を実行する
# Run all browser automation tests
pytest tests/test_browser_automation.py -v --headed

# Run with coverage
pytest tests/test_browser_automation.py --cov=src/automation --cov-report=term-missing

# Run security-specific tests
pytest tests/test_browser_automation.py -k "security" -v

3. パフォーマンスパターン

パターン 1:ブラウザーコンテキストの再利用
# BAD - Creates new browser for each test
def test_page_one():
    browser = playwright.chromium.launch()
    page = browser.new_page()
    page.goto("https://example.com/one")
    browser.close()

def test_page_two():
    browser = playwright.chromium.launch()  # Slow startup again
    page = browser.new_page()
    page.goto("https://example.com/two")
    browser.close()

# GOOD - Reuse browser context
@pytest.fixture(scope="session")
def browser():
    """Share browser across all tests in session."""
    pw = sync_playwright().start()
    browser = pw.chromium.launch()
    yield browser
    browser.close()
    pw.stop()

@pytest.fixture
def page(browser):
    """Create fresh context per test for isolation."""
    context = browser.new_context()
    page = context.new_page()
    yield page
    context.close()
パターン 2:並列実行
# BAD - Sequential scraping
def scrape_all(urls: list) -> list:
    results = []
    for url in urls:
        page.goto(url)
        results.append(page.content())
    return results  # Very slow for many URLs

# GOOD - Parallel with multiple contexts
def scrape_all_parallel(urls: list, browser, max_workers: int = 4) -> list:
    """Scrape URLs in parallel using multiple contexts."""
    from concurrent.futures import ThreadPoolExecutor, as_completed

    def scrape_url(url: str) -> str:
        context = browser.new_context()
        page = context.new_page()
        try:
            page.goto(url, wait_until='domcontentloaded')
            return page.content()
        finally:
            context.close()

    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = {executor.submit(scrape_url, url): url for url in urls}
        return [future.result() for future in as_completed(futures)]
パターン 3:ネットワークインターセプトによる高速化
# BAD - Load all resources
page.goto("https://example.com")  # Loads images, fonts, analytics

# GOOD - Block unnecessary resources
def setup_resource_blocking(page):
    """Block resources that slow down automation."""
    page.route("**/*", lambda route: (
        route.abort() if route.request.resource_type in [
            "image", "media", "font", "stylesheet"
        ] else route.continue_()
    ))

# Usage
setup_resource_blocking(page)
page.goto("https://example.com")  # 2-3x faster
パターン 4:分析用リクエストのブロック
# BAD - Allow all tracking requests
page.goto(url)  # Slow due to analytics loading

# GOOD - Block tracking domains
BLOCKED_DOMAINS = [
    '*google-analytics.com*',
    '*googletagmanager.com*',
    '*facebook.com/tr*',
    '*doubleclick.net*',
]

def setup_tracking_blocker(page):
    """Block tracking and analytics requests."""
    for pattern in BLOCKED_DOMAINS:
        page.route(pattern, lambda route: route.abort())

# Apply before navigation
setup_tracking_blocker(page)
page.goto(url)  # Faster, no tracking overhead
パターン 5:効率的なセレクター
# BAD - Slow selectors
page.locator("//div[@class='container']//span[contains(text(), 'Submit')]").click()
page.wait_for_selector(".dynamic-content", timeout=30000)

# GOOD - Fast, specific selectors
page.locator("[data-testid='submit-button']").click()  # Direct attribute
page.locator("#unique-id").click()  # ID is fastest

# GOOD - Use role selectors for accessibility
page.get_by_role("button", name="Submit").click()
page.get_by_label("Email").fill("[email protected]")

# GOOD - Combine selectors for specificity without XPath
page.locator("form.login >> button[type='submit']").click()

4. 主な責任

4.1 安全な自動化の原則

ブラウザーを自動化する際は、以下を実施します。

  • ドメインを許可リストに制限する
  • スクリプトに認証情報を保存しない
  • 機密性の高い URL(銀行、医療)をブロックする
  • すべてのナビゲーションと操作をログに記録する
  • すべての操作にタイムアウトを設定する
4.2 セキュリティ最優先のアプローチ

すべてのブラウザー操作で、必ず以下を行います。

  1. URL をドメインの許可リストと照合して検証する
  2. 認証情報が露出していないか確認する
  3. 機密性の高いサイトへのアクセスをブロックする
  4. 操作の詳細をログに記録する
  5. タイムアウト制限を適用する
4.3 データの取り扱い
  • ページから認証情報を抽出しない
  • ログ内の機密データをマスキングする
  • セッション終了後にブラウザーの状態を消去する
  • 分離されたプロファイルを使用する

5. 技術基盤

5.1 自動化フレームワーク

Chrome DevTools Protocol(CDP):

  • ブラウザーの直接制御
  • ネットワークインターセプト
  • パフォーマンスのプロファイリング

WebDriver/Selenium:

  • クロスブラウザー対応
  • W3C 標準

最新のフレームワーク:

  • Playwright:複数ブラウザー対応、自動待機
  • Puppeteer:Chrome 向け CDP ラッパー
5.2 セキュリティに関する考慮事項
リスク領域緩和策優先度
認証情報の窃取ドメインの許可リスト重大
フィッシングURL の検証重大
データの持ち出し出力のフィルタリング高
セッションハイジャック分離されたプロファイル高

6. 実装パターン

パターン 1:安全なブラウザーセッション
from playwright.sync_api import sync_playwright
import logging
import re
from urllib.parse import urlparse

class SecureBrowserAutomation:
    """Secure browser automation with comprehensive controls."""

    BLOCKED_DOMAINS = {
        'chase.com', 'bankofamerica.com', 'wellsfargo.com',
        'accounts.google.com', 'login.microsoft.com',
        'paypal.com', 'venmo.com', 'stripe.com',
    }

    BLOCKED_URL_PATTERNS = [
        r'/login', r'/signin', r'/auth', r'/password',
        r'/payment', r'/checkout', r'/billing',
    ]

    def __init__(self, domain_allowlist: list = None, permission_tier: str = 'standard'):
        self.domain_allowlist = domain_allowlist
        self.permission_tier = permission_tier
        self.logger = logging.getLogger('browser.security')
        self.timeout = 30000

    def start_session(self):
        """Start browser with security settings."""
        self.playwright = sync_playwright().start()
        self.browser = self.playwright.chromium.launch(
            headless=True,
            args=['--disable-extensions', '--disable-plugins', '--no-sandbox']
        )
        self.context = self.browser.new_context(ignore_https_errors=False)
        self.context.set_default_timeout(self.timeout)
        self.page = self.context.new_page()

    def navigate(self, url: str):
        """Navigate with URL validation."""
        if not self._validate_url(url):
            raise SecurityError(f"URL blocked: {url}")
        self._audit_log('navigate', url)
        self.page.goto(url, wait_until='networkidle')

    def _validate_url(self, url: str) -> bool:
        """Validate URL against security rules."""
        parsed = urlparse(url)
        domain = parsed.netloc.lower().removeprefix('www.')
        if any(domain == d or domain.endswith('.' + d) for d in self.BLOCKED_DOMAINS):
            return False
        if self.domain_allowlist:
            if not any(domain == d or domain.endswith('.' + d) for d in self.domain_allowlist):
                return False
        return not any(re.search(p, url, re.I) for p in self.BLOCKED_URL_PATTERNS)

    def close(self):
        """Clean up browser session."""
        if hasattr(self, 'context'):
            self.context.clear_cookies()
            self.context.close()
        if hasattr(self, 'browser'):
            self.browser.close()
        if hasattr(self, 'playwright'):
            self.playwright.stop()
パターン 2:レート制限
import time

class BrowserRateLimiter:
    """Rate limit browser operations."""

    def __init__(self, requests_per_minute: int = 60):
        self.requests_per_minute = requests_per_minute
        self.request_times = []

    def check_request(self):
        """Check if request is allowed."""
        cutoff = time.time() - 60
        self.request_times = [t for t in self.request_times if t > cutoff]
        if len(self.request_times) >= self.requests_per_minute:
            raise RateLimitError("Request rate limit exceeded")
        self.request_times.append(time.time())

7. セキュリティ標準

7.1 重大な脆弱性
脆弱性CWE深刻度緩和策
自動化経由の XSSCWE-79高挿入するスクリプトをサニタイズする
認証情報の収集CWE-522重大パスワードフィールドへのアクセスをブロックする
セッションハイジャックCWE-384高分離されたプロファイル、セッションの消去
フィッシングの自動化CWE-601重大ドメインの許可リスト、URL の検証
7.2 よくある間違い
# Never: Fill Password Fields
# BAD
page.fill('input[type="password"]', password)

# GOOD
if element.get_attribute('type') == 'password':
    raise SecurityError("Cannot fill password fields")

# Never: Access Banking Sites
# BAD
page.goto(user_url)

# GOOD
if not validate_url(user_url):
    raise SecurityError("URL blocked")
page.goto(user_url)

8. 実装前チェックリスト

コードを書く前に
  • PRD のセクション 8 からセキュリティ要件を読む
  • 新しい自動化機能について、失敗するテストを書く
  • 対象サイトのドメイン許可リストを定義する
  • ブロックまたはマスキングする機密要素を特定する
実装中
  • ナビゲーション前に URL 検証を実装する
  • すべての操作の監査ログを追加する
  • リクエストのインターセプトとブロックを設定する
  • すべての操作に適切なタイムアウトを設定する
  • パフォーマンス向上のため、ブラウザーコンテキストを再利用する
コミット前
  • すべてのテストに合格すること:pytest tests/test_browser_automation.py
  • セキュリティテストに合格すること:pytest -k security
  • コードやログに認証情報が含まれていないこと
  • セッションのクリーンアップを確認すること
  • レート制限を設定し、テストすること

9. まとめ

目標は、次のようなブラウザー自動化を作成することです。

  • テスト駆動:最初にテストを書き、合格するように実装する
  • 高パフォーマンス:コンテキストの再利用、並列化、リソースのブロック
  • セキュア:ドメイン制限、認証情報の保護、出力のフィルタリング
  • 監査可能:包括的なログ記録、リクエストの追跡

実装の順序:

  1. 最初に失敗するテストを書く
  2. テストに合格するための最小限のコードを実装する
  3. パフォーマンス向上のパターンを取り入れてリファクタリングする
  4. すべての検証コマンドを実行する
  5. すべて合格した場合にのみコミットする

参考資料

  • references/secure-session-full.md を参照してください — SecureBrowserAutomation クラスの完全な実装
  • references/security-examples.md を参照してください — その他のセキュリティパターン
  • references/threat-model.md を参照してください — 脅威分析の全容

コメント