society-protocol 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 +111 -0
- package/dist/adapters.d.ts +101 -0
- package/dist/adapters.d.ts.map +1 -0
- package/dist/adapters.js +764 -0
- package/dist/adapters.js.map +1 -0
- package/dist/agents-md.d.ts +59 -0
- package/dist/agents-md.d.ts.map +1 -0
- package/dist/agents-md.js +204 -0
- package/dist/agents-md.js.map +1 -0
- package/dist/autoconfig.d.ts +137 -0
- package/dist/autoconfig.d.ts.map +1 -0
- package/dist/autoconfig.js +452 -0
- package/dist/autoconfig.js.map +1 -0
- package/dist/bootstrap.d.ts +68 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +304 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/bridges/a2a-bridge.d.ts +156 -0
- package/dist/bridges/a2a-bridge.d.ts.map +1 -0
- package/dist/bridges/a2a-bridge.js +337 -0
- package/dist/bridges/a2a-bridge.js.map +1 -0
- package/dist/bridges/mcp-bridge.d.ts +87 -0
- package/dist/bridges/mcp-bridge.d.ts.map +1 -0
- package/dist/bridges/mcp-bridge.js +332 -0
- package/dist/bridges/mcp-bridge.js.map +1 -0
- package/dist/cache.d.ts +130 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +257 -0
- package/dist/cache.js.map +1 -0
- package/dist/capsules.d.ts +23 -0
- package/dist/capsules.d.ts.map +1 -0
- package/dist/capsules.js +75 -0
- package/dist/capsules.js.map +1 -0
- package/dist/cli/commands.d.ts +8 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +263 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/coc.d.ts +121 -0
- package/dist/coc.d.ts.map +1 -0
- package/dist/coc.js +629 -0
- package/dist/coc.js.map +1 -0
- package/dist/coc.test.d.ts +2 -0
- package/dist/coc.test.d.ts.map +1 -0
- package/dist/coc.test.js +80 -0
- package/dist/coc.test.js.map +1 -0
- package/dist/compression.d.ts +125 -0
- package/dist/compression.d.ts.map +1 -0
- package/dist/compression.js +573 -0
- package/dist/compression.js.map +1 -0
- package/dist/cot-stream.d.ts +220 -0
- package/dist/cot-stream.d.ts.map +1 -0
- package/dist/cot-stream.js +673 -0
- package/dist/cot-stream.js.map +1 -0
- package/dist/crypto-wasm.d.ts +100 -0
- package/dist/crypto-wasm.d.ts.map +1 -0
- package/dist/crypto-wasm.js +229 -0
- package/dist/crypto-wasm.js.map +1 -0
- package/dist/federation.d.ts +200 -0
- package/dist/federation.d.ts.map +1 -0
- package/dist/federation.js +691 -0
- package/dist/federation.js.map +1 -0
- package/dist/federation.test.d.ts +2 -0
- package/dist/federation.test.d.ts.map +1 -0
- package/dist/federation.test.js +71 -0
- package/dist/federation.test.js.map +1 -0
- package/dist/gateway/capability-router.d.ts +77 -0
- package/dist/gateway/capability-router.d.ts.map +1 -0
- package/dist/gateway/capability-router.js +222 -0
- package/dist/gateway/capability-router.js.map +1 -0
- package/dist/gateway/demand-spawner.d.ts +155 -0
- package/dist/gateway/demand-spawner.d.ts.map +1 -0
- package/dist/gateway/demand-spawner.js +426 -0
- package/dist/gateway/demand-spawner.js.map +1 -0
- package/dist/identity.d.ts +46 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +102 -0
- package/dist/identity.js.map +1 -0
- package/dist/identity.test.d.ts +2 -0
- package/dist/identity.test.d.ts.map +1 -0
- package/dist/identity.test.js +45 -0
- package/dist/identity.test.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1572 -0
- package/dist/index.js.map +1 -0
- package/dist/integration.d.ts +210 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +1105 -0
- package/dist/integration.js.map +1 -0
- package/dist/integration.test.d.ts +2 -0
- package/dist/integration.test.d.ts.map +1 -0
- package/dist/integration.test.js +155 -0
- package/dist/integration.test.js.map +1 -0
- package/dist/knowledge.d.ts +219 -0
- package/dist/knowledge.d.ts.map +1 -0
- package/dist/knowledge.js +543 -0
- package/dist/knowledge.js.map +1 -0
- package/dist/knowledge.test.d.ts +2 -0
- package/dist/knowledge.test.d.ts.map +1 -0
- package/dist/knowledge.test.js +72 -0
- package/dist/knowledge.test.js.map +1 -0
- package/dist/latent-space.d.ts +178 -0
- package/dist/latent-space.d.ts.map +1 -0
- package/dist/latent-space.js +385 -0
- package/dist/latent-space.js.map +1 -0
- package/dist/lib.d.ts +30 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +30 -0
- package/dist/lib.js.map +1 -0
- package/dist/mcp/server.d.ts +74 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +1392 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/metrics.d.ts +98 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +222 -0
- package/dist/metrics.js.map +1 -0
- package/dist/p2p.d.ts +87 -0
- package/dist/p2p.d.ts.map +1 -0
- package/dist/p2p.js +606 -0
- package/dist/p2p.js.map +1 -0
- package/dist/persona/capabilities.d.ts +17 -0
- package/dist/persona/capabilities.d.ts.map +1 -0
- package/dist/persona/capabilities.js +224 -0
- package/dist/persona/capabilities.js.map +1 -0
- package/dist/persona/domains.d.ts +22 -0
- package/dist/persona/domains.d.ts.map +1 -0
- package/dist/persona/domains.js +176 -0
- package/dist/persona/domains.js.map +1 -0
- package/dist/persona/embeddings.d.ts +40 -0
- package/dist/persona/embeddings.d.ts.map +1 -0
- package/dist/persona/embeddings.js +265 -0
- package/dist/persona/embeddings.js.map +1 -0
- package/dist/persona/engine.d.ts +79 -0
- package/dist/persona/engine.d.ts.map +1 -0
- package/dist/persona/engine.js +1087 -0
- package/dist/persona/engine.js.map +1 -0
- package/dist/persona/index.d.ts +11 -0
- package/dist/persona/index.d.ts.map +1 -0
- package/dist/persona/index.js +11 -0
- package/dist/persona/index.js.map +1 -0
- package/dist/persona/lifecycle.d.ts +17 -0
- package/dist/persona/lifecycle.d.ts.map +1 -0
- package/dist/persona/lifecycle.js +36 -0
- package/dist/persona/lifecycle.js.map +1 -0
- package/dist/persona/retrieval.d.ts +6 -0
- package/dist/persona/retrieval.d.ts.map +1 -0
- package/dist/persona/retrieval.js +122 -0
- package/dist/persona/retrieval.js.map +1 -0
- package/dist/persona/sync.d.ts +15 -0
- package/dist/persona/sync.d.ts.map +1 -0
- package/dist/persona/sync.js +92 -0
- package/dist/persona/sync.js.map +1 -0
- package/dist/persona/types.d.ts +283 -0
- package/dist/persona/types.d.ts.map +1 -0
- package/dist/persona/types.js +2 -0
- package/dist/persona/types.js.map +1 -0
- package/dist/persona/zkp/engine.d.ts +26 -0
- package/dist/persona/zkp/engine.d.ts.map +1 -0
- package/dist/persona/zkp/engine.js +370 -0
- package/dist/persona/zkp/engine.js.map +1 -0
- package/dist/persona/zkp/types.d.ts +39 -0
- package/dist/persona/zkp/types.d.ts.map +1 -0
- package/dist/persona/zkp/types.js +2 -0
- package/dist/persona/zkp/types.js.map +1 -0
- package/dist/planner.d.ts +114 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +522 -0
- package/dist/planner.js.map +1 -0
- package/dist/proactive/checkpoints.d.ts +9 -0
- package/dist/proactive/checkpoints.d.ts.map +1 -0
- package/dist/proactive/checkpoints.js +20 -0
- package/dist/proactive/checkpoints.js.map +1 -0
- package/dist/proactive/engine.d.ts +59 -0
- package/dist/proactive/engine.d.ts.map +1 -0
- package/dist/proactive/engine.js +406 -0
- package/dist/proactive/engine.js.map +1 -0
- package/dist/proactive/scheduler.d.ts +11 -0
- package/dist/proactive/scheduler.d.ts.map +1 -0
- package/dist/proactive/scheduler.js +45 -0
- package/dist/proactive/scheduler.js.map +1 -0
- package/dist/proactive/swarm-controller.d.ts +189 -0
- package/dist/proactive/swarm-controller.d.ts.map +1 -0
- package/dist/proactive/swarm-controller.js +477 -0
- package/dist/proactive/swarm-controller.js.map +1 -0
- package/dist/proactive/swarm-registry.d.ts +13 -0
- package/dist/proactive/swarm-registry.d.ts.map +1 -0
- package/dist/proactive/swarm-registry.js +122 -0
- package/dist/proactive/swarm-registry.js.map +1 -0
- package/dist/proactive/types.d.ts +145 -0
- package/dist/proactive/types.d.ts.map +1 -0
- package/dist/proactive/types.js +25 -0
- package/dist/proactive/types.js.map +1 -0
- package/dist/registry.d.ts +35 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +88 -0
- package/dist/registry.js.map +1 -0
- package/dist/reputation.d.ts +123 -0
- package/dist/reputation.d.ts.map +1 -0
- package/dist/reputation.js +366 -0
- package/dist/reputation.js.map +1 -0
- package/dist/reputation.test.d.ts +5 -0
- package/dist/reputation.test.d.ts.map +1 -0
- package/dist/reputation.test.js +265 -0
- package/dist/reputation.test.js.map +1 -0
- package/dist/rooms.d.ts +96 -0
- package/dist/rooms.d.ts.map +1 -0
- package/dist/rooms.js +410 -0
- package/dist/rooms.js.map +1 -0
- package/dist/sdk/client.d.ts +290 -0
- package/dist/sdk/client.d.ts.map +1 -0
- package/dist/sdk/client.js +1287 -0
- package/dist/sdk/client.js.map +1 -0
- package/dist/sdk/index.d.ts +32 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +70 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/security.d.ts +230 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +652 -0
- package/dist/security.js.map +1 -0
- package/dist/skills/engine.d.ts +262 -0
- package/dist/skills/engine.d.ts.map +1 -0
- package/dist/skills/engine.js +788 -0
- package/dist/skills/engine.js.map +1 -0
- package/dist/skills/engine.test.d.ts +2 -0
- package/dist/skills/engine.test.d.ts.map +1 -0
- package/dist/skills/engine.test.js +134 -0
- package/dist/skills/engine.test.js.map +1 -0
- package/dist/skills/parser.d.ts +129 -0
- package/dist/skills/parser.d.ts.map +1 -0
- package/dist/skills/parser.js +318 -0
- package/dist/skills/parser.js.map +1 -0
- package/dist/social.d.ts +149 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +401 -0
- package/dist/social.js.map +1 -0
- package/dist/storage-optimized.d.ts +116 -0
- package/dist/storage-optimized.d.ts.map +1 -0
- package/dist/storage-optimized.js +264 -0
- package/dist/storage-optimized.js.map +1 -0
- package/dist/storage.d.ts +584 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +2703 -0
- package/dist/storage.js.map +1 -0
- package/dist/storage.test.d.ts +2 -0
- package/dist/storage.test.d.ts.map +1 -0
- package/dist/storage.test.js +78 -0
- package/dist/storage.test.js.map +1 -0
- package/dist/swp.d.ts +443 -0
- package/dist/swp.d.ts.map +1 -0
- package/dist/swp.js +223 -0
- package/dist/swp.js.map +1 -0
- package/dist/swp.test.d.ts +5 -0
- package/dist/swp.test.d.ts.map +1 -0
- package/dist/swp.test.js +127 -0
- package/dist/swp.test.js.map +1 -0
- package/dist/templates.d.ts +25 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +1048 -0
- package/dist/templates.js.map +1 -0
- package/dist/test-e2e.d.ts +14 -0
- package/dist/test-e2e.d.ts.map +1 -0
- package/dist/test-e2e.js +266 -0
- package/dist/test-e2e.js.map +1 -0
- package/dist/workers/research-worker.d.ts +19 -0
- package/dist/workers/research-worker.d.ts.map +1 -0
- package/dist/workers/research-worker.js +141 -0
- package/dist/workers/research-worker.js.map +1 -0
- package/package.json +110 -0
package/dist/security.js
ADDED
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Society Protocol - Advanced Security Layer v1.0
|
|
3
|
+
*
|
|
4
|
+
* Camada de segurança aprimorada:
|
|
5
|
+
* - End-to-end encryption (E2EE)
|
|
6
|
+
* - Key rotation automático
|
|
7
|
+
* - Permission system granular
|
|
8
|
+
* - Audit logging
|
|
9
|
+
* - Rate limiting avançado
|
|
10
|
+
* - Content sanitization
|
|
11
|
+
* - DDoS protection
|
|
12
|
+
* - Threat detection
|
|
13
|
+
*/
|
|
14
|
+
import { createHash, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
|
|
15
|
+
import { promisify } from 'util';
|
|
16
|
+
import { sign as identitySign, verify as identityVerify } from './identity.js';
|
|
17
|
+
const randomBytesAsync = promisify(randomBytes);
|
|
18
|
+
// ─── End-to-End Encryption ───────────────────────────────────────
|
|
19
|
+
export class E2EEncryption {
|
|
20
|
+
keys = new Map();
|
|
21
|
+
sharedSecrets = new Map();
|
|
22
|
+
config;
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.config = {
|
|
25
|
+
algorithm: config?.algorithm || 'aes-256-gcm',
|
|
26
|
+
keyRotationInterval: config?.keyRotationInterval || 24 * 60 * 60 * 1000, // 24h
|
|
27
|
+
forwardSecrecy: config?.forwardSecrecy ?? true
|
|
28
|
+
};
|
|
29
|
+
// Rotacionar chaves periodicamente
|
|
30
|
+
setInterval(() => this.rotateKeys(), this.config.keyRotationInterval);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Gerar par de chaves X25519 para Diffie-Hellman
|
|
34
|
+
*/
|
|
35
|
+
async generateKeyPair(identityId) {
|
|
36
|
+
// Usar Web Crypto API
|
|
37
|
+
const keyPair = await crypto.subtle.generateKey({
|
|
38
|
+
name: 'X25519'
|
|
39
|
+
}, true, // extractable
|
|
40
|
+
['deriveBits']);
|
|
41
|
+
this.keys.set(identityId, keyPair);
|
|
42
|
+
return keyPair;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Derivar segredo compartilhado com outro participante
|
|
46
|
+
*/
|
|
47
|
+
async deriveSharedSecret(myId, theirPublicKey) {
|
|
48
|
+
const myKeyPair = this.keys.get(myId);
|
|
49
|
+
if (!myKeyPair) {
|
|
50
|
+
throw new Error('Key pair not found');
|
|
51
|
+
}
|
|
52
|
+
// Importar chave pública do outro
|
|
53
|
+
const theirKey = await crypto.subtle.importKey('raw', theirPublicKey, { name: 'X25519' }, false, []);
|
|
54
|
+
// Derivar segredo
|
|
55
|
+
const sharedSecret = await crypto.subtle.deriveBits({
|
|
56
|
+
name: 'X25519',
|
|
57
|
+
public: theirKey
|
|
58
|
+
}, myKeyPair.privateKey, 256);
|
|
59
|
+
const secret = new Uint8Array(sharedSecret);
|
|
60
|
+
// Derivar chave de criptografia
|
|
61
|
+
const key = await this.deriveEncryptionKey(secret);
|
|
62
|
+
const cacheKey = `${myId}:${Buffer.from(theirPublicKey).toString('hex')}`;
|
|
63
|
+
this.sharedSecrets.set(cacheKey, key);
|
|
64
|
+
return key;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Criptografar mensagem
|
|
68
|
+
*/
|
|
69
|
+
async encrypt(plaintext, recipientPublicKey, senderId) {
|
|
70
|
+
const key = await this.deriveSharedSecret(senderId, recipientPublicKey);
|
|
71
|
+
// Gerar nonce
|
|
72
|
+
const nonce = await randomBytesAsync(12);
|
|
73
|
+
// Criptografar
|
|
74
|
+
const data = new TextEncoder().encode(plaintext);
|
|
75
|
+
let ciphertext;
|
|
76
|
+
if (this.config.algorithm === 'aes-256-gcm') {
|
|
77
|
+
// Usar AES-256-GCM via Node.js crypto
|
|
78
|
+
const cipher = createCipheriv('aes-256-gcm', key.slice(0, 32), nonce);
|
|
79
|
+
const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
80
|
+
const authTag = cipher.getAuthTag();
|
|
81
|
+
ciphertext = new Uint8Array(Buffer.concat([encrypted, authTag]));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw new Error(`Unsupported encryption algorithm: ${this.config.algorithm}`);
|
|
85
|
+
}
|
|
86
|
+
return { ciphertext, nonce };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Descriptografar mensagem
|
|
90
|
+
*/
|
|
91
|
+
async decrypt(ciphertext, nonce, senderPublicKey, recipientId) {
|
|
92
|
+
const key = await this.deriveSharedSecret(recipientId, senderPublicKey);
|
|
93
|
+
let plaintext;
|
|
94
|
+
if (this.config.algorithm === 'aes-256-gcm') {
|
|
95
|
+
const decipher = createDecipheriv('aes-256-gcm', key.slice(0, 32), nonce);
|
|
96
|
+
const authTag = ciphertext.slice(-16);
|
|
97
|
+
decipher.setAuthTag(authTag);
|
|
98
|
+
const encrypted = ciphertext.slice(0, -16);
|
|
99
|
+
plaintext = new Uint8Array(Buffer.concat([
|
|
100
|
+
decipher.update(encrypted),
|
|
101
|
+
decipher.final()
|
|
102
|
+
]));
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
throw new Error(`Unsupported encryption algorithm: ${this.config.algorithm}`);
|
|
106
|
+
}
|
|
107
|
+
return new TextDecoder().decode(plaintext);
|
|
108
|
+
}
|
|
109
|
+
async deriveEncryptionKey(sharedSecret) {
|
|
110
|
+
// HKDF via Web Crypto API (RFC 5869)
|
|
111
|
+
// Use a deterministic salt derived from the shared secret itself,
|
|
112
|
+
// so both sides derive the same key from the same DH output.
|
|
113
|
+
const ikm = await crypto.subtle.importKey('raw', sharedSecret, 'HKDF', false, ['deriveBits']);
|
|
114
|
+
const salt = createHash('sha256').update('society-hkdf-salt-v1').digest();
|
|
115
|
+
const info = new TextEncoder().encode('society-protocol-e2e-v1');
|
|
116
|
+
const derived = await crypto.subtle.deriveBits({
|
|
117
|
+
name: 'HKDF',
|
|
118
|
+
hash: 'SHA-256',
|
|
119
|
+
salt,
|
|
120
|
+
info,
|
|
121
|
+
}, ikm, 256 // 32 bytes
|
|
122
|
+
);
|
|
123
|
+
return new Uint8Array(derived);
|
|
124
|
+
}
|
|
125
|
+
async xorWithKey(data, key) {
|
|
126
|
+
const result = new Uint8Array(data.length);
|
|
127
|
+
for (let i = 0; i < data.length; i++) {
|
|
128
|
+
result[i] = data[i] ^ key[i % key.length];
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
rotateKeys() {
|
|
133
|
+
// Implementar rotação de chaves
|
|
134
|
+
console.log('[security] Rotating encryption keys...');
|
|
135
|
+
if (this.config.forwardSecrecy) {
|
|
136
|
+
// Limpar segredos antigos
|
|
137
|
+
this.sharedSecrets.clear();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// ─── Permission System ───────────────────────────────────────────
|
|
142
|
+
export class PermissionManager {
|
|
143
|
+
roles = new Map();
|
|
144
|
+
userPermissions = new Map();
|
|
145
|
+
acl = new Map(); // resource -> Set<did>
|
|
146
|
+
constructor() {
|
|
147
|
+
this.setupDefaultRoles();
|
|
148
|
+
}
|
|
149
|
+
setupDefaultRoles() {
|
|
150
|
+
this.roles.set('guest', [
|
|
151
|
+
{ resource: 'room:*', action: 'read' },
|
|
152
|
+
{ resource: 'message:*', action: 'read' }
|
|
153
|
+
]);
|
|
154
|
+
this.roles.set('member', [
|
|
155
|
+
{ resource: 'room:*', action: 'read' },
|
|
156
|
+
{ resource: 'room:*', action: 'write' },
|
|
157
|
+
{ resource: 'message:*', action: 'read' },
|
|
158
|
+
{ resource: 'message:*', action: 'write' },
|
|
159
|
+
{ resource: 'coc:*', action: 'execute' }
|
|
160
|
+
]);
|
|
161
|
+
this.roles.set('moderator', [
|
|
162
|
+
{ resource: '*', action: 'read' },
|
|
163
|
+
{ resource: 'room:*', action: 'admin' },
|
|
164
|
+
{ resource: 'member:*', action: 'admin' },
|
|
165
|
+
{ resource: 'message:*', action: 'admin' }
|
|
166
|
+
]);
|
|
167
|
+
this.roles.set('admin', [
|
|
168
|
+
{ resource: '*', action: 'admin' }
|
|
169
|
+
]);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Verificar se usuário tem permissão
|
|
173
|
+
*/
|
|
174
|
+
checkPermission(context, resource, action) {
|
|
175
|
+
// Verificar permissões diretas
|
|
176
|
+
const userPerms = this.userPermissions.get(context.identity.did) || [];
|
|
177
|
+
if (this.hasMatchingPermission(userPerms, resource, action)) {
|
|
178
|
+
return this.checkConditions(context, userPerms, resource, action);
|
|
179
|
+
}
|
|
180
|
+
// Verificar via roles
|
|
181
|
+
// TODO: Implementar sistema de roles vinculado ao context
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Conceder permissão a usuário
|
|
186
|
+
*/
|
|
187
|
+
grantPermission(did, permission) {
|
|
188
|
+
if (!this.userPermissions.has(did)) {
|
|
189
|
+
this.userPermissions.set(did, []);
|
|
190
|
+
}
|
|
191
|
+
this.userPermissions.get(did).push(permission);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Revogar permissão
|
|
195
|
+
*/
|
|
196
|
+
revokePermission(did, resource, action) {
|
|
197
|
+
const perms = this.userPermissions.get(did);
|
|
198
|
+
if (!perms)
|
|
199
|
+
return;
|
|
200
|
+
const filtered = perms.filter(p => !(p.resource === resource && p.action === action));
|
|
201
|
+
this.userPermissions.set(did, filtered);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Verificar ACL de recurso
|
|
205
|
+
*/
|
|
206
|
+
checkACL(resource, did) {
|
|
207
|
+
const allowed = this.acl.get(resource);
|
|
208
|
+
if (!allowed)
|
|
209
|
+
return true; // Sem ACL = aberto
|
|
210
|
+
return allowed.has(did) || allowed.has('*');
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Adicionar entrada ACL
|
|
214
|
+
*/
|
|
215
|
+
addToACL(resource, did) {
|
|
216
|
+
if (!this.acl.has(resource)) {
|
|
217
|
+
this.acl.set(resource, new Set());
|
|
218
|
+
}
|
|
219
|
+
this.acl.get(resource).add(did);
|
|
220
|
+
}
|
|
221
|
+
hasMatchingPermission(permissions, resource, action) {
|
|
222
|
+
return permissions.some(p => {
|
|
223
|
+
const resourceMatch = this.matchResource(p.resource, resource);
|
|
224
|
+
const actionMatch = p.action === action || p.action === 'admin';
|
|
225
|
+
return resourceMatch && actionMatch;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
matchPermission(pattern, resource) {
|
|
229
|
+
// Suportar wildcards: room:*:message → room:abc123:message
|
|
230
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '[^:]*') + '$');
|
|
231
|
+
return regex.test(resource);
|
|
232
|
+
}
|
|
233
|
+
matchResource(pattern, resource) {
|
|
234
|
+
if (pattern === '*')
|
|
235
|
+
return true;
|
|
236
|
+
if (pattern === resource)
|
|
237
|
+
return true;
|
|
238
|
+
// Wildcard: room:*:message
|
|
239
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '[^:]*') + '$');
|
|
240
|
+
return regex.test(resource);
|
|
241
|
+
}
|
|
242
|
+
checkConditions(context, permissions, resource, action) {
|
|
243
|
+
const perm = permissions.find(p => this.matchResource(p.resource, resource) &&
|
|
244
|
+
(p.action === action || p.action === 'admin'));
|
|
245
|
+
if (!perm?.conditions)
|
|
246
|
+
return true;
|
|
247
|
+
const cond = perm.conditions;
|
|
248
|
+
// Verificar time window
|
|
249
|
+
if (cond.timeWindow) {
|
|
250
|
+
const now = Date.now();
|
|
251
|
+
if (now < cond.timeWindow.start || now > cond.timeWindow.end) {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Verificar IP whitelist
|
|
256
|
+
if (cond.ipWhitelist && context.ip) {
|
|
257
|
+
if (!cond.ipWhitelist.includes(context.ip)) {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// Verificar reputação mínima
|
|
262
|
+
if (cond.reputationMin && context.reputation < cond.reputationMin) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
// Verificar MFA
|
|
266
|
+
if (cond.mfaRequired && !context.mfaVerified) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// ─── Audit Logger ────────────────────────────────────────────────
|
|
273
|
+
export class AuditLogger {
|
|
274
|
+
events = [];
|
|
275
|
+
maxSize;
|
|
276
|
+
listeners = [];
|
|
277
|
+
constructor(maxSize = 10000) {
|
|
278
|
+
this.maxSize = maxSize;
|
|
279
|
+
}
|
|
280
|
+
async log(event) {
|
|
281
|
+
const fullEvent = {
|
|
282
|
+
id: `audit_${Date.now()}_${randomBytes(4).toString('hex')}`,
|
|
283
|
+
timestamp: Date.now(),
|
|
284
|
+
...event
|
|
285
|
+
};
|
|
286
|
+
// Assinar evento para integridade
|
|
287
|
+
fullEvent.signature = await this.signEvent(fullEvent);
|
|
288
|
+
this.events.push(fullEvent);
|
|
289
|
+
// Manter tamanho limitado
|
|
290
|
+
if (this.events.length > this.maxSize) {
|
|
291
|
+
this.events = this.events.slice(-this.maxSize);
|
|
292
|
+
}
|
|
293
|
+
// Notificar listeners
|
|
294
|
+
for (const listener of this.listeners) {
|
|
295
|
+
listener(fullEvent);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
query(filters) {
|
|
299
|
+
let results = this.events;
|
|
300
|
+
if (filters.type) {
|
|
301
|
+
results = results.filter(e => e.type === filters.type);
|
|
302
|
+
}
|
|
303
|
+
if (filters.actor) {
|
|
304
|
+
results = results.filter(e => e.actor === filters.actor);
|
|
305
|
+
}
|
|
306
|
+
if (filters.resource) {
|
|
307
|
+
results = results.filter(e => e.resource === filters.resource);
|
|
308
|
+
}
|
|
309
|
+
if (filters.severity) {
|
|
310
|
+
results = results.filter(e => e.severity === filters.severity);
|
|
311
|
+
}
|
|
312
|
+
if (filters.startTime) {
|
|
313
|
+
results = results.filter(e => e.timestamp >= filters.startTime);
|
|
314
|
+
}
|
|
315
|
+
if (filters.endTime) {
|
|
316
|
+
results = results.filter(e => e.timestamp <= filters.endTime);
|
|
317
|
+
}
|
|
318
|
+
// Ordenar por timestamp decrescente
|
|
319
|
+
results.sort((a, b) => b.timestamp - a.timestamp);
|
|
320
|
+
return results.slice(0, filters.limit || 100);
|
|
321
|
+
}
|
|
322
|
+
onEvent(listener) {
|
|
323
|
+
this.listeners.push(listener);
|
|
324
|
+
}
|
|
325
|
+
async signEvent(event) {
|
|
326
|
+
const data = JSON.stringify({
|
|
327
|
+
type: event.type,
|
|
328
|
+
actor: event.actor,
|
|
329
|
+
resource: event.resource,
|
|
330
|
+
action: event.action,
|
|
331
|
+
timestamp: event.timestamp
|
|
332
|
+
});
|
|
333
|
+
return createHash('sha256').update(data).digest('hex');
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// ─── Threat Detection ────────────────────────────────────────────
|
|
337
|
+
export class ThreatDetector {
|
|
338
|
+
threatIntel = new Map();
|
|
339
|
+
patterns = new Map();
|
|
340
|
+
behaviorScores = new Map();
|
|
341
|
+
constructor() {
|
|
342
|
+
this.setupPatterns();
|
|
343
|
+
}
|
|
344
|
+
setupPatterns() {
|
|
345
|
+
// Padrões de ameaça
|
|
346
|
+
this.patterns.set('spam', /\b(viagra|casino|lottery|winner)\b/gi);
|
|
347
|
+
this.patterns.set('suspicious_links', /https?:\/\/[^\s]{100,}/gi);
|
|
348
|
+
this.patterns.set('credential_harvest', /\b(password|login|credential)\s*=\s*/gi);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Analisar envelope para ameaças
|
|
352
|
+
*/
|
|
353
|
+
analyzeEnvelope(envelope) {
|
|
354
|
+
const checks = [
|
|
355
|
+
this.checkKnownThreats(envelope.from.did),
|
|
356
|
+
this.checkContentPatterns(envelope),
|
|
357
|
+
this.checkBehaviorAnomalies(envelope.from.did),
|
|
358
|
+
this.checkRateAnomalies(envelope.from.did)
|
|
359
|
+
];
|
|
360
|
+
const highestThreat = checks
|
|
361
|
+
.filter(c => c.threat)
|
|
362
|
+
.sort((a, b) => this.severityRank(b.severity) - this.severityRank(a.severity))[0];
|
|
363
|
+
return highestThreat || { threat: false, severity: 'low' };
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Reportar ameaça
|
|
367
|
+
*/
|
|
368
|
+
reportThreat(threat) {
|
|
369
|
+
const existing = this.threatIntel.get(threat.value);
|
|
370
|
+
if (existing) {
|
|
371
|
+
existing.lastSeen = Date.now();
|
|
372
|
+
existing.occurrences++;
|
|
373
|
+
existing.confidence = Math.min(1, existing.confidence + 0.1);
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
const newThreat = {
|
|
377
|
+
...threat,
|
|
378
|
+
firstSeen: Date.now(),
|
|
379
|
+
lastSeen: Date.now(),
|
|
380
|
+
occurrences: 1
|
|
381
|
+
};
|
|
382
|
+
this.threatIntel.set(threat.value, newThreat);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Verificar se está na lista de ameaças
|
|
387
|
+
*/
|
|
388
|
+
isThreat(value) {
|
|
389
|
+
return this.threatIntel.get(value);
|
|
390
|
+
}
|
|
391
|
+
checkKnownThreats(did) {
|
|
392
|
+
const threat = this.threatIntel.get(did);
|
|
393
|
+
if (threat) {
|
|
394
|
+
return {
|
|
395
|
+
threat: true,
|
|
396
|
+
severity: threat.severity,
|
|
397
|
+
category: threat.category,
|
|
398
|
+
reason: `Known threat: ${threat.category}`
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
return { threat: false, severity: 'low' };
|
|
402
|
+
}
|
|
403
|
+
checkContentPatterns(envelope) {
|
|
404
|
+
const bodyStr = JSON.stringify(envelope.body);
|
|
405
|
+
for (const [category, pattern] of this.patterns) {
|
|
406
|
+
if (pattern.test(bodyStr)) {
|
|
407
|
+
return {
|
|
408
|
+
threat: true,
|
|
409
|
+
severity: 'medium',
|
|
410
|
+
category: category,
|
|
411
|
+
reason: `Pattern match: ${category}`
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return { threat: false, severity: 'low' };
|
|
416
|
+
}
|
|
417
|
+
checkBehaviorAnomalies(did) {
|
|
418
|
+
const behavior = this.behaviorScores.get(did);
|
|
419
|
+
if (!behavior)
|
|
420
|
+
return { threat: false, severity: 'low' };
|
|
421
|
+
// Score anormalmente alto = possível Sybil ou ataque
|
|
422
|
+
if (behavior.score > 100) {
|
|
423
|
+
return {
|
|
424
|
+
threat: true,
|
|
425
|
+
severity: 'high',
|
|
426
|
+
category: 'sybil',
|
|
427
|
+
reason: 'Behavioral anomaly detected'
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
return { threat: false, severity: 'low' };
|
|
431
|
+
}
|
|
432
|
+
checkRateAnomalies(did) {
|
|
433
|
+
const behavior = this.behaviorScores.get(did);
|
|
434
|
+
if (!behavior)
|
|
435
|
+
return { threat: false, severity: 'low' };
|
|
436
|
+
// Muitos eventos em curto período
|
|
437
|
+
const recentEvents = behavior.events.filter(t => Date.now() - t < 60000);
|
|
438
|
+
if (recentEvents.length > 100) {
|
|
439
|
+
return {
|
|
440
|
+
threat: true,
|
|
441
|
+
severity: 'high',
|
|
442
|
+
category: 'ddos',
|
|
443
|
+
reason: 'Rate limit exceeded'
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
return { threat: false, severity: 'low' };
|
|
447
|
+
}
|
|
448
|
+
updateBehaviorScore(did, delta) {
|
|
449
|
+
if (!this.behaviorScores.has(did)) {
|
|
450
|
+
this.behaviorScores.set(did, { score: 0, events: [] });
|
|
451
|
+
}
|
|
452
|
+
const behavior = this.behaviorScores.get(did);
|
|
453
|
+
behavior.score += delta;
|
|
454
|
+
behavior.events.push(Date.now());
|
|
455
|
+
// Limpar eventos antigos
|
|
456
|
+
behavior.events = behavior.events.filter(t => Date.now() - t < 3600000);
|
|
457
|
+
}
|
|
458
|
+
severityRank(severity) {
|
|
459
|
+
const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
460
|
+
return ranks[severity];
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
// ─── Content Sanitizer ───────────────────────────────────────────
|
|
464
|
+
export class ContentSanitizer {
|
|
465
|
+
allowedTags = new Set(['b', 'i', 'u', 'code', 'pre', 'a']);
|
|
466
|
+
allowedSchemes = new Set(['http', 'https', 'mailto']);
|
|
467
|
+
/**
|
|
468
|
+
* Sanitizar conteúdo HTML/Markdown
|
|
469
|
+
*/
|
|
470
|
+
sanitize(input) {
|
|
471
|
+
let sanitized = input;
|
|
472
|
+
// Remover scripts e eventos
|
|
473
|
+
sanitized = sanitized.replace(/<script[^>]*>.*?<\/script>/gi, '');
|
|
474
|
+
sanitized = sanitized.replace(/on\w+\s*=\s*["'][^"']*["']/gi, '');
|
|
475
|
+
sanitized = sanitized.replace(/javascript:/gi, '');
|
|
476
|
+
// Validar links
|
|
477
|
+
sanitized = sanitized.replace(/href=["']([^"']*)["']/gi, (match, url) => {
|
|
478
|
+
try {
|
|
479
|
+
const parsed = new URL(url);
|
|
480
|
+
if (this.allowedSchemes.has(parsed.protocol.slice(0, -1))) {
|
|
481
|
+
return match;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
catch {
|
|
485
|
+
// URL relativa - permitir
|
|
486
|
+
return match;
|
|
487
|
+
}
|
|
488
|
+
return 'href="#"';
|
|
489
|
+
});
|
|
490
|
+
// Limitar tamanho
|
|
491
|
+
if (sanitized.length > 10000) {
|
|
492
|
+
sanitized = sanitized.slice(0, 10000) + '... [truncated]';
|
|
493
|
+
}
|
|
494
|
+
return sanitized;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Validar e normalizar input
|
|
498
|
+
*/
|
|
499
|
+
normalize(input) {
|
|
500
|
+
// Normalizar Unicode
|
|
501
|
+
let normalized = input.normalize('NFC');
|
|
502
|
+
// Remover caracteres de controle (exceto whitespace)
|
|
503
|
+
normalized = normalized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
|
|
504
|
+
// Normalizar whitespace
|
|
505
|
+
normalized = normalized.replace(/\s+/g, ' ').trim();
|
|
506
|
+
return normalized;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// ─── Security Manager ────────────────────────────────────────────
|
|
510
|
+
export class SecurityManager {
|
|
511
|
+
encryption;
|
|
512
|
+
permissions;
|
|
513
|
+
audit;
|
|
514
|
+
threats;
|
|
515
|
+
sanitizer;
|
|
516
|
+
identity;
|
|
517
|
+
localEncryptionId;
|
|
518
|
+
localEncryptionPublicKey;
|
|
519
|
+
localKeyGenerated = false;
|
|
520
|
+
constructor(identityOrConfig) {
|
|
521
|
+
const hasIdentity = this.isIdentity(identityOrConfig);
|
|
522
|
+
this.identity = hasIdentity ? identityOrConfig : undefined;
|
|
523
|
+
const config = hasIdentity ? undefined : identityOrConfig;
|
|
524
|
+
this.encryption = new E2EEncryption(config?.encryption);
|
|
525
|
+
this.permissions = new PermissionManager();
|
|
526
|
+
this.audit = new AuditLogger();
|
|
527
|
+
this.threats = new ThreatDetector();
|
|
528
|
+
this.sanitizer = new ContentSanitizer();
|
|
529
|
+
if (this.identity) {
|
|
530
|
+
this.localEncryptionId = this.identity.did;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Pipeline completo de segurança para mensagem
|
|
535
|
+
*/
|
|
536
|
+
async processIncoming(envelope) {
|
|
537
|
+
// 1. Verificar ameaças
|
|
538
|
+
const threatCheck = this.threats.analyzeEnvelope(envelope);
|
|
539
|
+
if (threatCheck.threat && threatCheck.severity === 'critical') {
|
|
540
|
+
await this.audit.log({
|
|
541
|
+
type: 'violation',
|
|
542
|
+
severity: 'critical',
|
|
543
|
+
actor: envelope.from.did,
|
|
544
|
+
resource: 'network',
|
|
545
|
+
action: 'receive',
|
|
546
|
+
result: 'blocked',
|
|
547
|
+
details: { threat: threatCheck }
|
|
548
|
+
});
|
|
549
|
+
return { allowed: false, reason: threatCheck.reason };
|
|
550
|
+
}
|
|
551
|
+
// 2. Sanitizar conteúdo
|
|
552
|
+
const sanitizedBody = this.sanitizer.sanitize(JSON.stringify(envelope.body));
|
|
553
|
+
const sanitized = {
|
|
554
|
+
...envelope,
|
|
555
|
+
body: JSON.parse(sanitizedBody)
|
|
556
|
+
};
|
|
557
|
+
// 3. Auditar
|
|
558
|
+
await this.audit.log({
|
|
559
|
+
type: 'access',
|
|
560
|
+
severity: 'info',
|
|
561
|
+
actor: envelope.from.did,
|
|
562
|
+
resource: `room:${envelope.room}`,
|
|
563
|
+
action: 'receive',
|
|
564
|
+
result: 'success',
|
|
565
|
+
details: { messageType: envelope.t }
|
|
566
|
+
});
|
|
567
|
+
return { allowed: true, sanitized };
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Verificar permissão completa
|
|
571
|
+
*/
|
|
572
|
+
async checkAccess(context, resource, action) {
|
|
573
|
+
// Verificar permissão
|
|
574
|
+
const hasPermission = this.permissions.checkPermission(context, resource, action);
|
|
575
|
+
if (!hasPermission) {
|
|
576
|
+
await this.audit.log({
|
|
577
|
+
type: 'access',
|
|
578
|
+
severity: 'warning',
|
|
579
|
+
actor: context.identity.did,
|
|
580
|
+
resource,
|
|
581
|
+
action,
|
|
582
|
+
result: 'blocked',
|
|
583
|
+
details: { reason: 'permission_denied' }
|
|
584
|
+
});
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
// ─── Compatibility Wrappers ───────────────────────────────────
|
|
590
|
+
async generateKeyPair(identityId) {
|
|
591
|
+
const id = identityId || this.localEncryptionId || this.identity?.did || `sec_${Date.now()}`;
|
|
592
|
+
const keyPair = await this.encryption.generateKeyPair(id);
|
|
593
|
+
if (!this.localEncryptionId) {
|
|
594
|
+
this.localEncryptionId = id;
|
|
595
|
+
}
|
|
596
|
+
if (id === this.localEncryptionId) {
|
|
597
|
+
this.localEncryptionId = id;
|
|
598
|
+
const exported = await crypto.subtle.exportKey('raw', keyPair.publicKey);
|
|
599
|
+
this.localEncryptionPublicKey = new Uint8Array(exported);
|
|
600
|
+
this.localKeyGenerated = true;
|
|
601
|
+
}
|
|
602
|
+
return keyPair;
|
|
603
|
+
}
|
|
604
|
+
async sign(message) {
|
|
605
|
+
if (!this.identity) {
|
|
606
|
+
throw new Error('Identity is required for signing');
|
|
607
|
+
}
|
|
608
|
+
const msgBase64 = Buffer.from(message).toString('base64');
|
|
609
|
+
const signatureBase64 = identitySign(this.identity, msgBase64);
|
|
610
|
+
return new Uint8Array(Buffer.from(signatureBase64, 'base64'));
|
|
611
|
+
}
|
|
612
|
+
async verify(message, signature, publicKey) {
|
|
613
|
+
const msgBase64 = Buffer.from(message).toString('base64');
|
|
614
|
+
const signatureBase64 = Buffer.from(signature).toString('base64');
|
|
615
|
+
return identityVerify(publicKey, msgBase64, signatureBase64);
|
|
616
|
+
}
|
|
617
|
+
async encrypt(plaintext, recipientPublicKey) {
|
|
618
|
+
const senderId = await this.ensureEncryptionIdentity();
|
|
619
|
+
const encodedPlaintext = Buffer.from(plaintext).toString('base64');
|
|
620
|
+
const encrypted = await this.encryption.encrypt(encodedPlaintext, recipientPublicKey, senderId);
|
|
621
|
+
if (!this.localEncryptionPublicKey) {
|
|
622
|
+
throw new Error('Missing sender public key');
|
|
623
|
+
}
|
|
624
|
+
return {
|
|
625
|
+
...encrypted,
|
|
626
|
+
senderPublicKey: this.localEncryptionPublicKey
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
async decrypt(message) {
|
|
630
|
+
const recipientId = await this.ensureEncryptionIdentity();
|
|
631
|
+
const decoded = await this.encryption.decrypt(message.ciphertext, message.nonce, message.senderPublicKey, recipientId);
|
|
632
|
+
return new Uint8Array(Buffer.from(decoded, 'base64'));
|
|
633
|
+
}
|
|
634
|
+
isIdentity(value) {
|
|
635
|
+
if (!value || typeof value !== 'object')
|
|
636
|
+
return false;
|
|
637
|
+
const candidate = value;
|
|
638
|
+
return (typeof candidate.did === 'string' &&
|
|
639
|
+
candidate.privateKey instanceof Uint8Array &&
|
|
640
|
+
candidate.publicKey instanceof Uint8Array);
|
|
641
|
+
}
|
|
642
|
+
async ensureEncryptionIdentity() {
|
|
643
|
+
const senderId = this.localEncryptionId || this.identity?.did || `sec_${Date.now()}`;
|
|
644
|
+
this.localEncryptionId = senderId;
|
|
645
|
+
if (!this.localKeyGenerated) {
|
|
646
|
+
await this.generateKeyPair(senderId);
|
|
647
|
+
}
|
|
648
|
+
return senderId;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
// Classes already exported via 'export class'
|
|
652
|
+
//# sourceMappingURL=security.js.map
|