Last active
April 18, 2024 22:39
-
-
Save brandonpayton/9e3da0845791f6e5c833013d83cf86d6 to your computer and use it in GitHub Desktop.
Serving playground.wordpress.net from nginx-based host with no customization of nginx config
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* ATTENTION: Please update Playground's .htaccess file as necessary | |
* whenever making changes here. | |
*/ | |
define('PLAYGROUND_DEBUG', false); | |
playground_handle_request(); | |
function playground_handle_request() { | |
$log = defined( 'PLAYGROUND_DEBUG' ) && PLAYGROUND_DEBUG | |
? function ( $str ) { error_log( "PLAYGROUND: $str" ); } | |
: function () {}; | |
$log( "Handling request for '${_SERVER['REQUEST_URI']}'" ); | |
$url = parse_url( $_SERVER['REQUEST_URI'] ); | |
if ( false === $url ) { | |
$log( "Unable to parse URL: '$url'" ); | |
return; | |
} | |
$original_requested_path = $url['path']; | |
$log( "Requested path: '$original_requested_path'" ); | |
// | |
// REWRITES | |
// | |
$requested_path = $original_requested_path; | |
if ( str_starts_with( $requested_path, '/scope:' ) ) { | |
$scope_match = array(); | |
$scope_match_result = preg_match( | |
'#/scope:[^/]*(?<path>/.*)#', | |
$requested_path, | |
$scope_match | |
); | |
if ( $scope_match_result === 1 ) { | |
$log( "Rewriting scope path '$requested_path' to '${scope_match['path']}'" ); | |
$requested_path = $scope_match['path']; | |
} | |
} | |
if ( str_ends_with( $requested_path, 'plugin-proxy' ) ) { | |
$requested_path = '/plugin-proxy.php'; | |
} | |
if ( $requested_path !== $original_requested_path ) { | |
$log( "Rewrote '$original_requested_path' to '$requested_path'" ); | |
} | |
// | |
// REDIRECTS | |
// | |
if ( str_ends_with( $requested_path, '/wordpress-browser.html' ) ) { | |
header( 'Location: /' ); | |
http_response_code( 301 ); | |
die(); | |
} | |
$has_dotorg_referrer = isset( $_SERVER['HTTP_REFERER'] ) && ( | |
str_starts_with( $_SERVER['HTTP_REFERER'], 'https://developer.wordpress.org/' ) || | |
str_starts_with( $_SERVER['HTTP_REFERER'], 'https://wordpress.org/' ) | |
); | |
if ( $has_dotorg_referrer && str_ends_with( $requested_path, '/wordpress.html' ) ) { | |
header( 'Location: /index.html' ); | |
http_response_code( 302 ); | |
die(); | |
} | |
// | |
// PATH RESOLUTION | |
// | |
$resolved_path = realpath( __DIR__ . $requested_path ); | |
if ( is_dir( $resolved_path ) ) { | |
$resolved_path = playground_resolve_to_index_file( $resolved_path ); | |
} | |
if ( false === $resolved_path ) { | |
$resolved_path = realpath( __DIR__ . '/playground-files' . $requested_path ); | |
if ( is_dir( $resolved_path ) ) { | |
$resolved_path = playground_resolve_to_index_file( $resolved_path ); | |
} | |
} | |
$log( "Resolved '$original_requested_path' to '$resolved_path'." ); | |
if ( false === $resolved_path ) { | |
$log( "File not found: '$resolved_path'" ); | |
http_response_code( 404 ); | |
die(); | |
} | |
if ( ! str_starts_with( $resolved_path, '/srv/htdocs/' ) ) { | |
$log( "This looks like attempted path traversal: '$original_requested_path'" ); | |
http_response_code( 403 ); | |
die(); | |
} | |
// | |
// RESPONSE HEADERS | |
// | |
$filename = basename( $resolved_path ); | |
$extension_match = array(); | |
$extension_match_result = preg_match( | |
'/\.(?<value>[^\.]+)$/', | |
$filename, | |
$extension_match | |
); | |
$extension = $extension_match_result === 1 | |
? strtolower( $extension_match['value'] ) | |
: false; | |
require_once __DIR__ . '/mime-types.php'; | |
if ( isset( $mime_types[ $extension ] ) ) { | |
$content_type = $mime_types[ $extension ]; | |
$log( "Setting Content-Type to '$content_type'" ); | |
header( "Content-Type: $content_type" ); | |
} | |
if ( 'iframe-worker.html' === $filename ) { | |
$log( "Setting Origin-Agent-Cluster header for '$filename'" ); | |
header( 'Origin-Agent-Cluster: ?1' ); | |
} elseif ( str_ends_with( $filename, 'store.zip' ) ) { | |
// Disable compression so zip file can be read piece by piece | |
// using file offsets embedded in the zip's metadata. | |
// TODO: Is this comment correct? | |
$log( "Disabling compression, Allowing use by all origins for: '$resolved_path'" ); | |
header( 'Content-Encoding: identity' ); | |
header( 'Access-Control-Allow-Origin: *' ); | |
} elseif ( 'index.html' === $filename ) { | |
$log( "Discouraging cache for: '$resolved_path'" ); | |
// TODO: .htaccess removed Etag header, but can we control whether nginx adds one? | |
header( 'Cache-Control: max-age=0, no-cache, no-store, must-revalidate' ); | |
} elseif ( | |
in_array( | |
$filename, | |
array( | |
'index.js', | |
'blueprint-schema.json', | |
'logger.php', | |
'wp-cli.phar', | |
'wordpress-importer.zip', | |
), | |
true | |
) | |
) { | |
$log( "Discouraging cache, Allowing use by all origins for: '$resolved_path'" ); | |
header( 'Access-Control-Allow-Origin: *' ); | |
// TODO: .htaccess removed Etag header, but can we control whether nginx adds one? | |
header( 'Cache-Control: max-age=0, no-cache, no-store, must-revalidate' ); | |
} else { | |
$log( "Marking for cache: '$resolved_path'" ); | |
header( 'A8C-Edge-Cache: cache' ); | |
header( 'Cache-Control: max-age=302, must-revalidate' ); | |
} | |
// | |
// CONTENT | |
// | |
if ( 'php' === $extension ) { | |
$log( "Running PHP: '$resolved_path'" ); | |
require $resolved_path; | |
} else { | |
$log( "Reading static file: '$resolved_path'" ); | |
readfile( $resolved_path ); | |
} | |
die(); | |
// TODO: Set these | |
/* | |
SetEnv GITHUB_TOKEN --secret-- | |
SetEnv DEPLOY_AUTH_TOKEN --secret-- | |
# GitHub App | |
SetEnv CLIENT_ID --secret-- | |
SetEnv CLIENT_SECRET --secret-- | |
# Slack All | |
SetEnv SLACK_CHANNEL --secret-- | |
SetEnv SLACK_TOKEN --secret-- | |
*/ | |
/* | |
Options +Multiviews | |
AddEncoding x-gzip .gz | |
*/ | |
} | |
function playground_resolve_to_index_file( $real_path ) { | |
if ( file_exists( "$real_path/index.php" ) ) { | |
return "$real_path/index.php"; | |
} elseif ( file_exists( "$real_path/index.html" ) ) { | |
return "$real_path/index.html"; | |
} else { | |
return false; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Adapted from https://github.com/nginx/nginx/blob/d8a849ae3c99ee5ca82c9a06074761e937dac6d6/conf/mime.types | |
$mime_types = array( | |
'3gpp' => 'video/3gpp', | |
'7z' => 'application/x-7z-compressed', | |
'asx' => 'video/x-ms-asf', | |
'atom' => 'application/atom+xml', | |
'avi' => 'video/x-msvideo', | |
'avif' => 'image/avif', | |
'bin' => 'application/octet-stream', | |
'bmp' => 'image/x-ms-bmp', | |
'cco' => 'application/x-cocoa', | |
'css' => 'text/css', | |
'data' => 'application/octet-stream', | |
'deb' => 'application/octet-stream', | |
'der' => 'application/x-x509-ca-cert', | |
'dmg' => 'application/octet-stream', | |
'doc' => 'application/msword', | |
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', | |
'eot' => 'application/vnd.ms-fontobject', | |
'flv' => 'video/x-flv', | |
'gif' => 'image/gif', | |
'hqx' => 'application/mac-binhex40', | |
'htc' => 'text/x-component', | |
'html' => 'text/html', | |
'ico' => 'image/x-icon', | |
'iso' => 'application/octet-stream', | |
'jad' => 'text/vnd.sun.j2me.app-descriptor', | |
'jar' => 'application/java-archive', | |
'jardiff' => 'application/x-java-archive-diff', | |
'jng' => 'image/x-jng', | |
'jnlp' => 'application/x-java-jnlp-file', | |
'jpeg' => 'image/jpeg', | |
'js' => 'application/javascript', | |
'json' => 'application/json', | |
'kml' => 'application/vnd.google-earth.kml+xml', | |
'kmz' => 'application/vnd.google-earth.kmz', | |
'm3u8' => 'application/vnd.apple.mpegurl', | |
'm4a' => 'audio/x-m4a', | |
'm4v' => 'video/x-m4v', | |
'mid' => 'audio/midi', | |
'mml' => 'text/mathml', | |
'mng' => 'video/x-mng', | |
'mov' => 'video/quicktime', | |
'mp3' => 'audio/mpeg', | |
'mp4' => 'video/mp4', | |
'mpeg' => 'video/mpeg', | |
'msi' => 'application/octet-stream', | |
'odg' => 'application/vnd.oasis.opendocument.graphics', | |
'odp' => 'application/vnd.oasis.opendocument.presentation', | |
'ods' => 'application/vnd.oasis.opendocument.spreadsheet', | |
'odt' => 'application/vnd.oasis.opendocument.text', | |
'ogg' => 'audio/ogg', | |
'pdf' => 'application/pdf', | |
'pl' => 'application/x-perl', | |
'png' => 'image/png', | |
'ppt' => 'application/vnd.ms-powerpoint', | |
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', | |
'prc' => 'application/x-pilot', | |
'ps' => 'application/postscript', | |
'ra' => 'audio/x-realaudio', | |
'rar' => 'application/x-rar-compressed', | |
'rpm' => 'application/x-redhat-package-manager', | |
'rss' => 'application/rss+xml', | |
'rtf' => 'application/rtf', | |
'run' => 'application/x-makeself', | |
'sea' => 'application/x-sea', | |
'sit' => 'application/x-stuffit', | |
'svg' => 'image/svg+xml', | |
'swf' => 'application/x-shockwave-flash', | |
'tcl' => 'application/x-tcl', | |
'tif' => 'image/tiff', | |
'ts' => 'video/mp2t', | |
'txt' => 'text/plain', | |
'wasm' => 'application/wasm', | |
'wbmp' => 'image/vnd.wap.wbmp', | |
'webm' => 'video/webm', | |
'webp' => 'image/webp', | |
'wml' => 'text/vnd.wap.wml', | |
'wmlc' => 'application/vnd.wap.wmlc', | |
'wmv' => 'video/x-ms-wmv', | |
'woff' => 'font/woff', | |
'woff2' => 'font/woff2', | |
'xhtml' => 'application/xhtml+xml', | |
'xls' => 'application/vnd.ms-excel', | |
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', | |
'xml' => 'text/xml', | |
'xpi' => 'application/x-xpinstall', | |
'xspf' => 'application/xspf+xml', | |
'zip' => 'application/zip', | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment