Merge pull request 'fix(376): XML-escape access_token value in XMLTV guide output' (#419) from fix/376-xmltv-token-escape into main
Build ErsatzTV Image / CI image pin matches docker/ci (push) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 5m23s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 18m59s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 19m55s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 5m19s
Build ErsatzTV Image / CI image pin matches docker/ci (push) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / Functional E2E (curl contracts) (push) Successful in 5m23s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 18m59s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 19m55s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 5m19s
This commit was merged in pull request #419.
This commit is contained in:
@@ -60,7 +60,10 @@ public partial class GetChannelGuideHandler(
|
||||
var accessTokenUri = $"?v={mtime}";
|
||||
if (!string.IsNullOrWhiteSpace(request.AccessToken))
|
||||
{
|
||||
accessTokenUri += $"&access_token={request.AccessToken}";
|
||||
// The token value is HTTP-request-derived and interpolated raw into the pre-built XMLTV
|
||||
// cache fragments, so it must be XML-escaped like {RequestBase} above — a token containing
|
||||
// '&', '<', '>', or '"' would otherwise malform the whole guide. Opaque tokens are a no-op.
|
||||
accessTokenUri += $"&access_token={SecurityElement.Escape(request.AccessToken)}";
|
||||
}
|
||||
|
||||
string channelsFragment = await ReadAllTextShared(channelsFile, cancellationToken);
|
||||
|
||||
@@ -187,6 +187,47 @@ public class ChannelGuideGoldenTests
|
||||
xml.ShouldNotContain("a&b");
|
||||
}
|
||||
|
||||
// The access-token value is HTTP-request-derived (?access_token=) and interpolated raw into the
|
||||
// {AccessTokenUri} placeholder, so a token containing XML-special chars must be escaped too —
|
||||
// otherwise it malforms the whole guide, exactly like the {RequestBase} case above. (Finding #376.)
|
||||
[Test]
|
||||
public async Task Guide_xml_escapes_access_token()
|
||||
{
|
||||
MockFileSystem fileSystem = BuildCacheFileSystem();
|
||||
var localFileSystem = Substitute.For<ILocalFileSystem>();
|
||||
localFileSystem
|
||||
.ListFiles(FileSystemLayout.ChannelGuideCacheFolder)
|
||||
.Returns(new[]
|
||||
{
|
||||
FragmentPath(fileSystem, "channels.xml"),
|
||||
FragmentPath(fileSystem, "2.xml")
|
||||
});
|
||||
|
||||
var configElementRepository = Substitute.For<IConfigElementRepository>();
|
||||
configElementRepository
|
||||
.GetValue<string>(Arg.Any<ConfigElementKey>(), Arg.Any<CancellationToken>())
|
||||
.Returns(Option<string>.None);
|
||||
|
||||
var handler = new GetChannelGuideHandler(
|
||||
_dbContextFactory,
|
||||
new RecyclableMemoryStreamManager(),
|
||||
fileSystem,
|
||||
localFileSystem,
|
||||
configElementRepository);
|
||||
|
||||
Either<BaseError, ChannelGuide> result = await handler.Handle(
|
||||
new GetChannelGuide(Scheme, Host, BaseUrl: "", AccessToken: "tok&<>\""),
|
||||
CancellationToken.None);
|
||||
|
||||
string xml = result.Match(
|
||||
Right: guide => guide.ToXml(),
|
||||
Left: error => throw new AssertionException($"Handler returned error: {error.Value}"));
|
||||
|
||||
// Every XML-special char in the token must be escaped; the raw token must never reach the output.
|
||||
xml.ShouldContain("access_token=tok&<>"");
|
||||
xml.ShouldNotContain("access_token=tok&<");
|
||||
}
|
||||
|
||||
// --- harness ---
|
||||
|
||||
private async Task Verify(string goldenName, GetChannelGuide request)
|
||||
|
||||
Reference in New Issue
Block a user