diff --git a/phpcs-sniffs/Formidable/Sniffs/Security/AddDirectFileAccessCheckSniff.php b/phpcs-sniffs/Formidable/Sniffs/Security/AddDirectFileAccessCheckSniff.php
new file mode 100644
index 0000000000..d07bdf8a93
--- /dev/null
+++ b/phpcs-sniffs/Formidable/Sniffs/Security/AddDirectFileAccessCheckSniff.php
@@ -0,0 +1,230 @@
+getTokens();
+ $filename = $phpcsFile->getFilename();
+
+ // Skip files in the tests folder.
+ if ( strpos( $filename, '/tests/' ) !== false || strpos( $filename, '\\tests\\' ) !== false ) {
+ return $phpcsFile->numTokens;
+ }
+
+ // Skip rector.php.
+ if ( basename( $filename ) === 'rector.php' ) {
+ return $phpcsFile->numTokens;
+ }
+
+ // Only process the first T_OPEN_TAG in the file.
+ $firstOpenTag = $phpcsFile->findNext( T_OPEN_TAG, 0 );
+
+ if ( $stackPtr !== $firstOpenTag ) {
+ return;
+ }
+
+ // Check if the file already has the ABSPATH check.
+ if ( $this->hasAbspathCheck( $phpcsFile ) ) {
+ return $phpcsFile->numTokens;
+ }
+
+ $fix = $phpcsFile->addFixableError(
+ 'Missing direct file access check. Add ABSPATH check to prevent direct file access.',
+ $stackPtr,
+ 'MissingAbspathCheck'
+ );
+
+ if ( $fix ) {
+ $this->addAbspathCheck( $phpcsFile, $stackPtr );
+ }
+
+ // Return the end of the file to prevent processing other T_OPEN_TAG tokens.
+ return $phpcsFile->numTokens;
+ }
+
+ /**
+ * Check if the file already has the ABSPATH check.
+ *
+ * @param File $phpcsFile The file being scanned.
+ *
+ * @return bool
+ */
+ private function hasAbspathCheck( File $phpcsFile ) {
+ $tokens = $phpcsFile->getTokens();
+
+ // Look for "defined( 'ABSPATH' )" or "defined('ABSPATH')" pattern.
+ for ( $i = 0; $i < $phpcsFile->numTokens; $i++ ) {
+ if ( $tokens[ $i ]['code'] !== T_STRING || strtolower( $tokens[ $i ]['content'] ) !== 'defined' ) {
+ continue;
+ }
+
+ // Check if the next non-whitespace token is an open parenthesis.
+ $nextToken = $phpcsFile->findNext( T_WHITESPACE, $i + 1, null, true );
+
+ if ( false === $nextToken || $tokens[ $nextToken ]['code'] !== T_OPEN_PARENTHESIS ) {
+ continue;
+ }
+
+ // Check if the string inside is 'ABSPATH'.
+ $stringToken = $phpcsFile->findNext( T_WHITESPACE, $nextToken + 1, null, true );
+
+ if ( false === $stringToken ) {
+ continue;
+ }
+
+ if ( $tokens[ $stringToken ]['code'] === T_CONSTANT_ENCAPSED_STRING ) {
+ $content = trim( $tokens[ $stringToken ]['content'], "\"'" );
+
+ if ( $content === 'ABSPATH' ) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Add the ABSPATH check to the file.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the T_OPEN_TAG token.
+ *
+ * @return void
+ */
+ private function addAbspathCheck( File $phpcsFile, $stackPtr ) {
+ $tokens = $phpcsFile->getTokens();
+
+ // Find where to insert the check.
+ $insertAfter = $this->findInsertPosition( $phpcsFile, $stackPtr );
+
+ $phpcsFile->fixer->beginChangeset();
+
+ // Replace the fixer->replaceToken( $stackPtr, "accessCheck . "\n\n" );
+
+ // Remove all whitespace tokens between findNext( T_WHITESPACE, $stackPtr + 1, null, true );
+
+ if ( false !== $nextNonWhitespace ) {
+ for ( $i = $stackPtr + 1; $i < $nextNonWhitespace; $i++ ) {
+ $phpcsFile->fixer->replaceToken( $i, '' );
+ }
+ }
+
+ $phpcsFile->fixer->endChangeset();
+ }
+
+ /**
+ * Find the position to insert the ABSPATH check.
+ *
+ * Should be after the opening PHP tag and any file-level docblock.
+ *
+ * @param File $phpcsFile The file being scanned.
+ * @param int $stackPtr The position of the T_OPEN_TAG token.
+ *
+ * @return int The token position to insert after.
+ */
+ private function findInsertPosition( File $phpcsFile, $stackPtr ) {
+ $tokens = $phpcsFile->getTokens();
+
+ // Start after the opening PHP tag.
+ $current = $stackPtr;
+
+ // Skip any whitespace after the opening tag.
+ $next = $phpcsFile->findNext( T_WHITESPACE, $current + 1, null, true );
+
+ if ( false === $next ) {
+ return $current;
+ }
+
+ // Check if there's a file-level docblock.
+ if ( $tokens[ $next ]['code'] === T_DOC_COMMENT_OPEN_TAG ) {
+ // Find the end of the docblock.
+ if ( isset( $tokens[ $next ]['comment_closer'] ) ) {
+ $docblockEnd = $tokens[ $next ]['comment_closer'];
+
+ // Check if this docblock contains @package (file-level docblock).
+ $hasPackage = false;
+
+ for ( $i = $next; $i <= $docblockEnd; $i++ ) {
+ if ( $tokens[ $i ]['code'] === T_DOC_COMMENT_TAG && $tokens[ $i ]['content'] === '@package' ) {
+ $hasPackage = true;
+ break;
+ }
+ }
+
+ if ( $hasPackage ) {
+ // Insert after the docblock.
+ // Find the whitespace after the docblock closer.
+ $afterDocblock = $phpcsFile->findNext( T_WHITESPACE, $docblockEnd + 1, null, true );
+
+ if ( false !== $afterDocblock ) {
+ // Return the whitespace token before the next code.
+ $whitespaceToken = $docblockEnd + 1;
+
+ if ( $tokens[ $whitespaceToken ]['code'] === T_WHITESPACE ) {
+ return $whitespaceToken;
+ }
+ }
+
+ return $docblockEnd;
+ }
+ }
+ }
+
+ // No file-level docblock, insert after the opening PHP tag.
+ // Find the whitespace after the opening tag.
+ $whitespaceAfterTag = $stackPtr + 1;
+
+ if ( isset( $tokens[ $whitespaceAfterTag ] ) && $tokens[ $whitespaceAfterTag ]['code'] === T_WHITESPACE ) {
+ return $whitespaceAfterTag;
+ }
+
+ return $stackPtr;
+ }
+}
diff --git a/phpcs-sniffs/Formidable/ruleset.xml b/phpcs-sniffs/Formidable/ruleset.xml
index c4d97aeb47..331dd48188 100644
--- a/phpcs-sniffs/Formidable/ruleset.xml
+++ b/phpcs-sniffs/Formidable/ruleset.xml
@@ -74,4 +74,7 @@
+
+
+
diff --git a/tests/phpunit/misc/test_FrmDirectFileAccess.php b/tests/phpunit/misc/test_FrmDirectFileAccess.php
deleted file mode 100644
index cce7d91b71..0000000000
--- a/tests/phpunit/misc/test_FrmDirectFileAccess.php
+++ /dev/null
@@ -1,57 +0,0 @@
-all_php_file_paths( $path, $results );
-
- if ( substr( $path, strlen( $path ) - 4 ) === '.php' ) {
- $results[] = $path;
- }
- }
- }
-
- return $results;
- }
-
- private function check_for_abspath_check( $path ) {
- return str_contains( file_get_contents( $path ), "! defined( 'ABSPATH' )" );
- }
-
- public function test_direct_file_access() {
- $all_php_paths = $this->all_php_file_paths();
-
- foreach ( $all_php_paths as $path ) {
- $this->assertTrue( $this->check_for_abspath_check( $path ), "{$path} should be checking for direct php file access but no check could be found" );
- }
- }
-}