Skip to content

Instantly share code, notes, and snippets.

@brandonpayton
Last active April 18, 2024 22:39
Show Gist options
  • Save brandonpayton/9e3da0845791f6e5c833013d83cf86d6 to your computer and use it in GitHub Desktop.
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
<?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;
}
}
<?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