fix(seo): escape inline JSON-LD, per-release byArtist, soft-404 + env-gated noindex
Escape </>& in JSON-LD body to kill script-breakout; byArtist now uses the release artist; detail-page not-found branches emit noindex; default robots gated to Production via a PersistentState SeoEnvironment bridge.
This commit is contained in:
@@ -22,13 +22,30 @@ public static class SeoJsonLd
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
// schema.org keys are PascalCase ("@type", "byArtist", "datePublished"); JsonPropertyName drives
|
||||
// each. Encoder relaxed so the JSON sits inline in HTML without over-escaping apostrophes etc.
|
||||
// Note: the relaxed encoder leaves <, >, & raw — InlineSafe re-escapes exactly those before the
|
||||
// body is injected into the <script> element. See Serialize.
|
||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
WriteIndented = false,
|
||||
};
|
||||
|
||||
/// <summary>Renders a node to its compact JSON-LD script body. The host component wraps it in the script tag.</summary>
|
||||
/// <summary>
|
||||
/// Renders a node to its compact JSON-LD script body. The host component wraps it in the script tag.
|
||||
/// The body is run through <see cref="InlineSafe"/> so CMS-authored values containing
|
||||
/// <c></script></c> or <c><</c> cannot break out of the inline script element (XSS).
|
||||
/// </summary>
|
||||
public static string Serialize<TNode>(TNode node) where TNode : JsonLdNode =>
|
||||
JsonSerializer.Serialize(node, node.GetType(), Options);
|
||||
InlineSafe(JsonSerializer.Serialize(node, node.GetType(), Options));
|
||||
|
||||
/// <summary>
|
||||
/// Escapes the three characters that can break out of an inline <c><script type="application/ld+json"></c>
|
||||
/// element. Replacing <c><</c>/<c>></c>/<c>&</c> with their <c>\uXXXX</c> JSON escapes keeps the
|
||||
/// JSON byte-for-byte equivalent on parse (a JSON string treats <c><</c> and <c><</c> identically)
|
||||
/// while making <c></script></c> impossible to emit raw — the documented safe pattern for inline JSON-LD.
|
||||
/// </summary>
|
||||
internal static string InlineSafe(string json) => json
|
||||
.Replace("<", "\\u003C")
|
||||
.Replace(">", "\\u003E")
|
||||
.Replace("&", "\\u0026");
|
||||
}
|
||||
|
||||
/// <summary>Base for every schema.org node: emits <c>@context</c> and <c>@type</c> first.</summary>
|
||||
|
||||
Reference in New Issue
Block a user