fix: prevent unsafe url rewrites

This commit is contained in:
Keith Solomon
2026-04-26 14:42:11 -05:00
parent ddb0e12a9b
commit badd58ada6
2 changed files with 100 additions and 6 deletions
+39 -6
View File
@@ -4,26 +4,59 @@ namespace WPContentSync\Url;
final class UrlTransformer {
public function transformString( string $value, UrlMappingCollection $mappings ): string {
$transformed = $value;
$replacements = array();
foreach ( $mappings->all() as $mapping ) {
$transformed = $this->replaceMapping( $transformed, $mapping );
$replacements = array_merge( $replacements, $this->replacementsForMapping( $mapping ) );
}
return $transformed;
if ( array() === $replacements ) {
return $value;
}
return preg_replace_callback(
$this->replacementPattern( array_keys( $replacements ) ),
static function ( array $matches ) use ( $replacements ): string {
return $replacements[ $matches[0] ];
},
$value
) ?? $value;
}
private function replaceMapping( string $value, UrlMapping $mapping ): string {
/**
* @return array<string, string>
*/
private function replacementsForMapping( UrlMapping $mapping ): array {
$source = $mapping->sourceUrl();
$destination = $mapping->destinationUrl();
$replacements = array(
return array(
$source => $destination,
str_replace( '&', '&amp;', $source ) => str_replace( '&', '&amp;', $destination ),
$this->toProtocolRelative( $source ) => $this->toProtocolRelative( $destination ),
str_replace( '&', '&amp;', $this->toProtocolRelative( $source ) ) => str_replace( '&', '&amp;', $this->toProtocolRelative( $destination ) ),
);
}
/**
* @param array<int, string> $sources Replacement sources.
*/
private function replacementPattern( array $sources ): string {
usort(
$sources,
static function ( string $left, string $right ): int {
return strlen( $right ) <=> strlen( $left );
}
);
return strtr( $value, $replacements );
$quoted = array_map(
static function ( string $source ): string {
return preg_quote( $source, '~' );
},
$sources
);
return '~(?<![A-Za-z0-9.-])(?:' . implode( '|', $quoted ) . ')(?![A-Za-z0-9.-])~';
}
private function toProtocolRelative( string $url ): string {