mojic

Obfuscate C source code into e...
Log | Files | Refs | README | LICENSE

commit 83249bb9c45eaf36c47b80b347fcdd6431e52415
parent 591699709409ddae146547fcefd0d3e1622fab08
Author: Amit Dutta <amitdutta4255@gmail.com>
Date:   Wed, 28 Jan 2026 14:30:26 +0530

Uploaded to Github Package

Diffstat:
Mbin/mojic.js | 2+-
Dnode_modules/.bin/mojic | 16----------------
Dnode_modules/.bin/mojic.cmd | 17-----------------
Dnode_modules/.bin/mojic.ps1 | 28----------------------------
Mnode_modules/.package-lock.json | 19+------------------
Dnode_modules/mojic/CODE_OF_CONDUCT.md | 34----------------------------------
Dnode_modules/mojic/CONTRIBUTING.md | 38--------------------------------------
Dnode_modules/mojic/LICENSE | 202-------------------------------------------------------------------------------
Dnode_modules/mojic/README.md | 67-------------------------------------------------------------------
Dnode_modules/mojic/SECURITY.md | 13-------------
Dnode_modules/mojic/bin/mojic.js | 341-------------------------------------------------------------------------------
Dnode_modules/mojic/lib/CipherEngine.js | 254-------------------------------------------------------------------------------
Dnode_modules/mojic/package.json | 32--------------------------------
Mpackage-lock.json | 24+++---------------------
Mpackage.json | 14+++++++++-----
15 files changed, 14 insertions(+), 1087 deletions(-)

diff --git a/bin/mojic.js b/bin/mojic.js @@ -12,7 +12,7 @@ import { CipherEngine } from '../lib/CipherEngine.js'; program .name('mojic') .description('Obfuscate C source code into emojis') - .version('1.0.2') + .version('1.0.3') .addHelpCommand('help [command]', 'Display help for command') .showHelpAfterError(); diff --git a/node_modules/.bin/mojic b/node_modules/.bin/mojic @@ -1,16 +0,0 @@ -#!/bin/sh -basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')") - -case `uname` in - *CYGWIN*|*MINGW*|*MSYS*) - if command -v cygpath > /dev/null 2>&1; then - basedir=`cygpath -w "$basedir"` - fi - ;; -esac - -if [ -x "$basedir/node" ]; then - exec "$basedir/node" "$basedir/../mojic/bin/mojic.js" "$@" -else - exec node "$basedir/../mojic/bin/mojic.js" "$@" -fi diff --git a/node_modules/.bin/mojic.cmd b/node_modules/.bin/mojic.cmd @@ -1,17 +0,0 @@ -@ECHO off -GOTO start -:find_dp0 -SET dp0=%~dp0 -EXIT /b -:start -SETLOCAL -CALL :find_dp0 - -IF EXIST "%dp0%\node.exe" ( - SET "_prog=%dp0%\node.exe" -) ELSE ( - SET "_prog=node" - SET PATHEXT=%PATHEXT:;.JS;=;% -) - -endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\mojic\bin\mojic.js" %* diff --git a/node_modules/.bin/mojic.ps1 b/node_modules/.bin/mojic.ps1 @@ -1,28 +0,0 @@ -#!/usr/bin/env pwsh -$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent - -$exe="" -if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { - # Fix case when both the Windows and Linux builds of Node - # are installed in the same directory - $exe=".exe" -} -$ret=0 -if (Test-Path "$basedir/node$exe") { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & "$basedir/node$exe" "$basedir/../mojic/bin/mojic.js" $args - } else { - & "$basedir/node$exe" "$basedir/../mojic/bin/mojic.js" $args - } - $ret=$LASTEXITCODE -} else { - # Support pipeline input - if ($MyInvocation.ExpectingInput) { - $input | & "node$exe" "$basedir/../mojic/bin/mojic.js" $args - } else { - & "node$exe" "$basedir/../mojic/bin/mojic.js" $args - } - $ret=$LASTEXITCODE -} -exit $ret diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json @@ -1,6 +1,6 @@ { "name": "mojic", - "version": "1.0.2", + "version": "1.0.3", "lockfileVersion": 3, "requires": true, "packages": { @@ -378,23 +378,6 @@ "node": ">=6" } }, - "node_modules/mojic": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/mojic/-/mojic-1.0.2.tgz", - "integrity": "sha512-p1J/707UtPmTRMCWNAaYwOAWjTWkuTIThLTkE2SsDebDbJd3mtbk/Yqe6odsXP09RJ192GTfnKXVqNE+YBcsLA==", - "license": "Apache-2.0", - "dependencies": { - "chalk": "^5.3.0", - "commander": "^11.1.0", - "inquirer": "^9.2.12" - }, - "bin": { - "mojic": "bin/mojic.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/mute-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", diff --git a/node_modules/mojic/CODE_OF_CONDUCT.md b/node_modules/mojic/CODE_OF_CONDUCT.md @@ -1,34 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at **amitdutta4255@gmail.com** or **mail@amit.is-a.dev**. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/node_modules/mojic/CONTRIBUTING.md b/node_modules/mojic/CONTRIBUTING.md @@ -1,38 +0,0 @@ -# Contributing to Mojic - -First off, thanks for taking the time to contribute! ๐ŸŽ‰ - -The following is a set of guidelines for contributing to Mojic. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. - -## How Can I Contribute? - -### Reporting Bugs - -This section guides you through submitting a bug report for Mojic. Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports. - -* **Use a clear and descriptive title** for the issue to identify the problem. -* **Describe the exact steps which reproduce the problem** in as many details as possible. -* **Provide specific examples** to demonstrate the steps. - -### Suggesting Enhancements - -This section guides you through submitting an enhancement suggestion for Mojic, including completely new features and minor improvements to existing functionality. - -* **Use a clear and descriptive title** for the issue to identify the suggestion. -* **Provide a step-by-step description of the suggested enhancement** in as many details as possible. -* **Explain why this enhancement would be useful** to most Mojic users. - -## Styleguides - -### Git Commit Messages - -* Use the present tense ("Add feature" not "Added feature") -* Use the imperative mood ("Move cursor to..." not "Moves cursor to...") -* Limit the first line to 72 characters or less - -### Mojic Code Style - -* **Cipher Logic:** Changes to `CipherEngine.js` must ensure backward compatibility with the header format. -* **Streams:** Always use `StringDecoder` when handling text streams to prevent multi-byte emoji corruption. - -Happy Hacking! ๐Ÿš€ diff --git a/node_modules/mojic/LICENSE b/node_modules/mojic/LICENSE @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License.- \ No newline at end of file diff --git a/node_modules/mojic/README.md b/node_modules/mojic/README.md @@ -1,67 +0,0 @@ -# Mojic ๐Ÿ”’๐Ÿ”ฎ - -> **Obfuscate C source code into a randomized stream of emojis using password-seeded encryption.** - -**Mojic** (Magic + Emoji + Logic) is a CLI tool that transforms readable C code into an unreadable mess of emojis. Unlike simple substitution ciphers, Mojic uses your password to seed a Pseudo-Random Number Generator (PRNG), creating a unique "Emoji Universe" for every password. - -## ๐Ÿš€ Features - -* **๐Ÿ” Seeded Shuffling:** Your password derives a seed that shuffles the emoji mapping. Two different passwords will produce completely different emoji outputs for the same file. -* **๐ŸŒš Moon Header Protocol:** The file header (Salt + Auth Hash) is encoded using only Moon and Clock emojis to allow metadata reading before decryption. -* **๐Ÿ“‚ Recursive Processing:** Encrypt or decrypt entire project directories with one flag. -* **๐Ÿ”„ Security Rotation:** Rotate passwords or re-scramble the entropy of encrypted files without seeing the plaintext. -* **โšก Stream Based:** Handles large files efficiently using Node.js streams. - -## ๐Ÿ“ฆ Installation - -```bash -npm install -g mojic -``` - -## ๐Ÿ› ๏ธ Usage - -### 1. Encrypting Code (`encode`) -Turns a `.c` file into a `.mojic` file. - -```bash -# Encrypt a single file -mojic encode main.c - -# Encrypt an entire directory recursively -mojic encode ./src -r -``` -*You will be prompted to create a password. This password is required to decrypt.* - -### 2. Decrypting Code (`decode`) -Restores the original C code from a `.mojic` file. - -```bash -# Decrypt a single file -mojic decode main.mojic - -# Decrypt an entire directory recursively -mojic decode ./src -r -``` - -### 3. Security & Rotation Tools (`srt`) -Manage encrypted files without revealing their contents. - -```bash -# Change the password of an encrypted file -mojic srt --pass secret.mojic - -# Re-shuffle the encryption (New Salt) with the SAME password -# (Useful if you want to change the visual emoji pattern without changing the password) -mojic srt --re secret.mojic -``` - -## ๐Ÿง  How it Works - -1. **Key Derivation:** Mojic uses **PBKDF2** to derive a 32-bit seed and an Auth Hash from your password and a random Salt. -2. **The Shuffle:** It uses a custom **Mulberry32 PRNG** seeded with your key to shuffle a "Universe" of ~1500 emojis. -3. **Tokenization:** C keywords (`int`, `return`, `void`) are tokenized and mapped to unique emojis from the shuffled universe. Remaining characters map to other emojis. -4. **The Header:** The Salt and Auth Hash are written to the top of the file using a fixed "Header Alphabet" (๐ŸŒ‘๐ŸŒ’๐ŸŒ“๐ŸŒ”...) so the tool can verify your password before attempting decryption. - -## ๐Ÿ“„ License - -This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details. diff --git a/node_modules/mojic/SECURITY.md b/node_modules/mojic/SECURITY.md @@ -1,13 +0,0 @@ -# Security Policy - -## Reporting a Vulnerability - -We take security seriously. - -If you discover a security vulnerability within Mojic (e.g., in the `CipherEngine` logic or the PRNG implementation), please send an e-mail to **amitdutta4255@gmail.com**. All security vulnerabilities will be promptly addressed. - -**Please do not open public issues for security vulnerabilities.** - -### Scope -* **Supported:** Issues regarding key derivation, salt collisions, or stream buffer overflows. -* **Not Supported:** Brute-forcing weak passwords (users are responsible for password strength). diff --git a/node_modules/mojic/bin/mojic.js b/node_modules/mojic/bin/mojic.js @@ -1,340 +0,0 @@ -#!/usr/bin/env node - -import { program } from 'commander'; -import inquirer from 'inquirer'; -import chalk from 'chalk'; -import fs from 'fs'; -import path from 'path'; -import { Transform } from 'stream'; -import { StringDecoder } from 'string_decoder'; -import { CipherEngine } from '../lib/CipherEngine.js'; - -program - .name('mojic') - .description('Obfuscate C source code into emojis') - .version('1.0.2') - .addHelpCommand('help [command]', 'Display help for command') - .showHelpAfterError(); - -// --- Helpers --- - -const promptPassword = async (msg) => { - const { password } = await inquirer.prompt([{ - type: 'password', name: 'password', message: msg, mask: '*', - validate: (input) => input.length > 5 || 'Password must be > 5 chars.' - }]); - return password; -}; - -const getStreamHeader = async (filePath) => { - const fileStream = fs.createReadStream(filePath, { highWaterMark: 1024 }); - const decoder = new StringDecoder('utf8'); - - return new Promise((resolve, reject) => { - let buffer = ''; - let found = false; - - fileStream.on('data', (chunk) => { - if (found) return; - buffer += decoder.write(chunk); - const newlineIndex = buffer.indexOf('\n'); - if (newlineIndex !== -1) { - found = true; - fileStream.destroy(); - resolve(buffer.substring(0, newlineIndex)); - } - }); - - fileStream.on('end', () => { - if (!found) resolve(buffer + decoder.end()); - }); - fileStream.on('error', reject); - }); -}; - -const createHeaderSkipper = () => { - const decoder = new StringDecoder('utf8'); - let isHeaderSkipped = false; - let buffer = ''; - - return new Transform({ - transform(chunk, encoding, cb) { - if (isHeaderSkipped) { - this.push(chunk); - return cb(); - } - - buffer += decoder.write(chunk); - const nlIndex = buffer.indexOf('\n'); - - if (nlIndex !== -1) { - const remainder = buffer.substring(nlIndex + 1); - this.push(remainder); - isHeaderSkipped = true; - buffer = ''; - } - cb(); - } - }); -}; - -// --- Recursive Logic --- - -async function traverseDirectory(currentPath, extension, callback) { - const entries = fs.readdirSync(currentPath, { withFileTypes: true }); - - for (const entry of entries) { - const fullPath = path.join(currentPath, entry.name); - if (entry.isDirectory()) { - await traverseDirectory(fullPath, extension, callback); - } else if (entry.isFile() && entry.name.endsWith(extension)) { - await callback(fullPath); - } - } -} - -// --- Main Commands --- - -program - .command('encode') - .argument('<path>', 'File or Directory to encode') - .option('-r, --recursive', 'Recursively encrypt all .c files in directory') - .description('Encrypt a file or directory into emojis') - .action(async (targetPath, options) => { - try { - if (!fs.existsSync(targetPath)) throw new Error('Path not found'); - const stats = fs.statSync(targetPath); - - // Validation - if (stats.isDirectory() && !options.recursive) { - throw new Error(`'${targetPath}' is a directory. Use -r to process recursively.`); - } - - console.log(chalk.blue('๐Ÿ”’ Initiating Mojic Encryption...')); - const password = await promptPassword('Create password for file(s):'); - - const processFile = async (filePath) => { - const outputName = filePath.replace(/\.c$/, '') + '.mojic'; - console.log(chalk.dim(` Processing: ${path.basename(filePath)} -> ${path.basename(outputName)}`)); - - const engine = new CipherEngine(password); - await engine.init(); // Unique salt per file - - const readStream = fs.createReadStream(filePath); - const writeStream = fs.createWriteStream(outputName); - - writeStream.write(engine._encodeHeader()); - - await new Promise((resolve, reject) => { - readStream - .pipe(engine.getEncryptStream()) - .pipe(writeStream) - .on('finish', resolve) - .on('error', reject); - }); - }; - - if (stats.isDirectory()) { - console.log(chalk.blue(`๐Ÿ“‚ Scanning directory: ${targetPath}`)); - await traverseDirectory(targetPath, '.c', processFile); - console.log(chalk.green('โœ… Batch encryption complete.')); - } else { - await processFile(targetPath); - console.log(chalk.green(`โœ… Encrypted.`)); - } - - } catch (err) { - console.error(chalk.red('Error:'), err.message); - } - }); - -program - .command('decode') - .argument('<path>', 'File or Directory to decode') - .option('-r, --recursive', 'Recursively decode all .mojic files') - .description('Restore mojic files to C source code') - .action(async (targetPath, options) => { - try { - if (!fs.existsSync(targetPath)) throw new Error('Path not found'); - const stats = fs.statSync(targetPath); - - if (stats.isDirectory() && !options.recursive) { - throw new Error(`'${targetPath}' is a directory. Use -r to process recursively.`); - } - - console.log(chalk.blue('๐Ÿ”“ Initiating Decryption...')); - const password = await promptPassword('Enter password:'); // Assume same pass for dir - - const processFile = async (filePath) => { - try { - const outputName = filePath.replace(/\.mojic$/, '') + '.restored.c'; - console.log(chalk.dim(` Restoring: ${path.basename(filePath)}`)); - - const headerStr = await getStreamHeader(filePath); - const metadata = CipherEngine.decodeHeader(headerStr); - - const engine = new CipherEngine(password); - await engine.init(metadata.saltHex); - - if (engine.authHash.toString('hex') !== metadata.authHex) { - console.log(chalk.red(` โŒ Skipped ${path.basename(filePath)}: Incorrect Password`)); - return; - } - - const readStream = fs.createReadStream(filePath); - const writeStream = fs.createWriteStream(outputName); - - await new Promise((resolve, reject) => { - readStream - .pipe(createHeaderSkipper()) - .pipe(engine.getDecryptStream()) - .pipe(writeStream) - .on('finish', resolve) - .on('error', reject); - }); - } catch (e) { - console.log(chalk.red(` โš ๏ธ Error processing ${path.basename(filePath)}: ${e.message}`)); - } - }; - - if (stats.isDirectory()) { - console.log(chalk.blue(`๐Ÿ“‚ Scanning directory: ${targetPath}`)); - await traverseDirectory(targetPath, '.mojic', processFile); - console.log(chalk.green('โœ… Batch decryption complete.')); - } else { - await processFile(targetPath); - console.log(chalk.green(`โœ… Restored.`)); - } - - } catch (err) { - console.error(chalk.red('Error:'), err.message); - } - }); - -// --- Security Rotation Tools (SRT) --- - -const rotatePassword = async (file) => { - try { - if (!fs.existsSync(file)) throw new Error('File not found'); - console.log(chalk.yellow(`๐Ÿ”„ Rotating Password for ${path.basename(file)}...`)); - - // 1. Authenticate Old - const headerStr = await getStreamHeader(file); - const metadata = CipherEngine.decodeHeader(headerStr); - const oldPass = await promptPassword('Enter CURRENT password:'); - - const oldEngine = new CipherEngine(oldPass); - await oldEngine.init(metadata.saltHex); - - if (oldEngine.authHash.toString('hex') !== metadata.authHex) { - console.error(chalk.red('โŒ Incorrect Current Password')); - process.exit(1); - } - - // 2. Init New - const newPass = await promptPassword('Enter NEW password:'); - const newEngine = new CipherEngine(newPass); - await newEngine.init(); - - // 3. Process - const tempFile = file + '.tmp'; - const readStream = fs.createReadStream(file); - const writeStream = fs.createWriteStream(tempFile); - - writeStream.write(newEngine._encodeHeader()); - - await new Promise((resolve, reject) => { - readStream - .pipe(createHeaderSkipper()) - .pipe(oldEngine.getDecryptStream()) - .pipe(newEngine.getEncryptStream()) - .pipe(writeStream) - .on('finish', resolve) - .on('error', reject); - }); - - fs.renameSync(tempFile, file); - console.log(chalk.green(`โœ… Password updated.`)); - - } catch (err) { - console.error(chalk.red('Error:'), err.message); - } -}; - -const reEncrypt = async (file) => { - try { - if (!fs.existsSync(file)) throw new Error('File not found'); - console.log(chalk.yellow(`๐ŸŽฒ Re-shuffling Entropy for ${path.basename(file)}...`)); - - // 1. Authenticate - const headerStr = await getStreamHeader(file); - const metadata = CipherEngine.decodeHeader(headerStr); - const password = await promptPassword('Enter password:'); - - const oldEngine = new CipherEngine(password); - await oldEngine.init(metadata.saltHex); - - if (oldEngine.authHash.toString('hex') !== metadata.authHex) { - console.error(chalk.red('โŒ Incorrect Password')); - process.exit(1); - } - - // 2. Init New - const newEngine = new CipherEngine(password); - await newEngine.init(); - - // 3. Process - const tempFile = file + '.tmp'; - const readStream = fs.createReadStream(file); - const writeStream = fs.createWriteStream(tempFile); - - writeStream.write(newEngine._encodeHeader()); - - await new Promise((resolve, reject) => { - readStream - .pipe(createHeaderSkipper()) - .pipe(oldEngine.getDecryptStream()) - .pipe(newEngine.getEncryptStream()) - .pipe(writeStream) - .on('finish', resolve) - .on('error', reject); - }); - - fs.renameSync(tempFile, file); - console.log(chalk.green(`โœ… File re-encrypted.`)); - - } catch (err) { - console.error(chalk.red('Error:'), err.message); - } -}; - -program - .command('srt') - .description('Security and Rotation Tools') - .option('--pass <file>', 'Update the password for an existing file') - .option('--re <file>', 'Re-encrypt with new random seed (Same password)') - .addHelpText('after', ` -Examples: - $ mojic srt --pass secret.mojic # Change the password of an encrypted file - $ mojic srt --re secret.mojic # Re-scramble the emojis (new salt) with same password -`) - .action(async (options) => { - if (options.pass) { - await rotatePassword(options.pass); - } else if (options.re) { - await reEncrypt(options.re); - } else { - console.log(chalk.yellow('Please specify an option: --pass <file> or --re <file>')); - program.commands.find(c => c.name() === 'srt').help(); - } - }); - -program.addHelpText('after', ` -Usage Examples: - $ mojic encode test.c # Encrypt a single C file - $ mojic encode ./src -r # Recursively encrypt all .c files in ./src - $ mojic decode test.mojic # Decrypt a single file - $ mojic decode ./src -r # Recursively decrypt all .mojic files -`); - -program.parse(process.argv);- \ No newline at end of file diff --git a/node_modules/mojic/lib/CipherEngine.js b/node_modules/mojic/lib/CipherEngine.js @@ -1,253 +0,0 @@ -import crypto from 'crypto'; -import { Transform } from 'stream'; -import { StringDecoder } from 'string_decoder'; - -/** - * Emojic CipherEngine - * Handles the logic for mapping C-code to Emojis based on a password seed. - * * * Updates: - * - Uses Intl.Segmenter for robust Emoji/Grapheme decoding. - * - Implements 'tail buffering' in streams to prevent splitting multi-codepoint emojis. - */ - -const HEADER_ALPHABET = ['๐ŸŒ‘', '๐ŸŒ’', '๐ŸŒ“', '๐ŸŒ”', '๐ŸŒ•', '๐ŸŒ–', '๐ŸŒ—', '๐ŸŒ˜', '๐Ÿ•', '๐Ÿ•‘', '๐Ÿ•’', '๐Ÿ•“', '๐Ÿ•”', '๐Ÿ••', '๐Ÿ•–', '๐Ÿ•—']; - -const EMOJI_UNIVERSE = [ - '๐Ÿ˜€','๐Ÿ˜ƒ','๐Ÿ˜„','๐Ÿ˜','๐Ÿ˜†','๐Ÿ˜…','๐Ÿคฃ','๐Ÿ˜‚','๐Ÿ™‚','๐Ÿ™ƒ','๐Ÿ˜‰','๐Ÿ˜Š','๐Ÿ˜‡','๐Ÿฅฐ','๐Ÿ˜','๐Ÿคฉ', - '๐Ÿ˜˜','๐Ÿ˜—','โ˜บ๏ธ','๐Ÿ˜š','๐Ÿ˜™','๐Ÿฅฒ','๐Ÿ˜‹','๐Ÿ˜›','๐Ÿ˜œ','๐Ÿคช','๐Ÿ˜','๐Ÿค‘','๐Ÿค—','๐Ÿคญ','๐Ÿคซ','๐Ÿค”', - '๐Ÿค','๐Ÿคจ','๐Ÿ˜','๐Ÿ˜‘','๐Ÿ˜ถ','๐Ÿ˜','๐Ÿ˜’','๐Ÿ™„','๐Ÿ˜ฌ','๐Ÿคฅ','๐Ÿ˜Œ','๐Ÿ˜”','๐Ÿ˜ช','๐Ÿคค','๐Ÿ˜ด','๐Ÿ˜ท', - '๐Ÿค’','๐Ÿค•','๐Ÿคข','๐Ÿคฎ','๐Ÿคง','๐Ÿฅต','๐Ÿฅถ','๐Ÿฅด','๐Ÿ˜ต','๐Ÿคฏ','๐Ÿค ','๐Ÿฅณ','๐Ÿ˜Ž','๐Ÿค“','๐Ÿง','๐Ÿ˜•', - '๐Ÿ˜Ÿ','๐Ÿ™','โ˜น๏ธ','๐Ÿ˜ฎ','๐Ÿ˜ฏ','๐Ÿ˜ฒ','๐Ÿ˜ณ','๐Ÿฅบ','๐Ÿ˜ฆ','๐Ÿ˜ง','๐Ÿ˜จ','๐Ÿ˜ฐ','๐Ÿ˜ฅ','๐Ÿ˜ข','๐Ÿ˜ญ','๐Ÿ˜ฑ', - '๐Ÿ˜–','๐Ÿ˜ฃ','๐Ÿ˜ž','๐Ÿ˜“','๐Ÿ˜ฉ','๐Ÿ˜ซ','๐Ÿฅฑ','๐Ÿ˜ค','๐Ÿ˜ก','๐Ÿ˜ ','๐Ÿคฌ','๐Ÿ˜ˆ','๐Ÿ‘ฟ','๐Ÿ’€','โ˜ ๏ธ','๐Ÿ’ฉ', - '๐Ÿคก','๐Ÿ‘น','๐Ÿ‘บ','๐Ÿ‘ป','๐Ÿ‘ฝ','๐Ÿ‘พ','๐Ÿค–','๐Ÿ˜บ','๐Ÿ˜ธ','๐Ÿ˜น','๐Ÿ˜ป','๐Ÿ˜ผ','๐Ÿ˜ฝ','๐Ÿ™€','๐Ÿ˜ฟ','๐Ÿ˜พ', - '๐Ÿ™ˆ','๐Ÿ™‰','๐Ÿ™Š','๐Ÿ’‹','๐Ÿ’Œ','๐Ÿ’˜','๐Ÿ’','๐Ÿ’–','๐Ÿ’—','๐Ÿ’“','๐Ÿ’ž','๐Ÿ’•','๐Ÿ’Ÿ','โฃ๏ธ','๐Ÿ’”','โค๏ธ', - '๐Ÿงก','๐Ÿ’›','๐Ÿ’š','๐Ÿ’™','๐Ÿ’œ','๐ŸคŽ','๐Ÿ–ค','๐Ÿค','๐Ÿ’ฏ','๐Ÿ’ข','๐Ÿ’ฅ','๐Ÿ’ซ','๐Ÿ’ฆ','๐Ÿ’จ','๐Ÿ•ณ๏ธ','๐Ÿ’ฃ', - '๐Ÿ’ฌ','๐Ÿ‘๏ธ','๐Ÿ’ญ','๐Ÿ’ค','๐Ÿ‘‹','๐Ÿคš','๐Ÿ–๏ธ','โœ‹','๐Ÿ––','๐Ÿ‘Œ','๐Ÿค','โœŒ๏ธ','๐Ÿคž','๐ŸคŸ','๐Ÿค˜', - '๐Ÿค™','๐Ÿ‘ˆ','๐Ÿ‘‰','๐Ÿ‘†','๐Ÿ–•','๐Ÿ‘‡','โ˜๏ธ','๐Ÿ‘','๐Ÿ‘Ž','โœŠ','๐Ÿ‘Š','๐Ÿค›','๐Ÿคœ','๐Ÿ‘','๐Ÿ™Œ','๐Ÿ‘', - '๐Ÿคฒ','๐Ÿค','๐Ÿ™','โœ๏ธ','๐Ÿ’…','๐Ÿคณ','๐Ÿ’ช','๐Ÿฆพ','๐Ÿฆฟ','๐Ÿฆต','๐Ÿฆถ','๐Ÿ‘‚','๐Ÿฆป','๐Ÿ‘ƒ','๐Ÿง ','๐Ÿซ€', - '๐Ÿซ','๐Ÿฆท','๐Ÿฆด','๐Ÿ‘€','๐Ÿ‘…','๐Ÿ‘„','๐Ÿ‘ถ','๐Ÿง’','๐Ÿ‘ฆ','๐Ÿ‘ง','๐Ÿง‘','๐Ÿ‘ฑ','๐Ÿ‘จ','๐Ÿง”','๐Ÿ‘จโ€๐Ÿฆฐ','๐Ÿ‘จโ€๐Ÿฆฑ', - '๐Ÿ”ฅ','๐ŸŒˆ','โ˜€๏ธ','โ›ˆ๏ธ','๐ŸŒฉ๏ธ','โ„๏ธ','๐ŸŒต','๐ŸŒท','๐ŸŒฒ','๐ŸŒณ','๐ŸŒด','๐Ÿฒ','๐Ÿ‰','๐Ÿฆ•','๐Ÿฆ–','๐Ÿ', - '๐ŸŽ','๐Ÿฆ„','๐Ÿฆ“','๐Ÿ†','๐Ÿ…','๐Ÿ‚','๐Ÿ„','๐Ÿ–','๐Ÿ','๐Ÿ‘','๐Ÿ','๐Ÿช','๐Ÿซ','๐Ÿฆ™','๐Ÿฆ’','๐Ÿ˜', - '๐Ÿฆ','๐Ÿฆ›','๐Ÿ','๐Ÿ€','๐Ÿน','๐Ÿฐ','๐Ÿ‡','๐Ÿฟ๏ธ','๐Ÿฆ”','๐Ÿฆ‡','๐Ÿป','๐Ÿจ','๐Ÿผ','๐Ÿฆฅ','๐Ÿฆฆ','๐Ÿฆจ', - '๐Ÿฆ˜','๐Ÿฆก','๐Ÿพ','๐Ÿฆƒ','๐Ÿ”','๐Ÿ“','๐Ÿฃ','๐Ÿค','๐Ÿฅ','๐Ÿฆ','๐Ÿง','๐Ÿ•Š๏ธ','๐Ÿฆ…','๐Ÿฆ†','๐Ÿฆข','๐Ÿฆ‰', - '๐ŸŽˆ','๐Ÿงจ','๐Ÿงง','๐ŸŽ€','๐ŸŽ','๐ŸŽ—๏ธ','๐ŸŽŸ๏ธ','๐ŸŽซ','๐ŸŽ–๏ธ','๐Ÿ†','๐Ÿ…','๐Ÿฅ‡','๐Ÿฅˆ','๐Ÿฅ‰','โšฝ','โšพ', - '๐ŸฅŽ','๐Ÿ€','๐Ÿ','๐Ÿˆ','๐Ÿ‰','๐ŸŽพ','๐Ÿฅ','๐ŸŽณ','๐Ÿ','๐Ÿ‘','๐Ÿ’','๐Ÿฅ','๐Ÿ“','๐Ÿธ','๐ŸฅŠ','๐Ÿฅ‹', - '๐Ÿฅ…','โ›ณ','โ›ธ๏ธ','๐ŸŽฃ','๐Ÿคฟ','๐ŸŽฝ','๐ŸŽฟ','๐Ÿ›ท','๐ŸฅŒ','๐ŸŽฏ','๐Ÿช€','๐Ÿช','๐ŸŽฑ','๐Ÿ”ฎ','๐Ÿงฟ','๐ŸŽฎ' -]; - -const C_KEYWORDS = [ - 'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', - 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', - 'int', 'long', 'register', 'return', 'short', 'signed', 'sizeof', 'static', - 'struct', 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while', - 'include', 'define', 'main', 'printf', 'NULL' -]; - -const ASCII_CHARS = Array.from({ length: 95 }, (_, i) => String.fromCharCode(i + 32)) - .concat(['\n', '\t', '\r']); - -const TOTAL_TOKENS = C_KEYWORDS.length + ASCII_CHARS.length; - -export class CipherEngine { - constructor(password) { - this.password = password; - this.tokenMap = new Map(); - this.reverseMap = new Map(); - this.isReady = false; - - if (EMOJI_UNIVERSE.length < TOTAL_TOKENS) { - throw new Error(`CRITICAL: Not enough emojis. Need ${TOTAL_TOKENS}, have ${EMOJI_UNIVERSE.length}`); - } - } - - async init(existingSaltHex = null) { - this.salt = existingSaltHex - ? Buffer.from(existingSaltHex, 'hex') - : crypto.randomBytes(16); - - const derivedKey = await new Promise((resolve, reject) => { - crypto.pbkdf2(this.password, this.salt, 100000, 36, 'sha256', (err, key) => { - if (err) reject(err); else resolve(key); - }); - }); - - const seedBuffer = derivedKey.subarray(0, 4); - this.authHash = derivedKey.subarray(4); - const seedInt = seedBuffer.readUInt32BE(0); - - const shuffledEmojis = this._shuffleArray([...EMOJI_UNIVERSE], seedInt); - - let emojiIndex = 0; - for (const keyword of C_KEYWORDS) { - const emo = shuffledEmojis[emojiIndex++]; - this.tokenMap.set(keyword, emo); - this.reverseMap.set(emo, keyword); - } - - for (const char of ASCII_CHARS) { - const emo = shuffledEmojis[emojiIndex++]; - this.tokenMap.set(char, emo); - this.reverseMap.set(emo, char); - } - - this.isReady = true; - } - - _mulberry32(a) { - return function() { - var t = a += 0x6D2B79F5; - t = Math.imul(t ^ t >>> 15, t | 1); - t ^= t + Math.imul(t ^ t >>> 7, t | 61); - return ((t ^ t >>> 14) >>> 0) / 4294967296; - } - } - - _shuffleArray(array, seed) { - const rng = this._mulberry32(seed); - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(rng() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } - return array; - } - - _encodeHeader() { - const saltHex = this.salt.toString('hex'); - const authHex = this.authHash.toString('hex'); - const fullHexString = saltHex + authHex; - - let headerStr = ''; - for (const char of fullHexString) { - const val = parseInt(char, 16); - headerStr += HEADER_ALPHABET[val]; - } - return headerStr + '\n'; - } - - static decodeHeader(headerStr) { - let hexString = ''; - // Use segmenter here just in case moon emojis have variation selectors - const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' }); - const segments = segmenter.segment(headerStr.trim()); - - for (const { segment } of segments) { - const index = HEADER_ALPHABET.indexOf(segment); - if (index === -1) throw new Error("Invalid Header format."); - hexString += index.toString(16); - } - const saltHex = hexString.substring(0, 32); - const authHex = hexString.substring(32); - return { saltHex, authHex }; - } - - getEncryptStream() { - if (!this.isReady) throw new Error("Engine not initialized"); - const engine = this; - const decoder = new StringDecoder('utf8'); - let buffer = ''; - - return new Transform({ - transform(chunk, encoding, callback) { - buffer += decoder.write(chunk); - - const sortedKeywords = [...C_KEYWORDS].sort((a, b) => b.length - a.length); - const keywordPattern = sortedKeywords.map(k => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'); - const regex = new RegExp(`\\b(${keywordPattern})\\b|([\\s\\S])`, 'g'); - - let match; - let output = ''; - - while ((match = regex.exec(buffer)) !== null) { - const token = match[0]; - if (engine.tokenMap.has(token)) { - output += engine.tokenMap.get(token); - } else { - output += token; - } - } - - buffer = ''; - this.push(output); - callback(); - }, - flush(callback) { - buffer += decoder.end(); - // Simple flush for MVP; in prod, we'd regex one last time. - callback(); - } - }); - } - - getDecryptStream() { - if (!this.isReady) throw new Error("Engine not initialized"); - const engine = this; - const decoder = new StringDecoder('utf8'); - const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' }); - - let buffer = ''; - - return new Transform({ - transform(chunk, encoding, callback) { - // 1. Decode bytes to string (handles partial UTF8 bytes) - buffer += decoder.write(chunk); - - // 2. Segment into Graphemes (Emojis) - const segments = [...segmenter.segment(buffer)]; - - // 3. Process all BUT the last segment - // We keep the last segment in the buffer because it might be - // the start of a multi-codepoint emoji that was split by the chunk boundary. - // (e.g. Base char is here, Variation Selector is in next chunk) - - const processUntilIndex = segments.length > 1 ? segments.length - 1 : 0; - let output = ''; - let processedString = ''; - - // If we only have 1 segment, we can't be sure it's complete, wait for next chunk - // UNLESS the buffer is getting huge, then force it. - if (segments.length === 1 && buffer.length < 100) { - // Wait for more data - callback(); - return; - } - - // Process safe segments - for (let i = 0; i < processUntilIndex; i++) { - const char = segments[i].segment; - processedString += char; - - if (engine.reverseMap.has(char)) { - output += engine.reverseMap.get(char); - } else { - output += char; - } - } - - // Update buffer to only contain the remaining tail - // Note: buffer might contain bytes not yet in segments if StringDecoder held them? - // No, decoder.write returns what is available. - // We just need to remove the processed part from the buffer string. - if (processUntilIndex > 0) { - buffer = buffer.slice(processedString.length); - this.push(output); - } - - callback(); - }, - flush(callback) { - // Process any remaining tail (buffer + decoder leftovers) - buffer += decoder.end(); - if (buffer) { - const segments = segmenter.segment(buffer); - let output = ''; - for (const { segment } of segments) { - if (engine.reverseMap.has(segment)) output += engine.reverseMap.get(segment); - else output += segment; - } - this.push(output); - } - callback(); - } - }); - } -}- \ No newline at end of file diff --git a/node_modules/mojic/package.json b/node_modules/mojic/package.json @@ -1,31 +0,0 @@ -{ - "name": "mojic", - "version": "1.0.2", - "description": "Obfuscate C source code into emojis using password-seeded mapping", - "main": "bin/mojic.js", - "bin": { - "mojic": "bin/mojic.js" - }, - "type": "module", - "scripts": { - "start": "node bin/mojic.js" - }, - "dependencies": { - "chalk": "^5.3.0", - "commander": "^11.1.0", - "inquirer": "^9.2.12" - }, - "engines": { - "node": ">=18.0.0" - }, - "keywords": [ - "cli", - "encryption", - "emoji", - "obfuscation", - "c", - "security" - ], - "author": "", - "license": "Apache-2.0" -}- \ No newline at end of file diff --git a/package-lock.json b/package-lock.json @@ -1,18 +1,17 @@ { "name": "mojic", - "version": "1.0.2", + "version": "1.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mojic", - "version": "1.0.2", + "version": "1.0.3", "license": "Apache-2.0", "dependencies": { "chalk": "^5.3.0", "commander": "^11.1.0", - "inquirer": "^9.2.12", - "mojic": "^1.0.2" + "inquirer": "^9.2.12" }, "bin": { "mojic": "bin/mojic.js" @@ -395,23 +394,6 @@ "node": ">=6" } }, - "node_modules/mojic": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/mojic/-/mojic-1.0.2.tgz", - "integrity": "sha512-p1J/707UtPmTRMCWNAaYwOAWjTWkuTIThLTkE2SsDebDbJd3mtbk/Yqe6odsXP09RJ192GTfnKXVqNE+YBcsLA==", - "license": "Apache-2.0", - "dependencies": { - "chalk": "^5.3.0", - "commander": "^11.1.0", - "inquirer": "^9.2.12" - }, - "bin": { - "mojic": "bin/mojic.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/mute-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", diff --git a/package.json b/package.json @@ -1,6 +1,6 @@ { "name": "mojic", - "version": "1.0.2", + "version": "1.0.3", "description": "Obfuscate C source code into emojis using password-seeded mapping", "main": "bin/mojic.js", "bin": { @@ -10,11 +10,14 @@ "scripts": { "start": "node bin/mojic.js" }, + "repository": { + "type": "git", + "url": "git+https://github.com/notamitgamer/mojic.git" + }, "dependencies": { "chalk": "^5.3.0", "commander": "^11.1.0", - "inquirer": "^9.2.12", - "mojic": "^1.0.2" + "inquirer": "^9.2.12" }, "engines": { "node": ">=18.0.0" @@ -27,6 +30,6 @@ "c", "security" ], - "author": "", + "author": "notamitgamer", "license": "Apache-2.0" -} +}+ \ No newline at end of file
ยฉ notamitgamer โ€ข Site Built: 2026-07-21 13:58:23 UTC โ€ข git-mirror commit: 1037f62 [view raw info]
Originally created with stagit โ€ข modified by notamitgamer
Forked from github.com/notamitgamer/git-mirror