Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ca Bundle Laravel Package

composer/ca-bundle

Utility to locate the system CA root bundle for TLS/SSL verification, with a bundled Mozilla CA bundle fallback. Provides helpers to validate CA files and integrate easily with cURL, PHP streams, and Guzzle.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:

    composer require composer/ca-bundle
    

    Add to composer.json under require if not using Composer directly.

  2. First use case: Use CaBundle::getSystemCaRootBundlePath() in your HTTP client initialization (e.g., Guzzle, cURL, or Laravel HTTP client). Example:

    use Composer\CaBundle\CaBundle;
    
    $caPath = CaBundle::getSystemCaRootBundlePath();
    
  3. Where to look first:

    • README.md for core methods.
    • Release notes for CA bundle updates (e.g., cacert.pem expiry dates).
    • Source code for advanced customization (e.g., overriding default paths).

Implementation Patterns

Core Workflows

1. HTTP Client Integration

  • Guzzle:
    $client = new GuzzleHttp\Client([
        GuzzleHttp\RequestOptions::VERIFY => CaBundle::getSystemCaRootBundlePath(),
    ]);
    
  • cURL:
    $ch = curl_init('https://example.com');
    $caPath = CaBundle::getSystemCaRootBundlePath();
    curl_setopt($ch, CURLOPT_CAINFO, is_dir($caPath) ? null : $caPath);
    curl_setopt($ch, CURLOPT_CAPATH, is_dir($caPath) ? $caPath : null);
    
  • Laravel HTTP Client:
    $response = Http::withOptions([
        'verify' => CaBundle::getSystemCaRootBundlePath(),
    ])->get('https://api.example.com');
    

2. Stream Contexts

Use for file_get_contents() or stream_context_create():

$context = stream_context_create([
    'ssl' => [
        'cafile' => is_file($caPath) ? $caPath : null,
        'capath' => is_dir($caPath) ? $caPath : null,
    ],
]);
file_get_contents('https://example.com', false, $context);

3. Fallback Handling

Explicitly check for system CA availability and log fallbacks:

$caPath = CaBundle::getSystemCaRootBundlePath();
if (str_starts_with($caPath, __DIR__ . '/vendor/composer/ca-bundle')) {
    Log::warning('Using bundled CA bundle (fallback)');
}

4. Validation Checks

Verify CA file integrity before use:

if (!CaBundle::validateCaFile($caPath)) {
    throw new RuntimeException('Invalid CA bundle');
}

5. Environment-Specific Overrides

Override default paths in bootstrap/app.php or a service provider:

CaBundle::reset(); // Clear cache
CaBundle::setDefaultCaPath('/custom/ca/path'); // Extend CaBundle class or use dependency injection

Integration Tips

Laravel-Specific

  • Service Provider Binding: Bind the CA path to the container for reusable HTTP clients:

    public function register()
    {
        $this->app->singleton('ca.path', function () {
            return CaBundle::getSystemCaRootBundlePath();
        });
    }
    

    Use in HTTP clients:

    $client = new GuzzleHttp\Client([
        GuzzleHttp\RequestOptions::VERIFY => $this->app['ca.path'],
    ]);
    
  • Configurable CA Path: Add to config/app.php:

    'ca_bundle' => [
        'path' => env('CA_BUNDLE_PATH', null), // Override via .env
    ],
    

    Use in a helper:

    function getCaPath(): string
    {
        return config('ca_bundle.path') ?? CaBundle::getSystemCaRootBundlePath();
    }
    

Testing

  • Mock System CA Paths: Use CaBundle::reset() and mock getSystemCaRootBundlePath() in tests:

    CaBundle::reset();
    $this->partialMock(CaBundle::class, function ($mock) {
        $mock->method('getSystemCaRootBundlePath')->willReturn('/mock/ca/path');
    });
    
  • Validate Fallback Behavior: Test with CaBundle::getBundledCaBundlePath() to ensure fallback works:

    $this->assertStringContainsString('cacert.pem', CaBundle::getBundledCaBundlePath());
    

Gotchas and Tips

Pitfalls

  1. Path Detection Quirks:

    • Windows: May return false if no system CA is found. Always check return type:
      $caPath = CaBundle::getSystemCaRootBundlePath();
      if ($caPath === false) {
          throw new RuntimeException('No CA bundle found');
      }
      
    • Docker/Alpine: Often lacks system CAs. Prefer the bundled fallback:
      $caPath = CaBundle::getBundledCaBundlePath(); // Explicit fallback
      
  2. openssl_x509_parse Safety:

    • The method CaBundle::isOpensslParseSafe() checks if PHP’s openssl_x509_parse is enabled and safe to use. If false, avoid validateCaFile():
      if (!CaBundle::isOpensslParseSafe()) {
          Log::warning('Skipping CA validation due to unsafe openssl_x509_parse');
      }
      
  3. Static Cache:

    • CaBundle caches results statically. Reset with CaBundle::reset() after environment changes (e.g., Docker builds):
      CaBundle::reset(); // Clear cache for dynamic environments
      
  4. Bundled CA Expiry:

    • The bundled cacert.pem expires ~1 year from release date (e.g., 2026-02-11 for v1.5.11). Monitor updates via releases.
  5. OpenSSL Version Mismatches:

    • If using PHP < 7.1 (unsupported), openssl_x509_parse may fail. Use CaBundle::getBundledCaBundlePath() as a fallback.

Debugging Tips

  1. Log CA Paths: Add debug logs to verify paths:

    Log::debug('System CA Path:', ['path' => CaBundle::getSystemCaRootBundlePath()]);
    Log::debug('Bundled CA Path:', ['path' => CaBundle::getBundledCaBundlePath()]);
    
  2. Validate CA File: Manually check the CA file format:

    openssl x509 -in vendor/composer/ca-bundle/cacert.pem -text -noout
    
  3. Check PHP OpenSSL: Ensure OpenSSL is enabled in php.ini:

    if (!extension_loaded('openssl')) {
        throw new RuntimeException('OpenSSL extension is required');
    }
    

Extension Points

  1. Custom CA Paths: Extend the CaBundle class to add custom logic:

    class CustomCaBundle extends \Composer\CaBundle\CaBundle
    {
        public static function getCustomCaPath(): string
        {
            return '/etc/ssl/certs/custom-ca.pem';
        }
    
        public static function getSystemCaRootBundlePath(): ?string
        {
            $path = parent::getSystemCaRootBundlePath();
            return $path ?: self::getCustomCaPath();
        }
    }
    
  2. Override Default Trust Stores: Modify the getTrustStorePaths() method (internal) by copying the class and overriding:

    protected static function getTrustStorePaths(): array
    {
        return [
            '/custom/trust/store/path',
            // ... other paths
        ];
    }
    
  3. Dynamic CA Updates: Use a cron job or Laravel scheduler to update the bundled CA periodically:

    // In a console command
    public function handle()
    {
        $caPath = CaBundle::getBundledCaBundlePath();
        if (filemtime($caPath) < strtotime('-30 days')) {
            // Trigger a CA update (e.g., via Composer or custom script)
        }
    }
    

Laravel-Specific Gotchas

  1. Forge/Docker Environments: If using Laravel Forge or Docker, ensure the container has access to host CA paths or use the bundled fallback:

    # Example Dockerfile snippet
    RUN apt-get update && apt-get install -y ca-certificates
    
  2. Homestead/Vagrant: Shared

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport
twbs/bootstrap4