yakmesh 1.0.2 → 1.1.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/.github/workflows/ci.yml +67 -0
- package/CHANGELOG.md +92 -0
- package/SECURITY.md +57 -0
- package/identity/node-key.js +2 -2
- package/mesh/message-validator.js +95 -0
- package/mesh/network.js +14 -1
- package/mesh/rate-limiter.js +353 -0
- package/mesh/replay-defense.js +138 -0
- package/mesh/sybil-defense.js +151 -0
- package/package.json +1 -1
- package/test-crypto.mjs +28 -0
- package/test-security.mjs +223 -0
- package/test-stress.mjs +198 -0
- package/test-suite.mjs +198 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint-and-test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
node-version: [18.x, 20.x, 22.x]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout repository
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Setup Node.js ${{ matrix.node-version }}
|
|
22
|
+
uses: actions/setup-node@v4
|
|
23
|
+
with:
|
|
24
|
+
node-version: ${{ matrix.node-version }}
|
|
25
|
+
cache: 'npm'
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: npm ci --ignore-scripts
|
|
29
|
+
|
|
30
|
+
- name: Check syntax (ESLint)
|
|
31
|
+
run: |
|
|
32
|
+
npm install eslint --save-dev
|
|
33
|
+
npx eslint --no-eslintrc --env node,es2022 --parser-options ecmaVersion:2022,sourceType:module "**/*.js" --ignore-pattern node_modules/ --ignore-pattern dashboard/ || true
|
|
34
|
+
continue-on-error: true
|
|
35
|
+
|
|
36
|
+
- name: Validate imports
|
|
37
|
+
run: |
|
|
38
|
+
echo "Checking for valid ES module syntax..."
|
|
39
|
+
node --check server/index.js || true
|
|
40
|
+
node --check oracle/index.js || true
|
|
41
|
+
node --check cli/index.js || true
|
|
42
|
+
|
|
43
|
+
- name: Security audit
|
|
44
|
+
run: npm audit --audit-level=high || true
|
|
45
|
+
continue-on-error: true
|
|
46
|
+
|
|
47
|
+
publish-check:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
needs: lint-and-test
|
|
50
|
+
|
|
51
|
+
steps:
|
|
52
|
+
- name: Checkout repository
|
|
53
|
+
uses: actions/checkout@v4
|
|
54
|
+
|
|
55
|
+
- name: Setup Node.js
|
|
56
|
+
uses: actions/setup-node@v4
|
|
57
|
+
with:
|
|
58
|
+
node-version: 20.x
|
|
59
|
+
|
|
60
|
+
- name: Verify package.json
|
|
61
|
+
run: |
|
|
62
|
+
echo "Package name: $(node -p "require('./package.json').name")"
|
|
63
|
+
echo "Package version: $(node -p "require('./package.json').version")"
|
|
64
|
+
echo "License: $(node -p "require('./package.json').license")"
|
|
65
|
+
|
|
66
|
+
- name: Dry-run pack
|
|
67
|
+
run: npm pack --dry-run
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to YAKMESH™ will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.1.0] - 2026-01-14
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **NAVR (Network Assimilation Validation Routine)**: Computational identity verification for new nodes
|
|
12
|
+
- Replaces traditional "Proof of Work" terminology to avoid blockchain confusion
|
|
13
|
+
- One-time puzzle solve during node registration (NOT mining)
|
|
14
|
+
- Configurable difficulty for network defense scaling
|
|
15
|
+
- **Sybil Defense Module** (`mesh/sybil-defense.js`):
|
|
16
|
+
- NAVR computational puzzle for identity creation
|
|
17
|
+
- ReputationTracker for trust scoring (0.0 to 1.0 scale)
|
|
18
|
+
- SubnetDiversity to prevent eclipse attacks (max 3 connections per /24 subnet)
|
|
19
|
+
- **Replay Defense Module** (`mesh/replay-defense.js`):
|
|
20
|
+
- NonceRegistry with cryptographic 32-byte nonces (1hr expiry)
|
|
21
|
+
- TimestampValidator (10-minute freshness window)
|
|
22
|
+
- SequenceTracker for per-sender message ordering
|
|
23
|
+
- ChallengeResponse for mutual node authentication
|
|
24
|
+
- **Message Validator Module** (`mesh/message-validator.js`):
|
|
25
|
+
- Size limits per message type (1MB max, gossip 64KB, handshake 8KB)
|
|
26
|
+
- Nesting depth protection (max 10 levels)
|
|
27
|
+
- SafeJsonParser with prototype pollution protection
|
|
28
|
+
- Expanded test suite: 24 security tests covering all attack vectors
|
|
29
|
+
|
|
30
|
+
### Security
|
|
31
|
+
- Protection against Sybil attacks via NAVR + reputation + subnet diversity
|
|
32
|
+
- Protection against replay attacks via nonces + timestamps + sequences
|
|
33
|
+
- Protection against amplification attacks via message size limits
|
|
34
|
+
- Protection against eclipse attacks via subnet connection limits
|
|
35
|
+
|
|
36
|
+
## [1.0.3] - 2026-01-15
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
- **CRITICAL**: Fixed ML-DSA-65 signature verification parameter order (was: publicKey, message, signature → now: signature, message, publicKey)
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- **Rate Limiter**: New `ConnectionRateLimiter` class for DoS protection
|
|
43
|
+
- Connection flood protection (30 connections/minute per IP)
|
|
44
|
+
- Handshake spam detection (100 handshakes/minute global)
|
|
45
|
+
- Gossip message throttling (500 messages/minute)
|
|
46
|
+
- Automatic cleanup of stale tracking data
|
|
47
|
+
- Comprehensive test suite (17 tests covering crypto, security, performance)
|
|
48
|
+
- Stress test suite (14 tests with edge cases)
|
|
49
|
+
|
|
50
|
+
### Security
|
|
51
|
+
- Integrated rate limiting into mesh/network.js WebSocket handling
|
|
52
|
+
- Protection against 51% / network isolation attacks via message throttling
|
|
53
|
+
|
|
54
|
+
## [1.0.2] - 2026-01-14
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
- Fixed README.md formatting for proper rendering on npm and GitHub
|
|
58
|
+
|
|
59
|
+
## [1.0.1] - 2026-01-14
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
- Removed Pro-only security module from public npm package
|
|
63
|
+
- Added `.npmignore` to exclude proprietary code
|
|
64
|
+
|
|
65
|
+
## [1.0.0] - 2026-01-14
|
|
66
|
+
|
|
67
|
+
### Added
|
|
68
|
+
- **Post-Quantum Cryptography**: ML-DSA-65 (NIST FIPS 204) signatures
|
|
69
|
+
- **Self-Verifying Oracle**: Deterministic validation without external trust
|
|
70
|
+
- **Mesh Networking**: P2P WebSocket communication with gossip protocol
|
|
71
|
+
- **Precision Timing**: Support for atomic clocks, GPS, PTP, NTP time sources
|
|
72
|
+
- **Plugin Architecture**: BaseAdapter for custom database integrations
|
|
73
|
+
- **Phase Modulation**: Time-based anti-replay protection
|
|
74
|
+
- **Network Identity**: Configurable salts for isolated network deployments
|
|
75
|
+
- **Code Proof Protocol**: Integrity verification for distributed code
|
|
76
|
+
- **Consensus Engine**: Distributed agreement on network state
|
|
77
|
+
- **CLI Tools**: `yakmesh init`, `yakmesh start`, `yakmesh status`
|
|
78
|
+
- **Dashboard**: Web-based node monitoring interface
|
|
79
|
+
- **Embedded Webserver**: Caddy integration for HTTPS/reverse proxy
|
|
80
|
+
|
|
81
|
+
### Security
|
|
82
|
+
- XChaCha20-Poly1305 encryption for message payloads
|
|
83
|
+
- Lattice-based cryptography resistant to quantum attacks
|
|
84
|
+
- Hardware timestamping support for timing attack mitigation
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
[1.0.3]: https://github.com/yakmesh/yakmesh/releases/tag/v1.0.3
|
|
89
|
+
[1.0.2]: https://github.com/yakmesh/yakmesh/releases/tag/v1.0.2
|
|
90
|
+
[1.0.1]: https://github.com/yakmesh/yakmesh/releases/tag/v1.0.1
|
|
91
|
+
[1.0.0]: https://github.com/yakmesh/yakmesh/releases/tag/v1.0.0
|
|
92
|
+
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
| ------- | ------------------ |
|
|
7
|
+
| 1.0.x | :white_check_mark: |
|
|
8
|
+
|
|
9
|
+
## Reporting a Vulnerability
|
|
10
|
+
|
|
11
|
+
We take security seriously at YAKMESH™. If you discover a security vulnerability, please report it responsibly.
|
|
12
|
+
|
|
13
|
+
### How to Report
|
|
14
|
+
|
|
15
|
+
**Email**: security@yakmesh.dev
|
|
16
|
+
|
|
17
|
+
**Do NOT**:
|
|
18
|
+
- Open a public GitHub issue for security vulnerabilities
|
|
19
|
+
- Disclose the vulnerability publicly before we've had a chance to address it
|
|
20
|
+
|
|
21
|
+
### What to Include
|
|
22
|
+
|
|
23
|
+
- Description of the vulnerability
|
|
24
|
+
- Steps to reproduce
|
|
25
|
+
- Potential impact
|
|
26
|
+
- Suggested fix (if any)
|
|
27
|
+
|
|
28
|
+
### Response Timeline
|
|
29
|
+
|
|
30
|
+
- **Acknowledgment**: Within 48 hours
|
|
31
|
+
- **Initial Assessment**: Within 7 days
|
|
32
|
+
- **Resolution Target**: Within 30 days (depending on severity)
|
|
33
|
+
|
|
34
|
+
### Recognition
|
|
35
|
+
|
|
36
|
+
We appreciate responsible disclosure and will:
|
|
37
|
+
- Credit you in the security advisory (unless you prefer anonymity)
|
|
38
|
+
- Work with you to understand and resolve the issue
|
|
39
|
+
- Not take legal action for good-faith security research
|
|
40
|
+
|
|
41
|
+
## Security Features
|
|
42
|
+
|
|
43
|
+
YAKMESH implements multiple layers of security:
|
|
44
|
+
|
|
45
|
+
- **Post-Quantum Cryptography**: ML-DSA-65 signatures (NIST FIPS 204)
|
|
46
|
+
- **Authenticated Encryption**: XChaCha20-Poly1305
|
|
47
|
+
- **Replay Protection**: Phase-epoch based message validation
|
|
48
|
+
- **Code Integrity**: Self-verifying oracle with module sealing
|
|
49
|
+
|
|
50
|
+
## Known Limitations
|
|
51
|
+
|
|
52
|
+
- NTP time sources are not suitable for oracle operations (use GPS/PTP/Atomic)
|
|
53
|
+
- Community edition does not include WebSocket authentication (see Pro)
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
YAKMESH™ is a trademark of PeerQuanta (USPTO Serial No. 99594620)
|
package/identity/node-key.js
CHANGED
|
@@ -60,8 +60,8 @@ export function verifySignature(message, signatureHex, publicKeyHex) {
|
|
|
60
60
|
const messageBytes = typeof message === 'string'
|
|
61
61
|
? new TextEncoder().encode(message)
|
|
62
62
|
: message;
|
|
63
|
-
// ml_dsa65.verify takes (
|
|
64
|
-
return ml_dsa65.verify(
|
|
63
|
+
// ml_dsa65.verify takes (signature, message, publicKey)
|
|
64
|
+
return ml_dsa65.verify(signature, messageBytes, publicKey);
|
|
65
65
|
} catch (e) {
|
|
66
66
|
console.error('Signature verification failed:', e.message);
|
|
67
67
|
return false;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message Validation Module
|
|
3
|
+
* @module mesh/message-validator.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const SIZE_LIMITS = {
|
|
7
|
+
maxMessageSize: 1024 * 1024,
|
|
8
|
+
maxPayloadSizes: { gossip: 64 * 1024, handshake: 8 * 1024, listing: 128 * 1024, data: 512 * 1024 },
|
|
9
|
+
maxDepth: 10,
|
|
10
|
+
maxArrayLength: 1000,
|
|
11
|
+
maxStringLength: 100000,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export class MessageValidator {
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.limits = { ...SIZE_LIMITS, ...options.limits };
|
|
17
|
+
this.stats = { validated: 0, rejected: 0, rejectionReasons: new Map() };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
validateRaw(rawMessage, type = 'gossip') {
|
|
21
|
+
const size = typeof rawMessage === 'string' ? Buffer.byteLength(rawMessage, 'utf8') : rawMessage.length;
|
|
22
|
+
if (size > this.limits.maxMessageSize) return this._reject('Message too large (' + size + ' > ' + this.limits.maxMessageSize + ')');
|
|
23
|
+
const typeLimit = this.limits.maxPayloadSizes[type] || this.limits.maxPayloadSizes.gossip;
|
|
24
|
+
if (size > typeLimit) return this._reject(type + ' message too large (' + size + ' > ' + typeLimit + ')');
|
|
25
|
+
this.stats.validated++;
|
|
26
|
+
return { valid: true, size };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
validateStructure(message, type = 'gossip') {
|
|
30
|
+
if (message === null || message === undefined) return this._reject('Message is null or undefined');
|
|
31
|
+
if (typeof message !== 'object') return this._reject('Expected object, got ' + typeof message);
|
|
32
|
+
const depthCheck = this._checkDepth(message, 0);
|
|
33
|
+
if (!depthCheck.valid) return depthCheck;
|
|
34
|
+
const requiredCheck = this._checkRequiredFields(message, type);
|
|
35
|
+
if (!requiredCheck.valid) return requiredCheck;
|
|
36
|
+
this.stats.validated++;
|
|
37
|
+
return { valid: true };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_checkDepth(obj, depth) {
|
|
41
|
+
if (depth > this.limits.maxDepth) return this._reject('Message nesting too deep');
|
|
42
|
+
if (Array.isArray(obj)) {
|
|
43
|
+
if (obj.length > this.limits.maxArrayLength) return this._reject('Array too long');
|
|
44
|
+
for (const item of obj) {
|
|
45
|
+
if (typeof item === 'object' && item !== null) {
|
|
46
|
+
const check = this._checkDepth(item, depth + 1);
|
|
47
|
+
if (!check.valid) return check;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} else if (typeof obj === 'object' && obj !== null) {
|
|
51
|
+
for (const value of Object.values(obj)) {
|
|
52
|
+
if (typeof value === 'string' && value.length > this.limits.maxStringLength) return this._reject('String too long');
|
|
53
|
+
if (typeof value === 'object' && value !== null) {
|
|
54
|
+
const check = this._checkDepth(value, depth + 1);
|
|
55
|
+
if (!check.valid) return check;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { valid: true };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_checkRequiredFields(message, type) {
|
|
63
|
+
const requirements = { handshake: ['type', 'nodeId'], gossip: ['type'], listing: ['type', 'data', 'signature'], data: ['type', 'payload'] };
|
|
64
|
+
const required = requirements[type] || requirements.gossip;
|
|
65
|
+
for (const field of required) { if (!(field in message)) return this._reject('Missing required field: ' + field); }
|
|
66
|
+
return { valid: true };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_reject(reason) {
|
|
70
|
+
this.stats.rejected++;
|
|
71
|
+
const count = this.stats.rejectionReasons.get(reason) || 0;
|
|
72
|
+
this.stats.rejectionReasons.set(reason, count + 1);
|
|
73
|
+
return { valid: false, reason };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getStats() {
|
|
77
|
+
return { validated: this.stats.validated, rejected: this.stats.rejected, topReasons: [...this.stats.rejectionReasons.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10) };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export class SafeJsonParser {
|
|
82
|
+
constructor(options = {}) {
|
|
83
|
+
this.maxSize = options.maxSize || SIZE_LIMITS.maxMessageSize;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
parse(json) {
|
|
87
|
+
if (typeof json !== 'string') return { success: false, error: 'Input must be a string' };
|
|
88
|
+
if (json.length > this.maxSize) return { success: false, error: 'JSON too large' };
|
|
89
|
+
if (/__proto__|constructor.*prototype/i.test(json)) return { success: false, error: 'Suspicious content detected' };
|
|
90
|
+
try { return { success: true, data: JSON.parse(json) }; }
|
|
91
|
+
catch (e) { return { success: false, error: 'JSON parse error: ' + e.message }; }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export default MessageValidator;
|
package/mesh/network.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
7
|
+
import { ConnectionRateLimiter } from './rate-limiter.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Message types for mesh protocol
|
|
@@ -47,6 +48,9 @@ export class MeshNetwork {
|
|
|
47
48
|
this.messageHandlers = new Map();
|
|
48
49
|
this.seenMessages = new Set(); // For gossip deduplication
|
|
49
50
|
|
|
51
|
+
// Rate limiter for connection/message flood protection
|
|
52
|
+
this.rateLimiter = new ConnectionRateLimiter(config.rateLimiter || {});
|
|
53
|
+
|
|
50
54
|
this._setupDefaultHandlers();
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -327,7 +331,16 @@ export class MeshNetwork {
|
|
|
327
331
|
}
|
|
328
332
|
|
|
329
333
|
_handleIncomingConnection(ws, req) {
|
|
330
|
-
|
|
334
|
+
const clientIp = req.socket.remoteAddress || 'unknown';
|
|
335
|
+
console.log(`← Incoming connection from ${clientIp}`);
|
|
336
|
+
|
|
337
|
+
// SECURITY: Rate limit check for connection flood protection
|
|
338
|
+
const connectionCheck = this.rateLimiter.checkConnection(clientIp);
|
|
339
|
+
if (!connectionCheck.allowed) {
|
|
340
|
+
console.warn(`⚠️ Connection rejected (rate limit): ${clientIp} - ${connectionCheck.reason}`);
|
|
341
|
+
ws.close(1008, connectionCheck.reason);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
331
344
|
|
|
332
345
|
ws.on('message', (data) => {
|
|
333
346
|
this._handleMessage(ws, data, req);
|