3declare(strict_types=1);
12if (!is_file(
$root .
'/RELEASE_PROCESS.md')) {
13 echo
"OK public release hygiene: authoritative development instance is not the public mirror.\n";
17if (is_dir(
$root .
'/.git') && function_exists(
'proc_open')) {
19 array(
'git',
'-C',
$root,
'ls-files',
'--cached',
'--others',
'--exclude-standard',
'-z'),
20 array(0 => array(
'pipe',
'r'), 1 => array(
'pipe',
'w'), 2 => array(
'pipe',
'w')),
24 array(
'bypass_shell' =>
true)
28 $output = stream_get_contents($pipes[1]);
29 $error = stream_get_contents($pipes[2]);
33 if ($code === 0 && is_string($output)) {
34 $paths = array_values(array_filter(explode(
"\0", $output),
'strlen'));
35 } elseif (trim((
string)$error) !==
'') {
36 $failures[] =
'Git-Dateiliste konnte nicht gelesen werden: ' . trim((
string)$error);
42 $iterator =
new RecursiveIteratorIterator(
43 new RecursiveCallbackFilterIterator(
44 new RecursiveDirectoryIterator(
$root, FilesystemIterator::SKIP_DOTS),
45 static function (SplFileInfo $item):
bool {
46 if (!$item->isDir()) {
49 $path = str_replace(
'\\',
'/', $item->getPathname());
50 foreach (array(
'/.git',
'/dbx/vendor',
'/dbx/files',
'/files',
'/dist',
'/output',
'/tmp') as $excluded) {
51 if (str_contains($path, $excluded)) {
60 if ($item->isFile()) {
61 $paths[] = str_replace(
'\\',
'/', substr($item->getPathname(), strlen(
$root) + 1));
67 '/-----BEGIN (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----\s+[A-Za-z0-9+\/=\r\n]{80,}-----END (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----/s',
68 '/\bgithub_pat_[A-Za-z0-9_]{40,}\b/',
69 '/\bgh[pousr]_[A-Za-z0-9]{30,}\b/',
70 '/\bAKIA[0-9A-Z]{16}\b/',
71 '/\bsk-[A-Za-z0-9]{32,}\b/',
72 '/[\'"](?:token_secret|client_secret|api_key|private_key|password|pass)[\'"]\s*\]\s*=\s*[\'"][^\'"\s]{12,}[\'"]/i',
75foreach (array_unique(
$paths) as $relative) {
76 $relative = ltrim(str_replace(
'\\',
'/', $relative),
'/');
77 $absolute =
$root .
'/' . $relative;
78 if (!is_file($absolute)) {
81 $base = basename($relative);
82 $extension = strtolower(pathinfo($relative, PATHINFO_EXTENSION));
83 if (in_array(
$base, array(
'.env',
'.env.local',
'config.local.php'),
true)
84 || in_array($extension, array(
'db3',
'sqlite',
'sqlite3',
'log',
'pem',
'key',
'p12',
'pfx'),
true)
85 || preg_match(
'#/(?:backup|_backup|\.backup|work)/#i',
'/' . $relative)
87 $failures[] =
'Nicht veröffentlichbare Datei: ' . $relative;
90 if (filesize($absolute) > 2 * 1024 * 1024
91 || !preg_match(
'/\.(?:php|js|mjs|ts|json|xml|ya?ml|md|txt|dox|htm|html|css|scss|env|example)$/i', $relative)
95 $content = file_get_contents($absolute);
100 if (preg_match($pattern,
$content)) {
101 $failures[] =
'Mögliches Secret in: ' . $relative;
108 fwrite(STDERR,
"FAIL\n- " . implode(
"\n- ",
$failures) .
"\n");
112echo
'OK public release hygiene: ' . count(array_unique(
$paths)) .
" Dateien geprüft.\n";
if(!is_file($root . '/RELEASE_PROCESS.md')) if(is_dir( $root . '/.git') &&function_exists( 'proc_open')) if($paths===array()) $secretPatterns