xypriss-security
Version:
XyPriss Security is an advanced JavaScript security library designed for enterprise applications. It provides military-grade encryption, secure data structures, quantum-resistant cryptography, and comprehensive security utilities for modern web applicatio
348 lines (345 loc) • 12.8 kB
JavaScript
import { SecureRandom } from '../core/random/random-core.js';
import '../core/random/random-types.js';
import 'crypto';
import '../core/random/random-sources.js';
import 'nehonix-uri-processor';
import { bufferToHex, hexToBuffer } from '../utils/encoding.js';
import '../utils/memory/index.js';
import '../types.js';
import '../core/random/random-security.js';
import { Hash } from '../core/hash/hash-core.js';
import '../core/hash/hash-types.js';
import '../core/hash/hash-security.js';
import '../core/hash/hash-advanced.js';
import '../algorithms/hash-algorithms.js';
import https from 'https';
/**
* Canary Tokens Module
*
* This module provides functionality for creating and managing canary tokens,
* which are special tokens that can detect unauthorized access or data breaches.
*
* Canary tokens can be embedded in sensitive data or systems, and when accessed,
* they trigger alerts or other actions to notify of potential security breaches.
*/
// Store for registered canaries
const canaries = new Map();
// Store for canary callbacks
const callbacks = new Map();
/**
* Creates a canary token
*
* @param options - Canary options
* @returns Canary token
*/
function createCanary(options = {}) {
// Generate a unique ID
const id = bufferToHex(SecureRandom.getRandomBytes(16));
// Generate a key if not provided
const key = options.key || bufferToHex(SecureRandom.getRandomBytes(32));
// Create canary data
const canaryData = {
id,
created: Date.now(),
};
// Add expiration if provided
if (options.expiresIn) {
canaryData.expires = canaryData.created + options.expiresIn;
}
// Add context if provided
if (options.context) {
canaryData.context = options.context;
}
// Add notify URL if provided
if (options.notifyUrl) {
canaryData.notifyUrl = options.notifyUrl;
}
// Register the canary
canaries.set(id, canaryData);
// Register the callback if provided
if (options.callback) {
callbacks.set(id, options.callback);
}
// Create the token
const tokenData = JSON.stringify({
id,
key,
});
// Encrypt the token
return encryptCanary(tokenData, key);
}
/**
* Triggers a canary token
*
* @param token - Canary token to trigger
* @param context - Additional context for the trigger
* @returns True if the canary was triggered successfully
*/
function triggerCanary(token, context = {}) {
try {
// Decrypt the token
const tokenData = decryptCanary(token, "");
if (!tokenData) {
return false;
}
// Parse the token data
const { id, key } = JSON.parse(tokenData);
// Verify with the correct key
const verifiedData = decryptCanary(token, key);
if (!verifiedData) {
return false;
}
// Get the canary data
const canaryData = canaries.get(id);
if (!canaryData) {
return false;
}
// Check if expired
if (canaryData.expires && Date.now() > canaryData.expires) {
return false;
}
// Combine contexts
const triggerContext = {
...canaryData.context,
...context,
triggeredAt: Date.now(),
canaryId: id,
};
// Execute the callback if registered
const callback = callbacks.get(id);
if (callback) {
callback(triggerContext);
}
// Send notification if URL is provided
if (canaryData.notifyUrl) {
// Send an actual HTTP request to notify about the triggered canary
try {
const notificationData = JSON.stringify({
canaryId: id,
triggeredAt: Date.now(),
context: triggerContext,
});
// Use fetch API if available (browser environment)
if (typeof fetch === "function") {
fetch(canaryData.notifyUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Canary-Alert": "true",
},
body: notificationData,
}).catch((err) => {
console.error("Failed to send canary notification:", err);
});
}
else if (typeof XMLHttpRequest === "function") {
// Fallback to XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open("POST", canaryData.notifyUrl, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("X-Canary-Alert", "true");
xhr.send(notificationData);
}
else if (typeof require === "function") {
// Node.js environment
try {
// const https = ("https");
const url = new URL(canaryData.notifyUrl);
const options = {
hostname: url.hostname,
port: url.port || 443,
path: url.pathname + url.search,
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(notificationData),
"X-Canary-Alert": "true",
},
};
const req = https.request(options);
req.write(notificationData);
req.end();
}
catch (e) {
console.error("Failed to send canary notification in Node.js:", e);
}
}
else if (typeof window !== "undefined" &&
typeof window.XMLHttpRequest === "function") {
// Fallback to XMLHttpRequest in browser environments
const xhr = new window.XMLHttpRequest();
xhr.open("POST", canaryData.notifyUrl, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("X-Canary-Alert", "true");
xhr.onerror = () => {
console.error("Failed to send canary notification via XMLHttpRequest");
};
xhr.send(notificationData);
}
else {
// Use Node.js https module as a last resort
try {
const url = new URL(canaryData.notifyUrl);
const options = {
hostname: url.hostname,
port: url.port ||
(url.protocol === "https:" ? 443 : 80),
path: url.pathname + url.search,
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(notificationData),
"X-Canary-Alert": "true",
},
};
const req = https.request(options);
req.write(notificationData);
req.end();
}
catch (e) {
console.error("Failed to send canary notification in Node.js:", e);
}
}
}
catch (e) {
console.error("Error sending canary notification:", e);
}
}
return true;
}
catch (e) {
return false;
}
}
/**
* Creates a canary object that triggers when accessed
*
* @param target - Object to wrap with a canary
* @param options - Canary options
* @returns Proxy object that triggers the canary when accessed
*/
function createCanaryObject(target, options = {}) {
// Create a canary token
const canaryToken = createCanary(options);
// Create a proxy that triggers the canary when accessed
return new Proxy(target, {
get(obj, prop) {
// Trigger the canary
triggerCanary(canaryToken, {
action: "get",
property: String(prop),
timestamp: Date.now(),
});
// Return the property
return obj[prop];
},
set(obj, prop, value) {
// Trigger the canary
triggerCanary(canaryToken, {
action: "set",
property: String(prop),
timestamp: Date.now(),
});
// Set the property
obj[prop] = value;
return true;
},
});
}
/**
* Creates a canary function that triggers when called
*
* @param fn - Function to wrap with a canary
* @param options - Canary options
* @returns Function that triggers the canary when called
*/
function createCanaryFunction(fn, options = {}) {
// Create a canary token
const canaryToken = createCanary(options);
// Create a wrapper function that triggers the canary when called
const wrapper = function (...args) {
// Trigger the canary
triggerCanary(canaryToken, {
action: "call",
arguments: args,
timestamp: Date.now(),
});
// Call the original function
return fn.apply(this, args);
};
// Copy properties from the original function
Object.defineProperties(wrapper, Object.getOwnPropertyDescriptors(fn));
return wrapper;
}
/**
* Simple encryption function for canary tokens
*
* @param data - Data to encrypt
* @param key - Encryption key
* @returns Encrypted data
*/
function encryptCanary(data, key) {
// Convert data to bytes
const dataBytes = new TextEncoder().encode(data);
// Create a key stream by hashing the key repeatedly
const keyStream = new Uint8Array(dataBytes.length);
let hashResult = Hash.create(key, { outputFormat: "hex" });
let keyBlock = hexToBuffer(hashResult);
for (let i = 0; i < keyStream.length; i += keyBlock.length) {
// Copy as much of the key block as needed
const bytesToCopy = Math.min(keyBlock.length, keyStream.length - i);
keyStream.set(keyBlock.subarray(0, bytesToCopy), i);
// Generate the next key block
hashResult = Hash.create(bufferToHex(keyBlock), {
outputFormat: "hex",
});
keyBlock = hexToBuffer(hashResult);
}
// XOR the data with the key stream
const encrypted = new Uint8Array(dataBytes.length);
for (let i = 0; i < dataBytes.length; i++) {
encrypted[i] = dataBytes[i] ^ keyStream[i];
}
return bufferToHex(encrypted);
}
/**
* Simple decryption function for canary tokens
*
* @param encrypted - Encrypted data
* @param key - Decryption key
* @returns Decrypted data, or null if decryption fails
*/
function decryptCanary(encrypted, key) {
try {
// Convert encrypted data to bytes
const encryptedBytes = hexToBuffer(encrypted);
// Create a key stream by hashing the key repeatedly
const keyStream = new Uint8Array(encryptedBytes.length);
let hashResult = Hash.create(key, {
outputFormat: "hex",
});
let keyBlock = hexToBuffer(hashResult);
for (let i = 0; i < keyStream.length; i += keyBlock.length) {
// Copy as much of the key block as needed
const bytesToCopy = Math.min(keyBlock.length, keyStream.length - i);
keyStream.set(keyBlock.subarray(0, bytesToCopy), i);
// Generate the next key block
hashResult = Hash.create(bufferToHex(keyBlock), {
outputFormat: "hex",
});
keyBlock = hexToBuffer(hashResult);
}
// XOR the encrypted data with the key stream
const decrypted = new Uint8Array(encryptedBytes.length);
for (let i = 0; i < encryptedBytes.length; i++) {
decrypted[i] = encryptedBytes[i] ^ keyStream[i];
}
// Convert back to string
return new TextDecoder().decode(decrypted);
}
catch (e) {
return null;
}
}
export { createCanary, createCanaryFunction, createCanaryObject, triggerCanary };
//# sourceMappingURL=canary-tokens.js.map