nolimit-x 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/nolimit +3 -0
- package/package.json +18 -0
- package/src/attachment-handler.js +253 -0
- package/src/cli.js +31 -0
- package/src/document-generator.js +275 -0
- package/src/encryption.js +125 -0
- package/src/init.js +79 -0
- package/src/multi-handler.js +184 -0
- package/src/obfuscator.js +350 -0
- package/src/placeholders.js +230 -0
- package/src/processor.js +542 -0
- package/src/qr-generator.js +222 -0
- package/src/sender.js +41 -0
- package/src/utils.js +374 -0
- package/templates/config.json +54 -0
- package/templates/emails.txt +3 -0
- package/templates/messages.html +5 -0
- package/templates/senders.txt +3 -0
- package/test-campaign/config.json +35 -0
- package/test-campaign/emails.txt +3 -0
- package/test-campaign/messages.html +5 -0
- package/test-campaign/senders.txt +3 -0
- package/test-raw-smtp/config.json +56 -0
- package/test-raw-smtp/emails.txt +7 -0
- package/test-raw-smtp/messages.html +5 -0
- package/test-raw-smtp/senders.txt +7 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
|
|
3
|
+
class MessageEncryption {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.algorithm = 'aes-256-cbc';
|
|
6
|
+
this.keyLength = 32;
|
|
7
|
+
this.ivLength = 16;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Generate a random encryption key
|
|
11
|
+
generateKey() {
|
|
12
|
+
return crypto.randomBytes(this.keyLength);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Generate a random IV
|
|
16
|
+
generateIV() {
|
|
17
|
+
return crypto.randomBytes(this.ivLength);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Encrypt message content
|
|
21
|
+
encryptMessage(message, key = null) {
|
|
22
|
+
try {
|
|
23
|
+
const encryptionKey = key || this.generateKey();
|
|
24
|
+
const iv = this.generateIV();
|
|
25
|
+
|
|
26
|
+
const cipher = crypto.createCipheriv(this.algorithm, encryptionKey, iv);
|
|
27
|
+
let encrypted = cipher.update(message, 'utf8', 'hex');
|
|
28
|
+
encrypted += cipher.final('hex');
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
encrypted: encrypted,
|
|
32
|
+
key: encryptionKey.toString('hex'),
|
|
33
|
+
iv: iv.toString('hex'),
|
|
34
|
+
algorithm: this.algorithm
|
|
35
|
+
};
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error('Encryption failed:', error.message);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Decrypt message content
|
|
43
|
+
decryptMessage(encryptedData) {
|
|
44
|
+
try {
|
|
45
|
+
const { encrypted, key, iv, algorithm } = encryptedData;
|
|
46
|
+
|
|
47
|
+
const decipher = crypto.createDecipheriv(algorithm, Buffer.from(key, 'hex'), Buffer.from(iv, 'hex'));
|
|
48
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
49
|
+
decrypted += decipher.final('utf8');
|
|
50
|
+
|
|
51
|
+
return decrypted;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Decryption failed:', error.message);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Encrypt file content
|
|
59
|
+
encryptFile(fileContent, key = null) {
|
|
60
|
+
return this.encryptMessage(fileContent, key);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Generate encrypted attachment wrapper
|
|
64
|
+
createEncryptedAttachment(originalContent, filename) {
|
|
65
|
+
const encrypted = this.encryptMessage(originalContent);
|
|
66
|
+
if (!encrypted) return null;
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
filename: `${filename}.encrypted`,
|
|
70
|
+
content: encrypted.encrypted,
|
|
71
|
+
metadata: {
|
|
72
|
+
originalName: filename,
|
|
73
|
+
key: encrypted.key,
|
|
74
|
+
iv: encrypted.iv,
|
|
75
|
+
algorithm: encrypted.algorithm
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Create self-decrypting HTML wrapper
|
|
81
|
+
createSelfDecryptingWrapper(encryptedData, originalFilename) {
|
|
82
|
+
const { encrypted, key, iv, algorithm } = encryptedData;
|
|
83
|
+
|
|
84
|
+
return `
|
|
85
|
+
<!DOCTYPE html>
|
|
86
|
+
<html>
|
|
87
|
+
<head>
|
|
88
|
+
<meta charset="UTF-8">
|
|
89
|
+
<title>Encrypted Message - ${originalFilename}</title>
|
|
90
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
|
|
91
|
+
</head>
|
|
92
|
+
<body>
|
|
93
|
+
<div id="encrypted-content" style="display:none;">${encrypted}</div>
|
|
94
|
+
<div id="decrypted-content"></div>
|
|
95
|
+
|
|
96
|
+
<script>
|
|
97
|
+
function decryptContent() {
|
|
98
|
+
try {
|
|
99
|
+
const encrypted = document.getElementById('encrypted-content').textContent;
|
|
100
|
+
const key = '${key}';
|
|
101
|
+
const iv = '${iv}';
|
|
102
|
+
|
|
103
|
+
const decrypted = CryptoJS.AES.decrypt(encrypted, key, {
|
|
104
|
+
iv: CryptoJS.enc.Hex.parse(iv),
|
|
105
|
+
mode: CryptoJS.mode.CBC,
|
|
106
|
+
padding: CryptoJS.pad.Pkcs7
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
|
|
110
|
+
document.getElementById('decrypted-content').innerHTML = decryptedText;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
document.getElementById('decrypted-content').innerHTML =
|
|
113
|
+
'<p style="color: red;">Decryption failed. Please contact the sender.</p>';
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Auto-decrypt on page load
|
|
118
|
+
window.onload = decryptContent;
|
|
119
|
+
</script>
|
|
120
|
+
</body>
|
|
121
|
+
</html>`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = MessageEncryption;
|
package/src/init.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
function printBanner() {
|
|
5
|
+
console.log(`\n\x1b[36m` +
|
|
6
|
+
` _ _ _ _ _ _ _ \n| \ | | ___ | | | __| (_)___| |_ \n| \| |/ _ \| | |/ _ | / __| __|\n| |\ | (_) | | | (_| | \__ \ |_ \n|_| \_|\___/|_|_|\__,_|_|___/\__|\n` + '\x1b[0m');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function copyTemplate(src, dest) {
|
|
10
|
+
try {
|
|
11
|
+
const srcPath = path.join(__dirname, "../templates", src);
|
|
12
|
+
const destPath = path.join(dest, src);
|
|
13
|
+
console.log(`Copying from ${srcPath} to ${destPath}`);
|
|
14
|
+
fs.copyFileSync(srcPath, destPath);
|
|
15
|
+
} catch (err) {
|
|
16
|
+
console.error(`Failed to copy template ${src}:`, err);
|
|
17
|
+
throw err;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function fixConfigPaths(configPath, workspace) {
|
|
22
|
+
// Read config, update all file paths to be relative to workspace
|
|
23
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
24
|
+
if (config.emails_list) config.emails_list.path = "emails.txt";
|
|
25
|
+
if (config.senders_list) config.senders_list.path = "senders.txt";
|
|
26
|
+
if (config.messages_body) config.messages_body.path = "messages.html";
|
|
27
|
+
if (Array.isArray(config.attachments)) {
|
|
28
|
+
config.attachments.forEach(att => {
|
|
29
|
+
if (att.path) att.path = path.join("attachments", path.basename(att.path));
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function init(projectName) {
|
|
36
|
+
printBanner();
|
|
37
|
+
process.stdout.write('Setting up workspace...');
|
|
38
|
+
const projectPath = path.resolve(process.cwd(), projectName);
|
|
39
|
+
console.log("Project path:", projectPath);
|
|
40
|
+
|
|
41
|
+
if (fs.existsSync(projectPath)) {
|
|
42
|
+
console.error(`\nDirectory ${projectName} already exists.`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
fs.mkdirSync(projectPath);
|
|
48
|
+
console.log("Created directory:", projectPath);
|
|
49
|
+
|
|
50
|
+
// Create attachments dir if needed
|
|
51
|
+
const attachmentsDir = path.join(projectPath, "attachments");
|
|
52
|
+
if (!fs.existsSync(attachmentsDir)) fs.mkdirSync(attachmentsDir);
|
|
53
|
+
|
|
54
|
+
["config.json", "emails.txt", "senders.txt", "messages.html"].forEach(file => {
|
|
55
|
+
copyTemplate(file, projectPath);
|
|
56
|
+
console.log("Copied template:", file);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Fix config paths
|
|
60
|
+
fixConfigPaths(path.join(projectPath, "config.json"), projectPath);
|
|
61
|
+
|
|
62
|
+
// Simulate progress
|
|
63
|
+
await new Promise(res => setTimeout(res, 800));
|
|
64
|
+
process.stdout.clearLine();
|
|
65
|
+
process.stdout.cursorTo(0);
|
|
66
|
+
console.log("\x1b[32mWorkspace ready!\x1b[0m\n");
|
|
67
|
+
console.log("Template files created:");
|
|
68
|
+
["config.json", "emails.txt", "senders.txt", "messages.html"].forEach(file => {
|
|
69
|
+
console.log(` - ${file}`);
|
|
70
|
+
});
|
|
71
|
+
console.log(" - attachments/ (directory)");
|
|
72
|
+
console.log(`\nInitialized new campaign in ${projectPath}`);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error("Error during initialization:", error);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = init;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const utils = require('./utils');
|
|
3
|
+
|
|
4
|
+
class MultiHandler {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.senders = [];
|
|
7
|
+
this.subjects = [];
|
|
8
|
+
this.fromNames = [];
|
|
9
|
+
this.links = [];
|
|
10
|
+
this.currentIndexes = {
|
|
11
|
+
sender: 0,
|
|
12
|
+
subject: 0,
|
|
13
|
+
fromName: 0,
|
|
14
|
+
link: 0
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Load multiple senders from file
|
|
19
|
+
loadSenders(filePath) {
|
|
20
|
+
try {
|
|
21
|
+
if (!fs.existsSync(filePath)) {
|
|
22
|
+
console.warn(`Senders file not found: ${filePath}`);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
27
|
+
this.senders = content.split('\n')
|
|
28
|
+
.map(line => line.trim())
|
|
29
|
+
.filter(line => line && !line.startsWith('#') && utils.isValidEmail(line));
|
|
30
|
+
|
|
31
|
+
console.log(`Loaded ${this.senders.length} senders from ${filePath}`);
|
|
32
|
+
return true;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(`Failed to load senders: ${error.message}`);
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Load multiple subjects from file
|
|
40
|
+
loadSubjects(filePath) {
|
|
41
|
+
try {
|
|
42
|
+
if (!fs.existsSync(filePath)) {
|
|
43
|
+
console.warn(`Subjects file not found: ${filePath}`);
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
48
|
+
this.subjects = content.split('\n')
|
|
49
|
+
.map(line => line.trim())
|
|
50
|
+
.filter(line => line && !line.startsWith('#'));
|
|
51
|
+
|
|
52
|
+
console.log(`Loaded ${this.subjects.length} subjects from ${filePath}`);
|
|
53
|
+
return true;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(`Failed to load subjects: ${error.message}`);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Load multiple from names from file
|
|
61
|
+
loadFromNames(filePath) {
|
|
62
|
+
try {
|
|
63
|
+
if (!fs.existsSync(filePath)) {
|
|
64
|
+
console.warn(`From names file not found: ${filePath}`);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
69
|
+
this.fromNames = content.split('\n')
|
|
70
|
+
.map(line => line.trim())
|
|
71
|
+
.filter(line => line && !line.startsWith('#'));
|
|
72
|
+
|
|
73
|
+
console.log(`Loaded ${this.fromNames.length} from names from ${filePath}`);
|
|
74
|
+
return true;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error(`Failed to load from names: ${error.message}`);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Load multiple links from file
|
|
82
|
+
loadLinks(filePath) {
|
|
83
|
+
try {
|
|
84
|
+
if (!fs.existsSync(filePath)) {
|
|
85
|
+
console.warn(`Links file not found: ${filePath}`);
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
90
|
+
this.links = content.split('\n')
|
|
91
|
+
.map(line => line.trim())
|
|
92
|
+
.filter(line => line && !line.startsWith('#'));
|
|
93
|
+
|
|
94
|
+
console.log(`Loaded ${this.links.length} links from ${filePath}`);
|
|
95
|
+
return true;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error(`Failed to load links: ${error.message}`);
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Get next sender (round-robin)
|
|
103
|
+
getNextSender() {
|
|
104
|
+
if (this.senders.length === 0) return null;
|
|
105
|
+
|
|
106
|
+
const sender = this.senders[this.currentIndexes.sender];
|
|
107
|
+
this.currentIndexes.sender = (this.currentIndexes.sender + 1) % this.senders.length;
|
|
108
|
+
return sender;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Get next subject (round-robin)
|
|
112
|
+
getNextSubject() {
|
|
113
|
+
if (this.subjects.length === 0) return null;
|
|
114
|
+
|
|
115
|
+
const subject = this.subjects[this.currentIndexes.subject];
|
|
116
|
+
this.currentIndexes.subject = (this.currentIndexes.subject + 1) % this.subjects.length;
|
|
117
|
+
return subject;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Get next from name (round-robin)
|
|
121
|
+
getNextFromName() {
|
|
122
|
+
if (this.fromNames.length === 0) return null;
|
|
123
|
+
|
|
124
|
+
const fromName = this.fromNames[this.currentIndexes.fromName];
|
|
125
|
+
this.currentIndexes.fromName = (this.currentIndexes.fromName + 1) % this.fromNames.length;
|
|
126
|
+
return fromName;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Get next link (round-robin)
|
|
130
|
+
getNextLink() {
|
|
131
|
+
if (this.links.length === 0) return null;
|
|
132
|
+
|
|
133
|
+
const link = this.links[this.currentIndexes.link];
|
|
134
|
+
this.currentIndexes.link = (this.currentIndexes.link + 1) % this.links.length;
|
|
135
|
+
return link;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Get random sender
|
|
139
|
+
getRandomSender() {
|
|
140
|
+
if (this.senders.length === 0) return null;
|
|
141
|
+
return this.senders[Math.floor(Math.random() * this.senders.length)];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Get random subject
|
|
145
|
+
getRandomSubject() {
|
|
146
|
+
if (this.subjects.length === 0) return null;
|
|
147
|
+
return this.subjects[Math.floor(Math.random() * this.subjects.length)];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Get random from name
|
|
151
|
+
getRandomFromName() {
|
|
152
|
+
if (this.fromNames.length === 0) return null;
|
|
153
|
+
return this.fromNames[Math.floor(Math.random() * this.fromNames.length)];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Get random link
|
|
157
|
+
getRandomLink() {
|
|
158
|
+
if (this.links.length === 0) return null;
|
|
159
|
+
return this.links[Math.floor(Math.random() * this.links.length)];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Reset all indexes
|
|
163
|
+
resetIndexes() {
|
|
164
|
+
this.currentIndexes = {
|
|
165
|
+
sender: 0,
|
|
166
|
+
subject: 0,
|
|
167
|
+
fromName: 0,
|
|
168
|
+
link: 0
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Get statistics
|
|
173
|
+
getStats() {
|
|
174
|
+
return {
|
|
175
|
+
senders: this.senders.length,
|
|
176
|
+
subjects: this.subjects.length,
|
|
177
|
+
fromNames: this.fromNames.length,
|
|
178
|
+
links: this.links.length,
|
|
179
|
+
currentIndexes: { ...this.currentIndexes }
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = MultiHandler;
|