Skip to content

Update abspath sniff#3072

Merged
Crabcyborg merged 1 commit into
masterfrom
update_abspath_sniff
Apr 10, 2026
Merged

Update abspath sniff#3072
Crabcyborg merged 1 commit into
masterfrom
update_abspath_sniff

Conversation

@Crabcyborg

@Crabcyborg Crabcyborg commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

It was adding the <?php tags inside of HTML, not at the top of the file.

Summary by CodeRabbit

  • New Features
    • Extended code analysis to support files that start with HTML content.
    • Improved consistency of check injection across different file type structures.

@coderabbitai

coderabbitai Bot commented Apr 10, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

A PHP CodeSniffer sniff was refactored to detect direct file access in both HTML-starting and PHP-starting files. The logic now registers for T_INLINE_HTML tokens and branches file processing based on the first token type, with separate handling methods for each scenario.

Changes

Cohort / File(s) Summary
File Access Security Sniff
phpcs-sniffs/Formidable/Sniffs/Security/AddDirectFileAccessCheckSniff.php
Simplified injected ABSPATH check string to single-line format; expanded token registration to include T_INLINE_HTML; refactored process() to only act at stackPtr === 0; added branching in addAbspathCheck() to handle HTML and PHP files separately via new helper method addAbspathCheckToHtmlFile(), and updated addAbspathCheckToPhpFile() with chr(10) newline delimiters.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

🐰 A sniff now guards both PHP and HTML,
With checks at the start—a protective spell!
Files opening with tags or content so plain,
Now both shall be shielded from access in vain! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Update abspath sniff' is vague and generic, using non-descriptive language that doesn't clearly convey what specific changes were made to the sniff. Consider a more specific title such as 'Add direct file access check for HTML-starting files' or 'Extend abspath sniff to handle HTML files with PHP injection' to better convey the main changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch update_abspath_sniff

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@deepsource-io

