This commit is contained in:
team 1
2026-05-01 19:29:01 +02:00
parent ad7cac72be
commit a42f8d656d
9 changed files with 767 additions and 0 deletions

View File

@@ -132,6 +132,104 @@ final class GovernanceConfig
return $this->requiredStringList('language.protected_stopword_terms');
}
/** @return string[] */
public function getCorePatternAuditSourceRoots(): array
{
return $this->requiredStringList('core_pattern_audit.source_roots');
}
/** @return string[] */
public function getCorePatternAuditExcludedPathPrefixes(): array
{
return $this->requiredStringList('core_pattern_audit.excluded_path_prefixes');
}
/** @return string[] */
public function getCorePatternAuditExcludedPathPatterns(): array
{
return $this->requiredStringList('core_pattern_audit.excluded_path_patterns');
}
/** @return string[] */
public function getCorePatternAuditWarningPathPrefixes(): array
{
return $this->requiredStringList('core_pattern_audit.warning_path_prefixes');
}
/** @return string[] */
public function getCorePatternAuditSuspiciousCalls(): array
{
return $this->requiredStringList('core_pattern_audit.suspicious_calls');
}
/** @return string[] */
public function getCorePatternAuditDomainMarkerTerms(): array
{
return $this->requiredStringList('core_pattern_audit.domain_marker_terms');
}
/** @return array<int, array{path:string, pattern:string, reason:string}> */
public function getCorePatternAuditAllowedLiteralPatterns(): array
{
$value = $this->requiredValue('core_pattern_audit.allowed_literal_patterns');
if (!is_array($value)) {
throw $this->invalid('core_pattern_audit.allowed_literal_patterns', 'must be a list of maps');
}
$out = [];
foreach ($value as $index => $item) {
$path = 'core_pattern_audit.allowed_literal_patterns.' . (string) $index;
if (!is_array($item)) {
throw $this->invalid($path, 'must be a map');
}
$pathPrefix = isset($item['path']) && is_scalar($item['path']) ? trim((string) $item['path']) : '';
$pattern = isset($item['pattern']) && is_scalar($item['pattern']) ? trim((string) $item['pattern']) : '';
$reason = isset($item['reason']) && is_scalar($item['reason']) ? trim((string) $item['reason']) : '';
if ($pathPrefix === '') {
throw $this->invalid($path . '.path', 'must not be empty');
}
if ($pattern === '') {
throw $this->invalid($path . '.pattern', 'must not be empty');
}
if (@preg_match($pattern, '') === false) {
throw $this->invalid($path . '.pattern', 'must be a valid regex pattern');
}
$out[] = [
'path' => $pathPrefix,
'pattern' => $pattern,
'reason' => $reason,
];
}
return $out;
}
public function getCorePatternAuditMaxSnippetLength(): int
{
return $this->requiredInt('core_pattern_audit.max_snippet_length', 20);
}
private function requiredInt(string $path, int $min = PHP_INT_MIN): int
{
$value = $this->requiredValue($path);
if (is_int($value)) {
$intValue = $value;
} elseif (is_string($value) && preg_match('/^-?\d+$/', trim($value)) === 1) {
$intValue = (int) trim($value);
} else {
throw $this->invalid($path, 'must be an integer');
}
if ($intValue < $min) {
throw $this->invalid($path, sprintf('must be greater than or equal to %d', $min));
}
return $intValue;
}
private function requiredString(string $path): string
{
$value = $this->requiredValue($path);