Fix emoji encoding and improve universal decoder

This commit is contained in:
EP
2025-03-09 18:45:56 -04:00
parent 1637dd3603
commit f65235c5d2
5 changed files with 227 additions and 35 deletions
+34
View File
@@ -0,0 +1,34 @@
/* Copy notification styles */
.copy-notification {
position: fixed;
bottom: 20px;
right: 20px;
background-color: #25282c;
color: #00FF41;
padding: 10px 20px;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
z-index: 1000;
display: flex;
align-items: center;
gap: 10px;
animation: fade-in 0.3s ease-in-out;
}
.copy-notification.error {
color: #ff4141;
}
.copy-notification.fade-out {
animation: fade-out 0.3s ease-in-out forwards;
}
@keyframes fade-in {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fade-out {
from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(20px); }
}
+1
View File
@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Parseltongue 2.0 - LLM Payload Crafter</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/notification.css">
<!-- Vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<!-- Font Awesome for icons -->
+117 -16
View File
@@ -83,23 +83,15 @@ new Vue({
return;
}
// Try invisible text decoding
let decoded = window.steganography.decodeInvisible(this.decodeInput);
if (decoded) {
this.decodedMessage = decoded;
this.copyToClipboard(decoded);
return;
// Use the universal decoder
const result = this.universalDecode(this.decodeInput);
if (result) {
this.decodedMessage = `Decoded (${result.method}): ${result.text}`;
this.copyToClipboard(result.text);
} else {
this.decodedMessage = 'No encoded message detected';
}
// Try emoji decoding
decoded = window.steganography.decodeEmoji(this.decodeInput);
if (decoded) {
this.decodedMessage = decoded;
this.copyToClipboard(decoded);
return;
}
this.decodedMessage = 'No hidden message found';
},
previewInvisible(text) {
return '[invisible]';
@@ -107,11 +99,120 @@ new Vue({
// Utility Methods
async copyToClipboard(text) {
if (!text) return;
try {
await navigator.clipboard.writeText(text);
// Show a brief notification
const notification = document.createElement('div');
notification.className = 'copy-notification';
notification.innerHTML = '<i class="fas fa-check"></i> Copied!';
document.body.appendChild(notification);
// Remove after animation
setTimeout(() => {
notification.classList.add('fade-out');
setTimeout(() => document.body.removeChild(notification), 300);
}, 1000);
} catch (err) {
console.error('Failed to copy text:', err);
// Show error notification
const notification = document.createElement('div');
notification.className = 'copy-notification error';
notification.innerHTML = '<i class="fas fa-times"></i> Copy failed';
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('fade-out');
setTimeout(() => document.body.removeChild(notification), 300);
}, 1000);
}
},
// Universal Decoder - tries all decoding methods
universalDecode(input) {
if (!input) return '';
// Try all decoders in order
// 1. Try steganography decoders
// - Check for emoji steganography first
// The emoji encoding uses variation selectors which are hard to see
if (/[\u{1F300}-\u{1F6FF}\u{2600}-\u{26FF}]/u.test(input)) {
const decoded = window.steganography.decodeEmoji(input);
if (decoded) {
return { text: decoded, method: 'Emoji Steganography' };
}
}
// - Invisible text
let decoded = window.steganography.decodeInvisible(input);
if (decoded) {
return { text: decoded, method: 'Invisible Text' };
}
// 2. Try transform reversals
// - Binary
if (/^[01\s]+$/.test(input.trim())) {
try {
// Use binary transform's reverse function if available
if (window.transforms.binary && window.transforms.binary.reverse) {
const result = window.transforms.binary.reverse(input);
if (result && /[\x20-\x7E]/.test(result)) { // Make sure it's readable ASCII
return { text: result, method: 'Binary' };
}
} else {
// Fallback implementation
const binText = input.replace(/\s+/g, '');
let result = '';
for (let i = 0; i < binText.length; i += 8) {
const byte = binText.substr(i, 8);
if (byte.length === 8) {
result += String.fromCharCode(parseInt(byte, 2));
}
}
if (result && /[\x20-\x7E]/.test(result)) { // Make sure it's readable ASCII
return { text: result, method: 'Binary' };
}
}
} catch (e) {
console.error('Binary decode error:', e);
}
}
// - Morse code
if (/^[.\-\s\/]+$/.test(input.trim())) {
try {
// Use morse transform's reverse function if available
if (window.transforms.morse && window.transforms.morse.reverse) {
const result = window.transforms.morse.reverse(input);
if (result !== input && /[a-zA-Z0-9]/.test(result)) {
return { text: result, method: 'Morse Code' };
}
}
} catch (e) {
console.error('Morse decode error:', e);
}
}
// - Try reverse each transform
for (const name in window.transforms) {
const transform = window.transforms[name];
if (transform.reverse) {
try {
const result = transform.reverse(input);
// Only return if the result is different and contains readable characters
if (result !== input && /[a-zA-Z0-9\s]/.test(result)) {
return { text: result, method: transform.name };
}
} catch (e) {
console.error(`Error decoding with ${name}:`, e);
}
}
}
return null;
}
},
// Initialize theme
+27 -17
View File
@@ -6,24 +6,22 @@ const carriers = [
{ emoji: '🐊', name: 'CROCODILE', desc: 'Dangerous Croc', preview: text => `🐊${text}` }
];
// Variation selector functions
function toVariationSelector(byte) {
return String.fromCodePoint(0xFE00 + byte);
}
function fromVariationSelector(codePoint) {
return codePoint - 0xFE00;
}
// Emoji encoding/decoding
function encodeEmoji(emoji, text) {
if (!text) return '';
if (!text) return emoji;
// Convert text to binary string
const binary = Array.from(text)
.map(c => c.charCodeAt(0).toString(2).padStart(8, '0'))
.join('');
// Use variation selectors to encode binary
const vs15 = '\ufe0e'; // text variation selector (0)
const vs16 = '\ufe0f'; // emoji variation selector (1)
const bytes = new TextEncoder().encode(text);
let result = emoji;
for (const byte of bytes) {
result += toVariationSelector(byte);
for (const bit of binary) {
result += bit === '0' ? vs15 : vs16;
}
return result;
@@ -32,11 +30,23 @@ function encodeEmoji(emoji, text) {
function decodeEmoji(text) {
if (!text) return '';
const matches = [...text.matchAll(/[\uFE00-\uFE0F]/g)];
// Extract variation selectors
const matches = [...text.matchAll(/[\ufe0e\ufe0f]/g)];
if (!matches.length) return '';
const bytes = new Uint8Array(matches.map(m => fromVariationSelector(m[0].codePointAt(0))));
return new TextDecoder().decode(bytes);
// Convert variation selectors to binary
const binary = matches.map(m => m[0] === '\ufe0e' ? '0' : '1').join('');
// Convert binary to text
let decoded = '';
for (let i = 0; i < binary.length; i += 8) {
const byte = binary.slice(i, i + 8);
if (byte.length === 8) {
decoded += String.fromCharCode(parseInt(byte, 2));
}
}
return decoded;
}
// Invisible text encoding/decoding
+48 -2
View File
@@ -15,11 +15,23 @@ const transforms = {
'(': ')', ')': '(', '[': ']', ']': '[', '{': '}', '}': '{', '<': '>', '>': '<',
'&': '⅋', '_': '‾'
},
// Create reverse map for decoding
reverseMap: function() {
const revMap = {};
for (const [key, value] of Object.entries(this.map)) {
revMap[value] = key;
}
return revMap;
},
func: function(text) {
return [...text].map(c => this.map[c] || c).reverse().join('');
},
preview: function(text) {
return this.func(text);
},
reverse: function(text) {
const revMap = this.reverseMap();
return [...text].map(c => revMap[c] || c).reverse().join('');
}
},
@@ -130,11 +142,29 @@ const transforms = {
'3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...',
'8': '---..', '9': '----.'
},
func: function(text) {
return [...text.toLowerCase()].map(c => this.map[c] || c).join(' ');
// Create reverse map for decoding
reverseMap: function() {
const revMap = {};
for (const [key, value] of Object.entries(this.map)) {
revMap[value] = key;
}
return revMap;
},
func: function(text, decode = false) {
if (decode) {
// Decode mode
const revMap = this.reverseMap();
return text.split(/\s+/).map(c => revMap[c] || c).join('');
} else {
// Encode mode
return [...text.toLowerCase()].map(c => this.map[c] || c).join(' ');
}
},
preview: function(text) {
return this.func(text);
},
reverse: function(text) {
return this.func(text, true);
}
},
@@ -145,8 +175,24 @@ const transforms = {
},
preview: function(text) {
return this.func(text);
},
reverse: function(text) {
// Remove spaces and ensure we have valid binary
const binText = text.replace(/\s+/g, '');
let result = '';
// Process 8 bits at a time
for (let i = 0; i < binText.length; i += 8) {
const byte = binText.substr(i, 8);
if (byte.length === 8) {
result += String.fromCharCode(parseInt(byte, 2));
}
}
return result;
}
}
// Note: other transforms don't have reverse functions because they're not easily reversible
// The universal decoder will only try to reverse transforms that have a reverse function
};
// Export transforms for use in app.js