Defang / Refang — C# source
Make URLs, IPs, and emails safe to paste by defanging indicators (https:// → hxxps[://], . → [.], @ → [@]) - and reverse refanged text back to its original form. Runs entirely in your browser, with a shareable link to your exact input and mode.
This is the C# implementation — the same logic the interactive tool runs, in a shareable, citable form.
// defang-refang — make URLs, IPs, and emails safe to paste (and reverse it).
//
// Language: C# (C# 12 / .NET 8, standard library only)
// Source: CosmoDev polyglot showcase port of the Defang/Refang tool, ported
// from src/lib/defang.ts (the canonical TypeScript implementation)
// and held in lock-step with cli/defang-refang/defang-refang.go.
// License: display source — part of CosmoDev's polyglot tool pages.
//
// Design goals:
// - Pure + deterministic; never throws for any string input.
// - Functionally equivalent to the TS / Go reference: same inputs -> same outputs.
// - Self-contained: string.Replace(String, String) replaces every occurrence
// natively — the closest one-call equivalent of the TS replaceAll.
//
// Algorithm: Defang replaces "https://" / "http://" schemes with "hxxps[://]"
// / "hxxp[://]", then brackets EVERY dot as "[.]" and every at-sign as "[@]" —
// the standard threat-intel indicator-defanging convention. Refang is its
// exact inverse: scheme markers are restored first, then "[.]" / "[@]" are
// unbracketed, so Refang(Defang(x)) == x for typical URLs, IPs, emails, and
// plain text.
//
// Ordering note: scheme replacements run BEFORE dot/at bracketing so the dots
// and colon inside the resulting "[://]" marker are not themselves re-bracketed
// (which would corrupt the scheme marker). Note that EVERY dot is bracketed,
// not just the domain separator — so Defang is NOT idempotent by design: apply
// it once to fresh text.
using System;
namespace CosmoDev.Polyglot;
/// <summary>
/// Make URLs, IPs, and emails safe to paste (and reverse it).
/// </summary>
public static class DefangRefang
{
/// <summary>
/// Defang text so URLs, IPs, and emails are no longer clickable or auto-linked.
/// <para>
/// Mirrors <c>defang()</c> in src/lib/defang.ts and <c>Defang()</c> in the Go
/// twin, and must agree with both on every shared vector. Scheme replacements
/// run first so the dots and colon inside the resulting <c>[://]</c> marker are
/// not themselves re-bracketed. EVERY dot is bracketed, not just the domain
/// separator. Empty input returns empty; text with no indicators passes
/// through unchanged.
/// </para>
/// </summary>
public static string Defang(string text) =>
text
.Replace("https://", "hxxps[://]")
.Replace("http://", "hxxp[://]")
.Replace(".", "[.]")
.Replace("@", "[@]");
/// <summary>
/// Reverse defanging — restore "hxxps[://]" / "hxxp[://]" to real schemes and
/// unbracket "[.]" / "[@]": the exact inverse of <see cref="Defang"/>.
/// <para>
/// Scheme markers are restored before "[.]" / "[@]" are unbracketed, so a
/// marker like "hxxps[://]" reassembles into "https://" rather than being
/// split by an earlier dot/at substitution. Empty input returns empty.
/// </para>
/// </summary>
public static string Refang(string text) =>
text
.Replace("hxxps[://]", "https://")
.Replace("hxxp[://]", "http://")
.Replace("[.]", ".")
.Replace("[@]", "@");
}
// ---------- tests (showcase-only; the canonical suite lives in src/lib) ----------
namespace CosmoDev.Polyglot
{
internal static class DefangRefangShowcase
{
private static int failures;
private static void Expect(string got, string want, string label)
{
if (got != want)
{
Console.Error.WriteLine($"FAIL {label}:\n got \"{got}\"\n want \"{want}\"");
failures++;
}
}
private static int Main()
{
// defangs url, email, and ip
Expect(DefangRefang.Defang("https://example.com"), "hxxps[://]example[.]com", "defang url");
Expect(DefangRefang.Defang("user@example.com"), "user[@]example[.]com", "defang email");
Expect(DefangRefang.Defang("192.168.1.1"), "192[.]168[.]1[.]1", "defang ip");
// defangs mixed text
Expect(
DefangRefang.Defang("contact admin@site.co.uk or visit http://site.co.uk"),
"contact admin[@]site[.]co[.]uk or visit hxxp[://]site[.]co[.]uk",
"defang mixed");
// applies scheme replacement before dot bracketing
string got = DefangRefang.Defang("https://a.com");
Expect(got, "hxxps[://]a[.]com", "scheme first");
if (got.Contains("https[://]") || got.Contains("hxxps://"))
{
Console.Error.WriteLine($"FAIL scheme marker corrupted: \"{got}\"");
failures++;
}
// refangs and round-trips
Expect(DefangRefang.Refang("hxxps[://]example[.]com"), "https://example.com", "refang https");
Expect(DefangRefang.Refang("hxxp[://]single[.]com"), "http://single.com", "refang http");
// hxxp and hxxps are distinct markers — never confused.
Expect(DefangRefang.Refang("hxxps[://]secure[.]com"), "https://secure.com", "refang hxxps distinct");
// Round-trip identity for typical URLs, IPs, emails, and plain text.
foreach (string x in new[]
{
"https://sub.example.com/path?q=1&a=b",
"first.last@sub.example.co.uk",
"172.16.254.1",
"just a sentence with no indicators",
})
{
Expect(DefangRefang.Refang(DefangRefang.Defang(x)), x, "round trip");
}
// empty and passthrough
Expect(DefangRefang.Defang(""), "", "defang empty");
Expect(DefangRefang.Refang(""), "", "refang empty");
Expect(DefangRefang.Defang("nothing to transform here"), "nothing to transform here", "defang passthrough");
Expect(DefangRefang.Refang("no indicators here"), "no indicators here", "refang passthrough");
if (failures > 0)
{
Console.Error.WriteLine($"{failures} check(s) failed");
return 1;
}
Console.WriteLine("all checks passed");
return 0;
}
}
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →