sri 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. #!/usr/bin/env php
  2. <?php declare(strict_types=1);
  3. /**
  4. * PrivateBin
  5. *
  6. * a zero-knowledge paste bin
  7. *
  8. * @link https://github.com/PrivateBin/PrivateBin
  9. * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
  10. * @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
  11. */
  12. /**
  13. * Regenerates the subresource integrity (SRI) hashes of all JavaScript files
  14. * in the 'sri' array of lib/Configuration.php. Entries for new files get
  15. * added, entries for removed files get dropped and the array is kept sorted.
  16. * On master this is run by the GitHub workflow .github/workflows/sri.yml.
  17. */
  18. define('PATH', dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR);
  19. // JS files that browsers never load, so they need no SRI hash
  20. const IGNORED_FILES = ['common.js', 'eslint.config.js'];
  21. $opts = getopt('ch', ['check', 'help']);
  22. if ($opts === false) {
  23. fail('Erroneous command line options. Please use --help');
  24. }
  25. if (array_key_exists('h', $opts) || array_key_exists('help', $opts)) {
  26. echo <<<'EOT'
  27. Usage:
  28. sri [--check]
  29. Regenerates the SRI hashes of all JavaScript files in js/ and writes them into
  30. the 'sri' array of lib/Configuration.php.
  31. Options:
  32. -c, --check don't write anything, exit with status 1 if any hash is
  33. outdated, missing or superfluous
  34. -h, --help displays this help message
  35. EOT;
  36. exit(0);
  37. }
  38. $check = array_key_exists('c', $opts) || array_key_exists('check', $opts);
  39. // calculate the hashes of all JS files
  40. $hashes = [];
  41. foreach (glob(PATH . 'js' . DIRECTORY_SEPARATOR . '*.js') as $file) {
  42. $name = basename($file);
  43. if (in_array($name, IGNORED_FILES, true)) {
  44. continue;
  45. }
  46. $hashes['js/' . $name] = 'sha512-' . base64_encode(
  47. hash('sha512', file_get_contents($file), true)
  48. );
  49. }
  50. if (count($hashes) === 0) {
  51. fail('No JS files found in ' . PATH . 'js');
  52. }
  53. ksort($hashes, SORT_STRING);
  54. // find the 'sri' array in the configuration defaults
  55. $file = PATH . 'lib' . DIRECTORY_SEPARATOR . 'Configuration.php';
  56. $content = file_get_contents($file);
  57. if ($content === false) {
  58. fail('Could not read ' . $file);
  59. }
  60. if (preg_match(
  61. '#^( *)\'sri\' => \[\n(.*?)^\1\],\n#ms',
  62. $content, $matches, PREG_OFFSET_CAPTURE
  63. ) !== 1) {
  64. fail('Could not find the \'sri\' array in ' . $file);
  65. }
  66. $indent = $matches[1][0] . ' ';
  67. $block = $matches[2][0];
  68. $start = $matches[2][1];
  69. $current = [];
  70. preg_match_all('#\'(js/[^\']+)\' *=> \'([^\']*)\',#', $block, $entries, PREG_SET_ORDER);
  71. foreach ($entries as $entry) {
  72. $current[$entry[1]] = $entry[2];
  73. }
  74. // generate the new array content, aligning the arrows
  75. $width = max(array_map('strlen', array_keys($hashes))) + 2;
  76. $lines = '';
  77. foreach ($hashes as $key => $hash) {
  78. $lines .= $indent . str_pad('\'' . $key . '\'', $width) . ' => \'' . $hash . '\',' . "\n";
  79. }
  80. // report the differences
  81. $changes = 0;
  82. foreach ($hashes as $key => $hash) {
  83. if (!array_key_exists($key, $current)) {
  84. echo 'added: ', $key, PHP_EOL;
  85. ++$changes;
  86. } elseif ($current[$key] !== $hash) {
  87. echo 'updated: ', $key, PHP_EOL;
  88. ++$changes;
  89. }
  90. }
  91. foreach (array_diff_key($current, $hashes) as $key => $hash) {
  92. echo 'removed: ', $key, PHP_EOL;
  93. ++$changes;
  94. }
  95. if ($lines === $block) {
  96. echo 'SRI hashes are up to date.', PHP_EOL;
  97. exit(0);
  98. }
  99. if ($check) {
  100. fail('SRI hashes in ' . $file . ' are outdated, run bin/sri to update them.', 1);
  101. }
  102. if ($changes === 0) {
  103. echo 'reformatted the \'sri\' array', PHP_EOL;
  104. }
  105. $content = substr($content, 0, $start) . $lines . substr($content, $start + strlen($block));
  106. if (file_put_contents($file, $content) === false) {
  107. fail('Could not write ' . $file);
  108. }
  109. /**
  110. * print an error to STDERR and exit
  111. *
  112. * @param string $message
  113. * @param int $code
  114. */
  115. function fail(string $message, int $code = 2): void
  116. {
  117. fwrite(STDERR, $message . PHP_EOL);
  118. exit($code);
  119. }