deepsource-io Bot commented Apr 10, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 1fa0718...6b77eec on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
PHP Apr 10, 2026 6:55p.m. Review ↗
JavaScript Apr 10, 2026 6:55p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@phpcs-sniffs/Formidable/Sniffs/Security/AddDirectFileAccessCheckSniff.php`:
- Around line 178-187: The code computes $insertAfter via findInsertPosition()
but then overwrites the PHP open tag by calling
$phpcsFile->fixer->replaceToken($stackPtr, $content); instead, insert the guard
at the computed position so file-level docblocks stay above it: use the fixer
insertion API (e.g. $phpcsFile->fixer->addContentBefore($insertAfter + 1,
$content) or addContentAfter($insertAfter, $content)) inside
addAbspathCheckToPhpFile() instead of replaceToken($stackPtr, ...), keeping the
beginChangeset()/endChangeset() usage and the same $content variable.
- Around line 58-60: The current logic returns when $stackPtr !== 0 and later
treats a T_INLINE_HTML at position 0 as HTML; instead detect if $tokens[0] is
T_INLINE_HTML and contains only whitespace/BOM (e.g., after trimming
"\xEF\xBB\xBF" and whitespace) then skip it by finding the first T_OPEN_TAG and
set $stackPtr to that index so the file is processed as PHP; update the code
path that routes T_INLINE_HTML to the HTML fixer to only do so for
non-whitespace content, and use the existing token array ($tokens) and token
constants (T_INLINE_HTML, T_OPEN_TAG) to locate and switch the starting token
before invoking the HTML/PHP fixer logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dd1139d9-27db-40d2-ac1d-e457bb255fdd

📥 Commits

Reviewing files that changed from the base of the PR and between 1fa0718 and 6b77eec.

📒 Files selected for processing (1)
  • phpcs-sniffs/Formidable/Sniffs/Security/AddDirectFileAccessCheckSniff.php

Comment on lines +58 to 60
// Only process the first token in the file (whether T_OPEN_TAG or T_INLINE_HTML).
if ( $stackPtr !== 0 ) {
return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, get the full file to examine the logic around lines 135-165
wc -l phpcs-sniffs/Formidable/Sniffs/Security/AddDirectFileAccessCheckSniff.php

Repository: Strategy11/formidable-forms

Length of output: 148


🏁 Script executed:

# Read the relevant sections: around line 136 and the addAbspathCheck method
cat -n phpcs-sniffs/Formidable/Sniffs/Security/AddDirectFileAccessCheckSniff.php | sed -n '120,170p'

Repository: Strategy11/formidable-forms

Length of output: 2007


🏁 Script executed:

# Look for the addAbspathCheckToHtmlFile method to see how it generates output
cat -n phpcs-sniffs/Formidable/Sniffs/Security/AddDirectFileAccessCheckSniff.php | sed -n '170,220p'

Repository: Strategy11/formidable-forms

Length of output: 1974


🏁 Script executed:

# Look for the addAbspathCheckToPhpFile method to understand the PHP branch
cat -n phpcs-sniffs/Formidable/Sniffs/Security/AddDirectFileAccessCheckSniff.php | sed -n '220,268p'

Repository: Strategy11/formidable-forms

Length of output: 1824


🌐 Web query:

PHP_CodeSniffer tokenization BOM T_INLINE_HTML first token

💡 Result:

In PHP_CodeSniffer (PHPCS), when a PHP file starts with a UTF-8 Byte Order Mark (BOM, bytes EF BB BF), the tokenizer includes the BOM as part of the content of the first T_INLINE_HTML token. The first token in the file is T_INLINE_HTML, and its 'content' property begins with the BOM characters followed by any subsequent HTML or whitespace before the first PHP opening tag (?php). This behavior occurs because PHP's token_get_all (used by PHPCS tokenizer) treats initial non-PHP content, including BOM, as T_INLINE_HTML. The BOM is not tokenized separately but embedded in this first token's content. The Generic.Files.ByteOrderMark sniff detects this by registering for T_INLINE_HTML tokens and checking if the first such token's content (trimmed) starts with the UTF-8 BOM signature "\xEF\xBB\xBF". If found at position 0, it reports a violation. The sniff optimizes by checking only once and skipping the rest of the file by returning $phpcsFile->numTokens. This is confirmed by recent PHPCS source analysis (2024 PRs #360, #278) where the sniff explicitly processes T_INLINE_HTML for BOM detection at file start, and Tokenizer.php code handling T_INLINE_HTML position mapping.

Citations:


🌐 Web query:

PHP_CodeSniffer leading whitespace tokenization file start

💡 Result:

In PHP_CodeSniffer (PHPCS), leading whitespace at the start of a PHP file before the opening PHP tag (<?php) is tokenized as a separate T_INLINE_HTML token. This occurs because PHPCS uses PHP's built-in token_get_all function in its PHPCSTokenizer (the concrete implementation extending the base Tokenizer class), which tokenizes content before the first PHP open tag as T_INLINE_HTML. The T_OPEN_TAG token itself includes any whitespace immediately following the '<?php' (e.g., a newline or space), as this is how PHP's tokenizer behaves—a PHP open tag must be followed by whitespace, and that trailing whitespace is part of the T_OPEN_TAG token content. PHPCS does not split this by default but preserves PHP's tokenization behavior. Sniffs like Squiz.WhiteSpace.SuperfluousWhitespace detect "Additional whitespace found at start of file" by checking for T_INLINE_HTML tokens at position 0 with non-empty content. Generic.PHP.CharacterBeforePHPOpenTag also flags non-whitespace characters before the open tag. Recent proposals (PHPCSStandards/PHP_CodeSniffer issue #593) discuss improving consistency by potentially splitting trailing whitespace from T_OPEN_TAG into a separate T_WHITESPACE token, but as of the latest sources (squizlabs/PHP_CodeSniffer master), it follows PHP's native tokenization. To verify tokenization yourself, use PHPCS with -vvv verbosity on a file starting with whitespace: File content: <?php Tokens (excerpt): Process token 0 on line 1 [lvl:0;]: T_INLINE_HTML => \n Sources confirm PHPCS Tokenizer calls token_get_all on the raw file content, producing T_INLINE_HTML for leading whitespace, followed by T_OPEN_TAG including its trailing whitespace.

Citations:


Skip whitespace-only leading tokens when routing to the HTML fixer.

Lines 136–138 route any first T_INLINE_HTML token to the HTML fixer. However, PHP_CodeSniffer tokenizes BOM (UTF-8 byte order mark) and leading whitespace before <?php as T_INLINE_HTML. When the HTML fixer replaces this token with <?php\n{check}\n\n?>\n{content}, it places the original whitespace/BOM after the ?> closing tag, converting invisible trivia into visible output and corrupting the file.

Check if the T_INLINE_HTML token at position 0 contains only whitespace. If so, skip it and find the actual T_OPEN_TAG to process as a PHP file instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@phpcs-sniffs/Formidable/Sniffs/Security/AddDirectFileAccessCheckSniff.php`
around lines 58 - 60, The current logic returns when $stackPtr !== 0 and later
treats a T_INLINE_HTML at position 0 as HTML; instead detect if $tokens[0] is
T_INLINE_HTML and contains only whitespace/BOM (e.g., after trimming
"\xEF\xBB\xBF" and whitespace) then skip it by finding the first T_OPEN_TAG and
set $stackPtr to that index so the file is processed as PHP; update the code
path that routes T_INLINE_HTML to the HTML fixer to only do so for
non-whitespace content, and use the existing token array ($tokens) and token
constants (T_INLINE_HTML, T_OPEN_TAG) to locate and switch the starting token
before invoking the HTML/PHP fixer logic.

