diff --git a/src/Plugin/FitCheck/VendorDirectoryOutsideWebrootCheck.php b/src/Plugin/FitCheck/VendorDirectoryOutsideWebrootCheck.php
new file mode 100644
index 0000000..6485e72
--- /dev/null
+++ b/src/Plugin/FitCheck/VendorDirectoryOutsideWebrootCheck.php
@@ -0,0 +1,186 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\drupalfit\Plugin\FitCheck;
+
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\drupalfit\Attribute\FitCheck;
+use Drupal\drupalfit\Enum\FitWeight;
+use Drupal\drupalfit\FitCheckPluginBase;
+use Drupal\drupalfit\FitResult;
+use Drupal\drupalfit\Plugin\FitCheckGroup\SecurityGroup;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Plugin implementation of the fit_check.
+ */
+#[FitCheck(
+  id: 'vendor_directory_outside_webroot_check',
+  fitGroup: SecurityGroup::GROUP_ID,
+  label: new TranslatableMarkup('Vendor Directory Location'),
+  description: new TranslatableMarkup('Checks if Composer vendor directory is outside the webroot.'),
+  successMessage: new TranslatableMarkup('Vendor directory is outside webroot or properly protected.'),
+  failureMessage: new TranslatableMarkup('Vendor directory is inside webroot and publicly accessible.'),
+)]
+class VendorDirectoryOutsideWebrootCheck extends FitCheckPluginBase {
+
+  /**
+   * {@inheritDoc}
+   */
+  public function __construct(
+    array $configuration,
+    string $plugin_id,
+    mixed $plugin_definition,
+  ) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  }
+
+  /**
+   * Inject the required services.
+   */
+  public static function create(
+    ContainerInterface $container,
+    array $configuration,
+    $plugin_id,
+    mixed $plugin_definition,
+  ): static {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+    );
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public function execute(): FitResult {
+    $result = FitResult::create(
+      $this->getPluginId(),
+      $this->label(),
+      $this->fitGroup(),
+      FitWeight::Ok
+    );
+
+    $vendorPath = $this->getVendorPath();
+    $webRoot = \Drupal::getContainer()->getParameter('app.root');
+
+    if (!$vendorPath) {
+      $result
+        ->setWeight(FitWeight::Info)
+        ->setInfoMessage($this->t('Vendor directory not found or not accessible.'));
+      return $result;
+    }
+
+    $realVendorPath = realpath($vendorPath);
+    $realWebRoot = realpath($webRoot);
+
+    if ($realVendorPath && $realWebRoot && $this->isVendorInsideWebroot($realVendorPath, $realWebRoot)) {
+      $isProtected = $this->isVendorProtected($vendorPath);
+      
+      if (!$isProtected) {
+        $result
+          ->setWeight(FitWeight::High)
+          ->setFailureMessage($this->failureMessage())
+          ->setHelpMessage([
+            '#type' => 'inline_template',
+            '#template' => '{{ text }}<br><strong>{{ label }}</strong> {{ path }}<br><br>{{ recommendations|raw }}',
+            '#context' => [
+              'text' => $this->t('Vendor directory is accessible via web, exposing library source code and potentially sensitive information.'),
+              'label' => $this->t('Current vendor path:'),
+              'path' => $vendorPath,
+              'recommendations' => $this->getRecommendations(),
+            ],
+          ]);
+      }
+      else {
+        $result
+          ->setWeight(FitWeight::Ok)
+          ->setSuccessMessage($this->t('Vendor directory is inside webroot but appears to be protected from HTTP access.'));
+      }
+    }
+    else {
+      $result->setSuccessMessage($this->successMessage());
+    }
+
+    return $result;
+  }
+
+  /**
+   * Gets the vendor directory path.
+   */
+  private function getVendorPath(): ?string {
+    $appRoot = \Drupal::getContainer()->getParameter('app.root');
+    
+    // Check if vendor directory exists in common locations
+    $possiblePaths = [
+      $appRoot . '/vendor',
+      $appRoot . '/../vendor',
+      dirname($appRoot) . '/vendor',
+    ];
+
+    foreach ($possiblePaths as $path) {
+      if (is_dir($path) && file_exists($path . '/autoload.php')) {
+        return $path;
+      }
+    }
+
+    // Try to get vendor path from Composer autoloader
+    if (class_exists('\Composer\Autoload\ClassLoader')) {
+      $reflection = new \ReflectionClass('\Composer\Autoload\ClassLoader');
+      $vendorDir = dirname(dirname($reflection->getFileName()));
+      if (is_dir($vendorDir) && basename($vendorDir) === 'vendor') {
+        return $vendorDir;
+      }
+    }
+
+    return NULL;
+  }
+
+  /**
+   * Checks if vendor directory is inside webroot.
+   */
+  private function isVendorInsideWebroot(string $vendorPath, string $webRoot): bool {
+    return str_starts_with($vendorPath, $webRoot);
+  }
+
+  /**
+   * Checks if vendor directory is protected from HTTP access.
+   */
+  private function isVendorProtected(string $vendorPath): bool {
+    // Check for .htaccess file that denies access
+    $htaccessPath = $vendorPath . '/.htaccess';
+    if (file_exists($htaccessPath)) {
+      $content = file_get_contents($htaccessPath);
+      if ($content && (
+        strpos($content, 'Deny from all') !== FALSE ||
+        strpos($content, 'Require all denied') !== FALSE ||
+        strpos($content, 'Order deny,allow') !== FALSE
+      )) {
+        return TRUE;
+      }
+    }
+
+    // Check for index.php that might block access
+    $indexPath = $vendorPath . '/index.php';
+    if (file_exists($indexPath)) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Gets recommendations for fixing the issue.
+   */
+  private function getRecommendations(): string {
+    return $this->t('<strong>Recommended solutions:</strong><br>
+1. Move vendor directory outside webroot by updating composer.json:<br>
+&nbsp;&nbsp;<code>"config": {"vendor-dir": "../vendor"}</code><br>
+2. Or block HTTP access at server level (Apache/Nginx)<br>
+3. Or add .htaccess to vendor directory:<br>
+&nbsp;&nbsp;<code>Order deny,allow<br>&nbsp;&nbsp;Deny from all</code>')->render();
+  }
+
+}
\ No newline at end of file
