Enhance JWT signing key validation and improve FfmpegBumperRenderer label handling: add checks for signing key length and placeholder values in DependencyInjection, and refactor label processing to read from files in FfmpegBumperRenderer. Update MediaProcessingBackgroundService to ensure slot release on claim failure.
This commit is contained in:
@@ -66,6 +66,19 @@ public static class DependencyInjection
|
||||
configuration.GetSection(JwtOptions.SectionName).Get<JwtOptions>()
|
||||
?? throw new InvalidOperationException("Секция конфигурации 'Jwt' не задана.");
|
||||
|
||||
// Fail-fast на подписывающем ключе: этот же ключ подписывает и JWT, и stream-токены
|
||||
// (StreamTokenService), поэтому placeholder/короткий ключ из appsettings.json = полный обход
|
||||
// авторизации (можно сфорджить admin-JWT). Лучше не стартовать вовсе, чем стартовать уязвимым.
|
||||
// HMAC-SHA256 требует ключ не короче размера хеша (32 байта), иначе он ослаблен нулевым паддингом.
|
||||
if (Encoding.UTF8.GetByteCount(jwtOptions.SigningKey) < 32)
|
||||
throw new InvalidOperationException(
|
||||
"Jwt:SigningKey должен быть не короче 32 байт. Задайте криптостойкий секрет через конфигурацию/переменную окружения Jwt__SigningKey."
|
||||
);
|
||||
if (jwtOptions.SigningKey.Contains("change-me", StringComparison.OrdinalIgnoreCase))
|
||||
throw new InvalidOperationException(
|
||||
"Jwt:SigningKey использует значение-заглушку из appsettings.json. Переопределите его криптостойким секретом (Jwt__SigningKey)."
|
||||
);
|
||||
|
||||
services
|
||||
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
|
||||
@@ -44,9 +44,40 @@ public sealed class FfmpegBumperRenderer(
|
||||
await File.WriteAllTextAsync(nowFile, line1, new UTF8Encoding(false), cancellationToken);
|
||||
await File.WriteAllTextAsync(nextFile, line2, new UTF8Encoding(false), cancellationToken);
|
||||
|
||||
// Подписи «Сейчас/Далее» тоже пользователь-редактируемы (валидатор ограничивает только длину),
|
||||
// поэтому их так же читаем через textfile=, а не подставляем в text= инлайн: иначе запятая/`;`/`[`/`]`
|
||||
// в подписи ломают (или инъектируют звенья в) цепочку -filter_complex. Нужны лишь в режиме
|
||||
// «Сейчас/Далее» (не FreeText), где рисуются подписи.
|
||||
var nowLabelFile = Path.Combine(assetDir, "nowlabel.txt");
|
||||
var nextLabelFile = Path.Combine(assetDir, "nextlabel.txt");
|
||||
if (!spec.FreeText)
|
||||
{
|
||||
await File.WriteAllTextAsync(
|
||||
nowLabelFile,
|
||||
spec.NowLabel,
|
||||
new UTF8Encoding(false),
|
||||
cancellationToken
|
||||
);
|
||||
await File.WriteAllTextAsync(
|
||||
nextLabelFile,
|
||||
spec.NextLabel,
|
||||
new UTF8Encoding(false),
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var args = BuildArgs(assetDir, seg, target, nowFile, nextFile, spec);
|
||||
var args = BuildArgs(
|
||||
assetDir,
|
||||
seg,
|
||||
target,
|
||||
nowFile,
|
||||
nextFile,
|
||||
nowLabelFile,
|
||||
nextLabelFile,
|
||||
spec
|
||||
);
|
||||
var result = await ProcessRunner.RunAsync(
|
||||
_media.FfmpegPath,
|
||||
args,
|
||||
@@ -83,6 +114,8 @@ public sealed class FfmpegBumperRenderer(
|
||||
{
|
||||
TryDelete(nowFile);
|
||||
TryDelete(nextFile);
|
||||
TryDelete(nowLabelFile);
|
||||
TryDelete(nextLabelFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +125,8 @@ public sealed class FfmpegBumperRenderer(
|
||||
int target,
|
||||
string nowFile,
|
||||
string nextFile,
|
||||
string nowLabelFile,
|
||||
string nextLabelFile,
|
||||
BumperRenderSpec spec
|
||||
)
|
||||
{
|
||||
@@ -186,7 +221,7 @@ public sealed class FfmpegBumperRenderer(
|
||||
vchain
|
||||
.Append(',')
|
||||
.Append(
|
||||
DrawLabel(font, spec.NowLabel, spec.AccentColor, labelSize, nowLabelY, 0.2)
|
||||
DrawLabel(font, nowLabelFile, spec.AccentColor, labelSize, nowLabelY, 0.2)
|
||||
);
|
||||
vchain
|
||||
.Append(',')
|
||||
@@ -194,7 +229,7 @@ public sealed class FfmpegBumperRenderer(
|
||||
vchain
|
||||
.Append(',')
|
||||
.Append(
|
||||
DrawLabel(font, spec.NextLabel, spec.AccentColor, labelSize, nextLabelY, 1.0)
|
||||
DrawLabel(font, nextLabelFile, spec.AccentColor, labelSize, nextLabelY, 1.0)
|
||||
);
|
||||
vchain
|
||||
.Append(',')
|
||||
@@ -284,13 +319,15 @@ public sealed class FfmpegBumperRenderer(
|
||||
|
||||
private static string DrawLabel(
|
||||
string font,
|
||||
string text,
|
||||
string textFile,
|
||||
string color,
|
||||
int size,
|
||||
int y,
|
||||
double fadeStart
|
||||
) =>
|
||||
$"drawtext=fontfile={font}:text={EscapeText(text)}:expansion=none"
|
||||
// Подпись читается из файла (textfile=) с expansion=none — произвольные символы подписи
|
||||
// не могут сломать/инъектировать цепочку filter_complex (см. запись файлов в RenderAsync).
|
||||
$"drawtext=fontfile={font}:textfile={EscapePath(textFile)}:expansion=none"
|
||||
+ $":fontcolor={color}:fontsize={size}:x=(w-text_w)/2:y={y}"
|
||||
+ ":shadowcolor=black@0.6:shadowx=1:shadowy=1"
|
||||
+ $":alpha='{FadeExpr(fadeStart)}'";
|
||||
@@ -302,10 +339,6 @@ public sealed class FfmpegBumperRenderer(
|
||||
/// двоеточие экранируется). На Linux (контейнере) — фактически no-op.</summary>
|
||||
private static string EscapePath(string path) => path.Replace('\\', '/').Replace(":", "\\:");
|
||||
|
||||
/// <summary>Экранирование литерального текста подписи внутри значения опции drawtext.</summary>
|
||||
private static string EscapeText(string text) =>
|
||||
text.Replace("\\", "\\\\").Replace(":", "\\:").Replace("'", "\\'");
|
||||
|
||||
private static string Fmt(double value) =>
|
||||
value.ToString("0.###", CultureInfo.InvariantCulture);
|
||||
|
||||
|
||||
@@ -50,7 +50,21 @@ public sealed class MediaProcessingBackgroundService(
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
await slots.WaitAsync(stoppingToken);
|
||||
var claim = await ClaimNextAsync(stoppingToken);
|
||||
|
||||
// Слот уже захвачен — любой сбой захвата ассета (транзиентная ошибка БД и т.п.)
|
||||
// обязан вернуть слот, иначе после нескольких ошибок семафор исчерпается и
|
||||
// диспетчер зависнет навсегда (сервис формально жив, но ничего не обрабатывает).
|
||||
(Guid Id, string Extension)? claim;
|
||||
try
|
||||
{
|
||||
claim = await ClaimNextAsync(stoppingToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
slots.Release();
|
||||
throw;
|
||||
}
|
||||
|
||||
if (claim is not { } job)
|
||||
{
|
||||
slots.Release();
|
||||
|
||||
Reference in New Issue
Block a user