nilsteampassnet /
TeamPass
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
| 1 | <?php |
||
| 2 | declare(strict_types=1); |
||
| 3 | |||
| 4 | /** |
||
| 5 | * Teampass License Compliance Checker (Memory Optimized) |
||
| 6 | * Generates a comprehensive license compliance report for all dependencies |
||
| 7 | * |
||
| 8 | * @author Nils Laumaillé |
||
| 9 | * @license GPL-3.0 |
||
| 10 | */ |
||
| 11 | |||
| 12 | class LicenseComplianceChecker |
||
| 13 | { |
||
| 14 | private const GPL_COMPATIBLE_LICENSES = [ |
||
| 15 | 'MIT', 'BSD-2-Clause', 'BSD-3-Clause', 'Apache-2.0', 'LGPL-2.1', |
||
| 16 | 'LGPL-3.0', 'GPL-2.0', 'GPL-3.0', 'ISC', 'Unlicense', 'CC0-1.0' |
||
| 17 | ]; |
||
| 18 | |||
| 19 | private int $phpCount = 0; |
||
| 20 | private int $jsCount = 0; |
||
| 21 | private int $errorCount = 0; |
||
| 22 | private int $warningCount = 0; |
||
| 23 | private $reportFile; |
||
| 24 | |||
| 25 | /** |
||
| 26 | * Main execution method |
||
| 27 | * |
||
| 28 | * @return void |
||
| 29 | */ |
||
| 30 | public function run(): void |
||
| 31 | { |
||
| 32 | echo "=== Teampass License Compliance Checker ===\n\n"; |
||
| 33 | |||
| 34 | // Open report file for streaming write |
||
| 35 | $this->reportFile = fopen(__DIR__ . '/LICENSE_COMPLIANCE_REPORT.md', 'w'); |
||
| 36 | |||
| 37 | $this->writeHeader(); |
||
| 38 | $this->processPhpDependencies(); |
||
| 39 | $this->processJsDependencies(); |
||
| 40 | $this->writeFooter(); |
||
| 41 | |||
| 42 | fclose($this->reportFile); |
||
| 43 | |||
| 44 | echo "\n✓ Compliance report generated: licences/LICENSE_COMPLIANCE_REPORT.md\n"; |
||
| 45 | echo " PHP dependencies: {$this->phpCount}\n"; |
||
| 46 | echo " JS dependencies: {$this->jsCount}\n"; |
||
| 47 | echo " Errors: {$this->errorCount}\n"; |
||
| 48 | echo " Warnings: {$this->warningCount}\n"; |
||
| 49 | |||
| 50 | if ($this->errorCount > 0) { |
||
| 51 | echo "\n⚠️ CRITICAL: Incompatible licenses detected!\n"; |
||
| 52 | exit(1); |
||
|
0 ignored issues
–
show
|
|||
| 53 | } |
||
| 54 | } |
||
| 55 | |||
| 56 | /** |
||
| 57 | * Write report header |
||
| 58 | * |
||
| 59 | * @return void |
||
| 60 | */ |
||
| 61 | private function writeHeader(): void |
||
| 62 | { |
||
| 63 | $header = "# Teampass License Compliance Report\n\n"; |
||
| 64 | $header .= "**Generated:** " . date('Y-m-d H:i:s') . "\n"; |
||
| 65 | $header .= "**Project License:** GNU General Public License v3.0\n\n"; |
||
| 66 | |||
| 67 | fwrite($this->reportFile, $header); |
||
| 68 | } |
||
| 69 | |||
| 70 | /** |
||
| 71 | * Process PHP dependencies from composer.lock |
||
| 72 | * |
||
| 73 | * @return void |
||
| 74 | */ |
||
| 75 | private function processPhpDependencies(): void |
||
| 76 | { |
||
| 77 | $lockFile = __DIR__ . '/../composer.lock'; |
||
| 78 | |||
| 79 | if (!file_exists($lockFile)) { |
||
| 80 | $this->errorCount++; |
||
| 81 | fwrite($this->reportFile, "❌ **ERROR:** composer.lock not found\n\n"); |
||
| 82 | return; |
||
| 83 | } |
||
| 84 | |||
| 85 | echo "Processing PHP dependencies...\n"; |
||
| 86 | |||
| 87 | // Stream parse JSON to avoid loading everything in memory |
||
| 88 | $content = file_get_contents($lockFile); |
||
| 89 | $data = json_decode($content, true); |
||
| 90 | unset($content); // Free memory |
||
| 91 | |||
| 92 | if (!isset($data['packages'])) { |
||
| 93 | $this->errorCount++; |
||
| 94 | fwrite($this->reportFile, "❌ **ERROR:** Invalid composer.lock format\n\n"); |
||
| 95 | return; |
||
| 96 | } |
||
| 97 | |||
| 98 | fwrite($this->reportFile, "## PHP Dependencies (Composer)\n\n"); |
||
| 99 | fwrite($this->reportFile, "| Package | Version | License | Status |\n"); |
||
| 100 | fwrite($this->reportFile, "|---------|---------|---------|--------|\n"); |
||
| 101 | |||
| 102 | foreach ($data['packages'] as $package) { |
||
| 103 | $this->phpCount++; |
||
| 104 | $this->writePhpDependency($package); |
||
| 105 | |||
| 106 | // Progress indicator |
||
| 107 | if ($this->phpCount % 20 === 0) { |
||
| 108 | echo " Processed {$this->phpCount} packages...\n"; |
||
| 109 | } |
||
| 110 | } |
||
| 111 | |||
| 112 | fwrite($this->reportFile, "\n"); |
||
| 113 | echo "✓ Processed {$this->phpCount} PHP dependencies\n"; |
||
| 114 | } |
||
| 115 | |||
| 116 | /** |
||
| 117 | * Write single PHP dependency to report |
||
| 118 | * |
||
| 119 | * @param array $package Package data from composer.lock |
||
| 120 | * @return void |
||
| 121 | */ |
||
| 122 | private function writePhpDependency(array $package): void |
||
| 123 | { |
||
| 124 | $name = $package['name']; |
||
| 125 | $version = $package['version']; |
||
| 126 | $licenses = $package['license'] ?? ['Unknown']; |
||
| 127 | $licenseStr = implode(', ', $licenses); |
||
| 128 | |||
| 129 | $status = $this->getComplianceStatus($licenses); |
||
| 130 | |||
| 131 | $line = "| {$name} | {$version} | {$licenseStr} | {$status} |\n"; |
||
| 132 | fwrite($this->reportFile, $line); |
||
| 133 | } |
||
| 134 | |||
| 135 | /** |
||
| 136 | * Process JavaScript dependencies |
||
| 137 | * |
||
| 138 | * @return void |
||
| 139 | */ |
||
| 140 | private function processJsDependencies(): void |
||
| 141 | { |
||
| 142 | $jsFile = __DIR__ . '/javascript-dependencies.json'; |
||
| 143 | |||
| 144 | fwrite($this->reportFile, "## JavaScript/CSS Dependencies\n\n"); |
||
| 145 | |||
| 146 | if (!file_exists($jsFile)) { |
||
| 147 | $this->warningCount++; |
||
| 148 | fwrite($this->reportFile, "⚠️ **WARNING:** javascript-dependencies.json not found\n\n"); |
||
| 149 | $this->createJsTemplate(); |
||
| 150 | fwrite($this->reportFile, "Template created at `licences/javascript-dependencies.json`\n\n"); |
||
| 151 | return; |
||
| 152 | } |
||
| 153 | |||
| 154 | echo "Processing JavaScript dependencies...\n"; |
||
| 155 | |||
| 156 | $jsData = json_decode(file_get_contents($jsFile), true); |
||
| 157 | |||
| 158 | if (!isset($jsData['dependencies']) || empty($jsData['dependencies'])) { |
||
| 159 | fwrite($this->reportFile, "_No JavaScript dependencies registered._\n\n"); |
||
| 160 | return; |
||
| 161 | } |
||
| 162 | |||
| 163 | fwrite($this->reportFile, "| Package | Version | License | Status |\n"); |
||
| 164 | fwrite($this->reportFile, "|---------|---------|---------|--------|\n"); |
||
| 165 | |||
| 166 | foreach ($jsData['dependencies'] as $dep) { |
||
| 167 | $this->jsCount++; |
||
| 168 | $this->writeJsDependency($dep); |
||
| 169 | } |
||
| 170 | |||
| 171 | fwrite($this->reportFile, "\n"); |
||
| 172 | echo "✓ Processed {$this->jsCount} JavaScript dependencies\n"; |
||
| 173 | } |
||
| 174 | |||
| 175 | /** |
||
| 176 | * Write single JavaScript dependency to report |
||
| 177 | * |
||
| 178 | * @param array $dep Dependency data |
||
| 179 | * @return void |
||
| 180 | */ |
||
| 181 | private function writeJsDependency(array $dep): void |
||
| 182 | { |
||
| 183 | $name = $dep['name'] ?? 'Unknown'; |
||
| 184 | $version = $dep['version'] ?? 'Unknown'; |
||
| 185 | $licenses = $dep['licenses'] ?? ['Unknown']; |
||
| 186 | $licenseStr = implode(', ', $licenses); |
||
| 187 | |||
| 188 | $status = $this->getComplianceStatus($licenses); |
||
| 189 | |||
| 190 | $line = "| {$name} | {$version} | {$licenseStr} | {$status} |\n"; |
||
| 191 | fwrite($this->reportFile, $line); |
||
| 192 | } |
||
| 193 | |||
| 194 | /** |
||
| 195 | * Get compliance status for licenses |
||
| 196 | * |
||
| 197 | * @param array $licenses Array of license identifiers |
||
| 198 | * @return string Status string with emoji |
||
| 199 | */ |
||
| 200 | private function getComplianceStatus(array $licenses): string |
||
| 201 | { |
||
| 202 | if (empty($licenses) || in_array('Unknown', $licenses)) { |
||
| 203 | $this->warningCount++; |
||
| 204 | return '⚠️ Unknown'; |
||
| 205 | } |
||
| 206 | |||
| 207 | $compatible = false; |
||
| 208 | foreach ($licenses as $license) { |
||
| 209 | $normalized = trim($license); |
||
| 210 | |||
| 211 | // Check if compatible |
||
| 212 | foreach (self::GPL_COMPATIBLE_LICENSES as $compat) { |
||
| 213 | if (stripos($normalized, $compat) !== false) { |
||
| 214 | $compatible = true; |
||
| 215 | break 2; |
||
| 216 | } |
||
| 217 | } |
||
| 218 | } |
||
| 219 | |||
| 220 | if (!$compatible) { |
||
| 221 | $this->warningCount++; |
||
| 222 | return '⚠️ Review'; |
||
| 223 | } |
||
| 224 | |||
| 225 | return '✅ Compatible'; |
||
| 226 | } |
||
| 227 | |||
| 228 | /** |
||
| 229 | * Write report footer |
||
| 230 | * |
||
| 231 | * @return void |
||
| 232 | */ |
||
| 233 | private function writeFooter(): void |
||
| 234 | { |
||
| 235 | $footer = "## Summary\n\n"; |
||
| 236 | $footer .= "- **Total Dependencies:** " . ($this->phpCount + $this->jsCount) . "\n"; |
||
| 237 | $footer .= "- **PHP Dependencies:** {$this->phpCount}\n"; |
||
| 238 | $footer .= "- **JavaScript Dependencies:** {$this->jsCount}\n"; |
||
| 239 | $footer .= "- **Errors:** {$this->errorCount}\n"; |
||
| 240 | $footer .= "- **Warnings:** {$this->warningCount}\n\n"; |
||
| 241 | |||
| 242 | if ($this->errorCount === 0 && $this->warningCount === 0) { |
||
| 243 | $footer .= "✅ **Status:** All dependencies are GPL-3.0 compatible\n\n"; |
||
| 244 | } elseif ($this->errorCount > 0) { |
||
| 245 | $footer .= "❌ **Status:** CRITICAL - Issues detected\n\n"; |
||
| 246 | } else { |
||
| 247 | $footer .= "⚠️ **Status:** Some licenses require manual review\n\n"; |
||
| 248 | } |
||
| 249 | |||
| 250 | $footer .= "## GPL-3.0 Compatible Licenses\n\n"; |
||
| 251 | foreach (self::GPL_COMPATIBLE_LICENSES as $license) { |
||
| 252 | $footer .= "- {$license}\n"; |
||
| 253 | } |
||
| 254 | $footer .= "\n"; |
||
| 255 | |||
| 256 | $footer .= "## Maintenance\n\n"; |
||
| 257 | $footer .= "**Update JavaScript dependencies:**\n"; |
||
| 258 | $footer .= "Edit `licences/javascript-dependencies.json`\n\n"; |
||
| 259 | $footer .= "**Run compliance check:**\n"; |
||
| 260 | $footer .= "```bash\n"; |
||
| 261 | $footer .= "php licences/compliance-checker.php\n"; |
||
| 262 | $footer .= "```\n\n"; |
||
| 263 | |||
| 264 | $footer .= "---\n\n"; |
||
| 265 | $footer .= "*Auto-generated report - Last updated: " . date('Y-m-d H:i:s') . "*\n"; |
||
| 266 | |||
| 267 | fwrite($this->reportFile, $footer); |
||
| 268 | } |
||
| 269 | |||
| 270 | /** |
||
| 271 | * Create JavaScript dependencies template |
||
| 272 | * |
||
| 273 | * @return void |
||
| 274 | */ |
||
| 275 | private function createJsTemplate(): void |
||
| 276 | { |
||
| 277 | $template = [ |
||
| 278 | '_comment' => 'Manually maintain this file with JavaScript/CSS dependencies', |
||
| 279 | 'last_updated' => date('Y-m-d'), |
||
| 280 | 'dependencies' => [ |
||
| 281 | [ |
||
| 282 | 'name' => 'jQuery', |
||
| 283 | 'version' => '3.x', |
||
| 284 | 'licenses' => ['MIT'], |
||
| 285 | 'homepage' => 'https://jquery.org', |
||
| 286 | 'type' => 'JavaScript' |
||
| 287 | ], |
||
| 288 | [ |
||
| 289 | 'name' => 'AdminLTE', |
||
| 290 | 'version' => '3.x', |
||
| 291 | 'licenses' => ['MIT'], |
||
| 292 | 'homepage' => 'https://adminlte.io', |
||
| 293 | 'type' => 'CSS/JavaScript' |
||
| 294 | ] |
||
| 295 | ] |
||
| 296 | ]; |
||
| 297 | |||
| 298 | file_put_contents( |
||
| 299 | __DIR__ . '/javascript-dependencies.json', |
||
| 300 | json_encode($template, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) |
||
| 301 | ); |
||
| 302 | } |
||
| 303 | } |
||
| 304 | |||
| 305 | // Execute |
||
| 306 | $checker = new LicenseComplianceChecker(); |
||
| 307 | $checker->run(); |
In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.