Skip to content

Commit

Permalink
Make allowed and default protocols for autolinks configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
colinodell committed Jul 31, 2023
1 parent 462859f commit 2e78c27
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) princi
### Added

- The `AttributesExtension` now supports attributes without values (#985, #986)
- The `AutolinkExtension` exposes two new configuration options to override the default behavior (#969):
- `autolink/allowed_protocols` - an array of protocols to allow autolinking for
- `autolink/default_protocol` - the default protocol to use when none is specified

## [2.4.0] - 2023-03-24

Expand Down
19 changes: 18 additions & 1 deletion docs/2.5/extensions/autolinks.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\MarkdownConverter;

// Define your configuration, if needed
$config = [];
$config = [
'autolink' => [
'allowed_protocols' => ['https'], // defaults to ['https', 'http', 'ftp']
'default_protocols' => 'https', // defaults to 'http'
],
];

// Configure the Environment with all the CommonMark parsers/renderers
$environment = new Environment($config);
Expand All @@ -46,6 +51,18 @@ $converter = new MarkdownConverter($environment);
echo $converter->convert('I successfully installed the https://github.com/thephpleague/commonmark project with the Autolink extension!');
```

## Configuration

As of version 2.5.0, this extension supports the following configuration options under the `autolink` configuration:

### `allowed_protocols` option

This option defines which types of URLs will be autolinked. The default value of `['https', 'http', 'ftp']` means that only URLs using those protocols will be autolinked. Setting this to just `['https']` means that only HTTPS URLs will be autolinked.

### `default_protocol` option

This option defines the default protocol for URLs that start with `www.` and don't have an explicit protocol set. For example, setting this to `https` would convert `www.example.com` to `https://www.example.com`.

## `@mention`-style Autolinking

As of v1.5, [mention autolinking is now handled by a Mention Parser outside of this extension](/2.5/extensions/mentions/).
Expand Down
19 changes: 16 additions & 3 deletions src/Extension/Autolink/AutolinkExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,26 @@
namespace League\CommonMark\Extension\Autolink;

use League\CommonMark\Environment\EnvironmentBuilderInterface;
use League\CommonMark\Extension\ExtensionInterface;
use League\CommonMark\Extension\ConfigurableExtensionInterface;
use League\Config\ConfigurationBuilderInterface;
use Nette\Schema\Expect;

final class AutolinkExtension implements ExtensionInterface
final class AutolinkExtension implements ConfigurableExtensionInterface
{
public function configureSchema(ConfigurationBuilderInterface $builder): void
{
$builder->addSchema('autolink', Expect::structure([
'allowed_protocols' => Expect::listOf('string')->default(['http', 'https', 'ftp'])->mergeDefaults(false),
'default_protocol' => Expect::string()->default('http'),
]));
}

public function register(EnvironmentBuilderInterface $environment): void
{
$environment->addInlineParser(new EmailAutolinkParser());
$environment->addInlineParser(new UrlAutolinkParser());
$environment->addInlineParser(new UrlAutolinkParser(
$environment->getConfiguration()->get('autolink.allowed_protocols'),
$environment->getConfiguration()->get('autolink.default_protocol'),
));
}
}
10 changes: 7 additions & 3 deletions src/Extension/Autolink/UrlAutolinkParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,12 @@ final class UrlAutolinkParser implements InlineParserInterface
*/
private string $finalRegex;

private string $defaultProtocol;

/**
* @param array<int, string> $allowedProtocols
*/
public function __construct(array $allowedProtocols = ['http', 'https', 'ftp'])
public function __construct(array $allowedProtocols = ['http', 'https', 'ftp'], string $defaultProtocol = 'http')
{
/**
* @psalm-suppress PropertyTypeCoercion
Expand All @@ -78,6 +80,8 @@ public function __construct(array $allowedProtocols = ['http', 'https', 'ftp'])
foreach ($allowedProtocols as $protocol) {
$this->prefixes[] = $protocol . '://';
}

$this->defaultProtocol = $defaultProtocol;
}

public function getMatchDefinition(): InlineParserMatch
Expand Down Expand Up @@ -120,9 +124,9 @@ public function parse(InlineParserContext $inlineContext): bool

$cursor->advanceBy(\mb_strlen($url, 'UTF-8'));

// Auto-prefix 'http://' onto 'www' URLs
// Auto-prefix 'http(s)://' onto 'www' URLs
if (\substr($url, 0, 4) === 'www.') {
$inlineContext->getContainer()->appendChild(new Link('http://' . $url, $url));
$inlineContext->getContainer()->appendChild(new Link($this->defaultProtocol . '://' . $url, $url));

return true;
}
Expand Down
50 changes: 50 additions & 0 deletions tests/functional/Extension/Autolink/UrlAutolinkParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,54 @@ public function testUrlAutolinksWithStrikethrough(): void
$html
);
}

public function testDisallowedProtocols(): void
{
$environment = new Environment([
'autolink' => [
'allowed_protocols' => ['https'],
],
]);
$environment->addExtension(new CommonMarkCoreExtension());
$environment->addExtension(new AutolinkExtension());

$converter = new MarkdownConverter($environment);
$html = $converter->convert('http://insecure.example.com')->getContent();

$this->assertSame("<p>http://insecure.example.com</p>\n", $html);
}

/**
* @dataProvider dataProviderForSchemes
*/
public function testUrlAutolinksWithConfigurableSchemes(string $scheme): void
{
$markdown = 'www.example.com';

$environment = new Environment([
'autolink' => [
'default_protocol' => $scheme,
],
]);
$environment->addExtension(new CommonMarkCoreExtension());
$environment->addExtension(new AutolinkExtension());

$converter = new MarkdownConverter($environment);
$html = $converter->convert($markdown)->getContent();

$this->assertSame(
'<p><a href="' . $scheme . '://www.example.com">www.example.com</a></p>' . "\n",
$html
);
}

/**
* @return iterable<array<string>>
*/
public function dataProviderForSchemes(): iterable
{
yield ['http'];
yield ['https'];
yield ['ftp'];
}
}

0 comments on commit 2e78c27

Please sign in to comment.