mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
* feat: improvements covering solana nfts(send, ownership badge, persistent imports etc), closing emptied token accounts and other fixes from patch * feat: more improvements for nfts including security hardening * fix: display error in full
90 lines
3.2 KiB
Dart
90 lines
3.2 KiB
Dart
import "package:cw_core/utils/nft_text.dart";
|
|
import "package:flutter_test/flutter_test.dart";
|
|
|
|
void main() {
|
|
group("sanitizeNFTText", () {
|
|
test("removes the separators that would fake extra lines", () {
|
|
expect(sanitizeNFTText("Ape\u2028Address: 9xQeW"), "Ape Address: 9xQeW");
|
|
expect(sanitizeNFTText("Ape\u2029Address: 9xQeW"), "Ape Address: 9xQeW");
|
|
expect(sanitizeNFTText("Ape\nAddress: 9xQeW"), "Ape Address: 9xQeW");
|
|
});
|
|
|
|
test("removes bidi overrides and isolates that reorder what follows", () {
|
|
expect(sanitizeNFTText("evil\u202Etxen"), "evil txen");
|
|
expect(sanitizeNFTText("a\u2066b\u2069c"), "a b c");
|
|
});
|
|
|
|
test("removes invisibles that hide or reorder characters", () {
|
|
expect(sanitizeNFTText("A\u200BB"), "A B");
|
|
expect(sanitizeNFTText("A\u200FB"), "A B");
|
|
expect(sanitizeNFTText("A\u061CB"), "A B");
|
|
expect(sanitizeNFTText("A\u2060B"), "A B");
|
|
});
|
|
|
|
test("removes control characters and delete", () {
|
|
expect(sanitizeNFTText("A\u0000B"), "A B");
|
|
expect(sanitizeNFTText("A\u001FB"), "A B");
|
|
expect(sanitizeNFTText("A\u007FB"), "A B");
|
|
});
|
|
|
|
test("leaves ordinary text alone", () {
|
|
expect(sanitizeNFTText("DeGod #1234"), "DeGod #1234");
|
|
expect(sanitizeNFTText(" padded "), "padded");
|
|
});
|
|
|
|
test("returns null for null", () {
|
|
expect(sanitizeNFTText(null), isNull);
|
|
});
|
|
|
|
test("counts the cap in characters, so emoji are not cut in half", () {
|
|
final long = sanitizeNFTText("\u{1F600}" * 600)!;
|
|
|
|
expect(long.runes.length, 513);
|
|
expect(long.runes.last, 0x2026);
|
|
|
|
final units = long.codeUnits;
|
|
final endsOnHighSurrogate = units.isNotEmpty && units.last >= 0xD800 && units.last <= 0xDBFF;
|
|
expect(endsOnHighSurrogate, isFalse);
|
|
});
|
|
|
|
test("does not truncate a name that is long in code units but short in characters", () {
|
|
expect(sanitizeNFTText("\u{1F600}" * 400)!.runes.length, 400);
|
|
});
|
|
|
|
test("is idempotent, so re-reading a saved value does not change it", () {
|
|
const raw = "Ape\u2028Name\u202E with \u{1F600}";
|
|
final once = sanitizeNFTText(raw);
|
|
|
|
expect(sanitizeNFTText(once), once);
|
|
});
|
|
|
|
test("keeps the joiners that carry meaning in persian and emoji", () {
|
|
// Replacing these put a visible space inside a Persian word and split
|
|
// a two-person emoji into two glyphs.
|
|
expect(sanitizeNFTText("\u{1F468}\u200D\u{1F4BB}"), "\u{1F468}\u200D\u{1F4BB}");
|
|
expect(
|
|
sanitizeNFTText("\u0645\u06CC\u200C\u062E\u0648\u0627\u0647\u0645"),
|
|
"\u0645\u06CC\u200C\u062E\u0648\u0627\u0647\u0645",
|
|
);
|
|
});
|
|
|
|
test("strips the invisibles a mint can hide text behind", () {
|
|
for (final invisible in [
|
|
"\u00AD", // soft hyphen
|
|
"\u034F", // combining grapheme joiner
|
|
"\u180E", // mongolian vowel separator
|
|
"\u3164", // hangul filler
|
|
"\uFEFF", // zero width no-break space
|
|
"\uFFA0", // halfwidth hangul filler
|
|
"\u{E0041}", // tag latin capital A
|
|
]) {
|
|
expect(
|
|
sanitizeNFTText("A${invisible}B"),
|
|
"A B",
|
|
reason: "U+${invisible.runes.first.toRadixString(16).toUpperCase()} survived",
|
|
);
|
|
}
|
|
});
|
|
});
|
|
}
|