Skip to content

Commit 3c1d500

Browse files
Mingsong HuMingsong Hu
authored andcommitted
Add View open access sniffer
1 parent cbdf1d8 commit 3c1d500

File tree

2 files changed

+1000
-0
lines changed

2 files changed

+1000
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
<?php
2+
3+
/**
4+
* \DrupalSecurity\Sniffs\Yaml\ViewAccessSniff.
5+
*
6+
* @category PHP
7+
* @package PHP_CodeSniffer
8+
* @link http://pear.php.net/package/PHP_CodeSniffer
9+
*/
10+
namespace DrupalSecurity\Sniffs\Yaml;
11+
12+
use PHP_CodeSniffer\Files\File;
13+
use PHP_CodeSniffer\Sniffs\Sniff;
14+
use Symfony\Component\Yaml\Yaml;
15+
use Symfony\Component\Yaml\Exception\ParseException;
16+
17+
/**
18+
* Checks if there are potential security issue in views.view.*.yml
19+
* files.
20+
*
21+
*
22+
* @category Yaml
23+
* @package PHP_CodeSniffer
24+
* @link http://pear.php.net/package/PHP_CodeSniffer
25+
*/
26+
class ViewAccessSniff implements Sniff {
27+
28+
/**
29+
* Returns an array of tokens this test wants to listen for.
30+
*
31+
* @return array<int|string>
32+
*/
33+
public function register() {
34+
return [
35+
T_INLINE_HTML
36+
];
37+
}
38+
39+
// end register()
40+
41+
/**
42+
* Processes this test, when one of its tokens is encountered.
43+
*
44+
* @param \PHP_CodeSniffer\Files\File $phpcsFile
45+
* The current file being processed.
46+
* @param int $stackPtr
47+
* The position of the current token
48+
* in the stack passed in $tokens.
49+
*
50+
* @return void|int
51+
*/
52+
public function process(File $phpcsFile, $stackPtr) {
53+
$lines = $phpcsFile->getTokens();
54+
55+
$fileExtension = strtolower(substr($phpcsFile->getFilename(), -3));
56+
$fileNameStartWith = strtolower(substr(basename($phpcsFile->getFilename()), 0, 11));
57+
if ($fileExtension !== 'yml' || $fileNameStartWith !== 'views.view.') {
58+
return ($phpcsFile->numTokens + 1);
59+
}
60+
61+
$contents = file_get_contents($phpcsFile->getFilename());
62+
try {
63+
$info = Yaml::parse($contents);
64+
}
65+
catch (ParseException $e) {
66+
// If the YAML is invalid we ignore this file.
67+
return ($phpcsFile->numTokens + 1);
68+
}
69+
70+
if (!isset($info['display'])) {
71+
return ($phpcsFile->numTokens + 1);
72+
}
73+
// Start checking open access to a view.
74+
foreach ($info['display'] as $display_name => $display_properties) {
75+
if (isset($display_properties['display_options']['access']['type'])) {
76+
$access_setting = $display_properties['display_options']['access'];
77+
// Check unrestricted access.
78+
if (strtolower(trim($access_setting['type'])) === 'none') {
79+
$warning = "Open access to $display_name display found";
80+
$line_num = $this->getTheLineNumber($display_name, $lines);
81+
$phpcsFile->addWarning($warning, $line_num ?: 0, 'OpenAccess');
82+
}
83+
// Check 'View published content' permission.
84+
elseif (strtolower(trim($access_setting['type'])) === 'perm') {
85+
if (isset($access_setting['options']['perm']) && strtolower(trim($access_setting['options']['perm'])) === 'access content')
86+
{
87+
$warning = "Open access to $display_name display found";
88+
$line_num = $this->getTheLineNumber($display_name, $lines);
89+
$phpcsFile->addWarning($warning, $line_num, 'OpenAccess');
90+
}
91+
}
92+
// Check anonymous role access.
93+
elseif (strtolower(trim($access_setting['type'])) === 'role') {
94+
if (isset($access_setting['options']['role']))
95+
{
96+
if (is_array($access_setting['options']['role']) && array_key_exists('anonymous', $access_setting['options']['role'])) {
97+
$warning = "Open access to $display_name display found";
98+
$line_num = $this->getTheLineNumber($display_name, $lines);
99+
$phpcsFile->addWarning($warning, $line_num, 'OpenAccess');
100+
}
101+
}
102+
}
103+
}
104+
}
105+
106+
return ($phpcsFile->numTokens + 1);
107+
}
108+
109+
/**
110+
* Get the line number of the display options.
111+
*
112+
* @param string $displayName
113+
* @param array $lines
114+
* @return int|boolean
115+
* Return the line number if the display option line found,
116+
* otherwise return false.
117+
*/
118+
private function getTheLineNumber(string $displayName, array $lines) {
119+
$found_display = false;
120+
$found_display_section = false;
121+
$display_options_start = 0;
122+
foreach ($lines as $line_num => $line) {
123+
if ($found_display) {
124+
if ($display_options_start) {
125+
$key = explode(':', $line['content']);
126+
$property_name = $key[0] ?? '';
127+
if (strtolower(trim($property_name)) === 'access') {
128+
return $line_num;
129+
}
130+
}
131+
else {
132+
$key = explode(':', $line['content']);
133+
$property_name = $key[0] ?? '';
134+
if (strtolower(trim($property_name)) === 'display_options') {
135+
$display_options_start = $line_num;
136+
}
137+
}
138+
}
139+
elseif ($found_display_section) {
140+
$key = explode(':', $line['content']);
141+
$property_name = $key[0] ?? '';
142+
if (strtolower(trim($property_name)) === $displayName) {
143+
$found_display = $line_num;
144+
}
145+
}
146+
else {
147+
$key = explode(':', $line['content']);
148+
$property_name = $key[0] ?? '';
149+
if ($property_name === 'display') {
150+
$found_display_section = $line_num;
151+
}
152+
}
153+
}
154+
155+
return false;
156+
}
157+
}

0 commit comments

Comments
 (0)