Comment on lines +178 to +187
private function addAbspathCheckToPhpFile( File $phpcsFile, $stackPtr ) {
$tokens = $phpcsFile->getTokens();

// Find where to insert the check.
$insertAfter = $this->findInsertPosition( $phpcsFile, $stackPtr );

$phpcsFile->fixer->beginChangeset();

// Replace the <?php tag with <?php + newline + ABSPATH check + blank line.
$phpcsFile->fixer->replaceToken( $stackPtr, "<?php\n" . $this->accessCheck . "\n\n" );
$content = '<?php' . chr( 10 ) . $this->accessCheck . chr( 10 ) . chr( 10 );
$phpcsFile->fixer->replaceToken( $stackPtr, $content );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use the computed insertion point for PHP-starting files.

Line 182 still computes $insertAfter, but Line 187 always rewrites the opening tag instead of inserting at that offset. That moves the guard above any file-level docblock, which is the opposite of what findInsertPosition() is supposed to preserve.

💡 Suggested fix
 private function addAbspathCheckToPhpFile( File $phpcsFile, $stackPtr ) {
-		$tokens = $phpcsFile->getTokens();
-
 		// Find where to insert the check.
 		$insertAfter = $this->findInsertPosition( $phpcsFile, $stackPtr );
 
 		$phpcsFile->fixer->beginChangeset();
 
-		$content = '<?php' . chr( 10 ) . $this->accessCheck . chr( 10 ) . chr( 10 );
-		$phpcsFile->fixer->replaceToken( $stackPtr, $content );
-
-		$nextNonWhitespace = $phpcsFile->findNext( T_WHITESPACE, $stackPtr + 1, null, true );
-
-		if ( false !== $nextNonWhitespace ) {
-			for ( $i = $stackPtr + 1; $i < $nextNonWhitespace; $i++ ) {
-				$phpcsFile->fixer->replaceToken( $i, '' );
-			}
-		}
+		if ( $insertAfter === $stackPtr ) {
+			$content = '<?php' . chr( 10 ) . $this->accessCheck . chr( 10 ) . chr( 10 );
+			$phpcsFile->fixer->replaceToken( $stackPtr, $content );
+
+			$nextNonWhitespace = $phpcsFile->findNext( T_WHITESPACE, $stackPtr + 1, null, true );
+			if ( false !== $nextNonWhitespace ) {
+				for ( $i = $stackPtr + 1; $i < $nextNonWhitespace; $i++ ) {
+					$phpcsFile->fixer->replaceToken( $i, '' );
+				}
+			}
+		} else {
+			$phpcsFile->fixer->addContentAfter(
+				$insertAfter,
+				chr( 10 ) . $this->accessCheck . chr( 10 ) . chr( 10 )
+			);
+		}
 
 		$phpcsFile->fixer->endChangeset();
 	}
🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 179-179: Avoid unused local variables such as '$tokens'. (undefined)

(UnusedLocalVariable)


[warning] 182-182: Avoid unused local variables such as '$insertAfter'. (undefined)

(UnusedLocalVariable)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@phpcs-sniffs/Formidable/Sniffs/Security/AddDirectFileAccessCheckSniff.php`
around lines 178 - 187, The code computes $insertAfter via findInsertPosition()
but then overwrites the PHP open tag by calling
$phpcsFile->fixer->replaceToken($stackPtr, $content); instead, insert the guard
at the computed position so file-level docblocks stay above it: use the fixer
insertion API (e.g. $phpcsFile->fixer->addContentBefore($insertAfter + 1,
$content) or addContentAfter($insertAfter, $content)) inside
addAbspathCheckToPhpFile() instead of replaceToken($stackPtr, ...), keeping the
beginChangeset()/endChangeset() usage and the same $content variable.

@Crabcyborg Crabcyborg merged commit 73a1acc into master Apr 10, 2026
48 checks passed
@Crabcyborg Crabcyborg deleted the update_abspath_sniff branch April 10, 2026 19:05
@Crabcyborg Crabcyborg restored the update_abspath_sniff branch April 10, 2026 19:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant