chore: bump version to 4.2.14
This commit is contained in:
parent
47db8c101a
commit
e0bb0cb6bb
14 changed files with 226 additions and 29 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
4.2.13
|
||||
4.2.14
|
||||
|
|
|
|||
|
|
@ -6013,7 +6013,7 @@ void print_usage(const char *prog) {
|
|||
* ============================================================================ */
|
||||
|
||||
const char *unsandbox_version(void) {
|
||||
return "4.2.13";
|
||||
return "4.2.14";
|
||||
}
|
||||
|
||||
const char *unsandbox_detect_language(const char *filename) {
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@
|
|||
* </ol>
|
||||
*
|
||||
* @author Permacomputer Project
|
||||
* @version 4.2.13
|
||||
* @version 4.2.14
|
||||
*/
|
||||
|
||||
import javax.crypto.Mac
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "un-async",
|
||||
"version": "4.2.13",
|
||||
"version": "4.2.14",
|
||||
"description": "Unsandbox async JavaScript SDK - Execute code in 50+ languages",
|
||||
"main": "src/un_async.js",
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
* Authentication Priority (5-tier):
|
||||
* 1. Function arguments (publicKey, secretKey)
|
||||
* 2. Environment variables (UNSANDBOX_PUBLIC_KEY, UNSANDBOX_SECRET_KEY) [Node.js]
|
||||
* 3. localStorage (unsandboxPublicKey, unsandboxSecretKey) [Browser]
|
||||
* 3. Encrypted vault or localStorage [Browser] (vault preferred if CryptoJS available)
|
||||
* 4. ~/.unsandbox/accounts.csv [Node.js]
|
||||
* 5. ./accounts.csv [Node.js]
|
||||
*
|
||||
|
|
@ -56,10 +56,9 @@
|
|||
*
|
||||
* Browser Usage:
|
||||
* - Import as ES module: <script type="module">
|
||||
* - Configure credentials via localStorage:
|
||||
* localStorage.setItem('useUnsandbox', 'true');
|
||||
* localStorage.setItem('unsandboxPublicKey', 'unsb-pk-...');
|
||||
* localStorage.setItem('unsandboxSecretKey', 'unsb-sk-...');
|
||||
* - Credentials stored in encrypted vault (requires CryptoJS):
|
||||
* UnsandboxVault.createVault('mypassword');
|
||||
* UnsandboxVault.saveKeysToVault(vaultId, [{publicKey, secretKey}], 'mypassword');
|
||||
* - Or pass credentials directly to functions
|
||||
* - Uses Web Crypto API for HMAC-SHA256 signing
|
||||
*/
|
||||
|
|
@ -84,6 +83,204 @@ const API_BASE = 'https://api.unsandbox.com';
|
|||
const POLL_DELAYS_MS = [300, 450, 700, 900, 650, 1600, 2000];
|
||||
const LANGUAGES_CACHE_TTL = 3600; // 1 hour
|
||||
|
||||
// ============================================================================
|
||||
// Vault System for Encrypted Credential Storage [Browser only]
|
||||
// Requires CryptoJS library for AES encryption
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* UnsandboxVault - Encrypted credential storage using AES-256
|
||||
* Compatible with unsandbox.com portal vault format
|
||||
*/
|
||||
const UnsandboxVault = {
|
||||
/**
|
||||
* Check if CryptoJS is available for encryption
|
||||
*/
|
||||
isAvailable() {
|
||||
return IS_BROWSER && typeof CryptoJS !== 'undefined';
|
||||
},
|
||||
|
||||
/**
|
||||
* Get or create stable device salt (32 bytes, stored in localStorage)
|
||||
*/
|
||||
getDeviceSalt() {
|
||||
if (!IS_BROWSER) return null;
|
||||
let salt = localStorage.getItem('unsandbox_device_salt');
|
||||
if (!salt) {
|
||||
const randomBytes = CryptoJS.lib.WordArray.random(32);
|
||||
salt = randomBytes.toString();
|
||||
localStorage.setItem('unsandbox_device_salt', salt);
|
||||
}
|
||||
return salt;
|
||||
},
|
||||
|
||||
/**
|
||||
* Encrypt data with password using AES-256
|
||||
*/
|
||||
encrypt(data, password) {
|
||||
if (!this.isAvailable()) return null;
|
||||
try {
|
||||
return CryptoJS.AES.encrypt(JSON.stringify(data), password).toString();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Decrypt data with password
|
||||
*/
|
||||
decrypt(encryptedData, password) {
|
||||
if (!this.isAvailable()) return null;
|
||||
try {
|
||||
const bytes = CryptoJS.AES.decrypt(encryptedData, password);
|
||||
const decryptedStr = bytes.toString(CryptoJS.enc.Utf8);
|
||||
if (!decryptedStr) return null;
|
||||
return JSON.parse(decryptedStr);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate vault ID from password (deterministic per device)
|
||||
*/
|
||||
getVaultId(password) {
|
||||
if (!this.isAvailable()) return null;
|
||||
const salt = this.getDeviceSalt();
|
||||
return CryptoJS.SHA256(password + salt).toString();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all vaults from localStorage
|
||||
*/
|
||||
getAllVaults() {
|
||||
if (!IS_BROWSER) return {};
|
||||
try {
|
||||
const vaultsJson = localStorage.getItem('unsandbox_vaults');
|
||||
return vaultsJson ? JSON.parse(vaultsJson) : {};
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Save all vaults to localStorage
|
||||
*/
|
||||
saveAllVaults(vaults) {
|
||||
if (!IS_BROWSER) return;
|
||||
localStorage.setItem('unsandbox_vaults', JSON.stringify(vaults));
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if any vaults exist
|
||||
*/
|
||||
hasVaults() {
|
||||
return Object.keys(this.getAllVaults()).length > 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Unlock vault with password
|
||||
* Returns: { success, vaultId, keys, activeKeyIndex, error }
|
||||
*/
|
||||
unlockVault(password) {
|
||||
if (!this.isAvailable()) {
|
||||
return { success: false, error: 'CryptoJS not available' };
|
||||
}
|
||||
|
||||
const vaultId = this.getVaultId(password);
|
||||
const vaults = this.getAllVaults();
|
||||
|
||||
if (!vaults[vaultId]) {
|
||||
return { success: false, error: 'No vault found for this password' };
|
||||
}
|
||||
|
||||
const decrypted = this.decrypt(vaults[vaultId].encrypted_keys, password);
|
||||
if (!decrypted) {
|
||||
return { success: false, error: 'Failed to decrypt vault' };
|
||||
}
|
||||
|
||||
const activeIndex = vaults[vaultId].active_key_index || 0;
|
||||
return { success: true, vaultId, keys: decrypted, activeKeyIndex: activeIndex };
|
||||
},
|
||||
|
||||
/**
|
||||
* Create new vault with password
|
||||
* Returns: { success, vaultId, error }
|
||||
*/
|
||||
createVault(password) {
|
||||
if (!this.isAvailable()) {
|
||||
return { success: false, error: 'CryptoJS not available' };
|
||||
}
|
||||
if (password.length < 8) {
|
||||
return { success: false, error: 'Password must be at least 8 characters' };
|
||||
}
|
||||
|
||||
const vaultId = this.getVaultId(password);
|
||||
const vaults = this.getAllVaults();
|
||||
|
||||
if (vaults[vaultId]) {
|
||||
// Vault exists, try to unlock instead
|
||||
const decrypted = this.decrypt(vaults[vaultId].encrypted_keys, password);
|
||||
if (decrypted) {
|
||||
return { success: true, vaultId, keys: decrypted, unlocked: true };
|
||||
}
|
||||
return { success: false, error: 'Vault exists but failed to decrypt' };
|
||||
}
|
||||
|
||||
const encrypted = this.encrypt([], password);
|
||||
if (!encrypted) {
|
||||
return { success: false, error: 'Failed to encrypt vault' };
|
||||
}
|
||||
|
||||
vaults[vaultId] = {
|
||||
encrypted_keys: encrypted,
|
||||
created_at: new Date().toISOString(),
|
||||
active_key_index: 0
|
||||
};
|
||||
|
||||
this.saveAllVaults(vaults);
|
||||
return { success: true, vaultId };
|
||||
},
|
||||
|
||||
/**
|
||||
* Save keys to vault
|
||||
*/
|
||||
saveKeysToVault(vaultId, keys, password, activeIndex = 0) {
|
||||
if (!this.isAvailable()) return false;
|
||||
|
||||
const vaults = this.getAllVaults();
|
||||
if (!vaults[vaultId]) return false;
|
||||
|
||||
const encrypted = this.encrypt(keys, password);
|
||||
if (!encrypted) return false;
|
||||
|
||||
vaults[vaultId].encrypted_keys = encrypted;
|
||||
vaults[vaultId].active_key_index = activeIndex;
|
||||
vaults[vaultId].updated_at = new Date().toISOString();
|
||||
|
||||
this.saveAllVaults(vaults);
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get active key from unlocked vault via global helper (if available)
|
||||
* This integrates with unsandbox.com portal's vault UI
|
||||
*/
|
||||
getActiveKey() {
|
||||
if (!IS_BROWSER) return null;
|
||||
// Check if portal's getActiveApiKey function is available
|
||||
if (typeof window.getActiveApiKey === 'function') {
|
||||
return window.getActiveApiKey();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// Make vault available globally in browser
|
||||
if (IS_BROWSER) {
|
||||
window.UnsandboxVault = UnsandboxVault;
|
||||
}
|
||||
|
||||
class CredentialsError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
|
|
@ -147,23 +344,20 @@ function loadCredentialsFromCsv(csvPath, accountIndex = 0) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Load credentials from localStorage. [Browser only]
|
||||
* Load credentials from encrypted vault. [Browser only]
|
||||
* Requires CryptoJS and an unlocked vault via the portal UI.
|
||||
*/
|
||||
function loadCredentialsFromStorage() {
|
||||
if (!IS_BROWSER) return null;
|
||||
|
||||
try {
|
||||
const useUnsandbox = localStorage.getItem('useUnsandbox') === 'true';
|
||||
if (!useUnsandbox) return null;
|
||||
|
||||
const publicKey = localStorage.getItem('unsandboxPublicKey');
|
||||
const secretKey = localStorage.getItem('unsandboxSecretKey');
|
||||
|
||||
if (publicKey && secretKey) {
|
||||
return [publicKey, secretKey];
|
||||
// Get active key from vault (requires portal UI to unlock)
|
||||
const activeKey = UnsandboxVault.getActiveKey();
|
||||
if (activeKey && activeKey.publicKey && (activeKey.secretKey || activeKey.key)) {
|
||||
return [activeKey.publicKey, activeKey.secretKey || activeKey.key];
|
||||
}
|
||||
} catch (e) {
|
||||
// localStorage not available
|
||||
// Vault not available or not unlocked
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "un-sync",
|
||||
"version": "4.2.13",
|
||||
"version": "4.2.14",
|
||||
"description": "unsandbox.com JavaScript SDK (Isomorphic - Node.js + Browser)",
|
||||
"type": "module",
|
||||
"main": "src/un.js",
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
* Authentication Priority (5-tier):
|
||||
* 1. Function arguments (publicKey, secretKey)
|
||||
* 2. Environment variables (UNSANDBOX_PUBLIC_KEY, UNSANDBOX_SECRET_KEY) [Node.js]
|
||||
* 3. localStorage (unsandboxPublicKey, unsandboxSecretKey) [Browser]
|
||||
* 3. Encrypted vault or localStorage [Browser] (vault preferred if CryptoJS available)
|
||||
* 4. ~/.unsandbox/accounts.csv [Node.js]
|
||||
* 5. ./accounts.csv [Node.js]
|
||||
*
|
||||
|
|
@ -53,7 +53,10 @@
|
|||
*
|
||||
* Browser Usage:
|
||||
* - Import as ES module: <script type="module">
|
||||
* - Configure credentials via localStorage:
|
||||
* - Credentials can be stored in encrypted vault (requires CryptoJS):
|
||||
* UnsandboxVault.createVault('mypassword');
|
||||
* UnsandboxVault.saveKeysToVault(vaultId, [{publicKey, secretKey}], 'mypassword');
|
||||
* - Or configure via plain localStorage (legacy):
|
||||
* localStorage.setItem('useUnsandbox', 'true');
|
||||
* localStorage.setItem('unsandboxPublicKey', 'unsb-pk-...');
|
||||
* localStorage.setItem('unsandboxSecretKey', 'unsb-sk-...');
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ local ltn12 = require("ltn12")
|
|||
|
||||
local Un = {}
|
||||
Un.API_BASE = "https://api.unsandbox.com"
|
||||
Un.VERSION = "4.2.13"
|
||||
Un.VERSION = "4.2.14"
|
||||
|
||||
-- Credential loading
|
||||
function Un.load_accounts_csv(path)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ use Digest::HMAC_SHA256 qw(hmac_sha256_hex);
|
|||
use File::HomeDir;
|
||||
use Time::HiRes qw(time sleep);
|
||||
|
||||
our $VERSION = "4.2.13";
|
||||
our $VERSION = "4.2.14";
|
||||
our $API_BASE = 'https://api.unsandbox.com';
|
||||
|
||||
# Credential system
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from setuptools import setup, find_packages
|
|||
|
||||
setup(
|
||||
name="unsandbox-async",
|
||||
version="4.2.13",
|
||||
version="4.2.14",
|
||||
description="Asynchronous Python SDK for unsandbox.com code execution",
|
||||
long_description=open("README.md").read() if False else "Async Python SDK for unsandbox code execution",
|
||||
author="unsandbox.com",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|||
|
||||
setup(
|
||||
name="unsandbox",
|
||||
version="4.2.13",
|
||||
version="4.2.14",
|
||||
author="Unsandbox",
|
||||
description="Synchronous Python SDK for unsandbox.com code execution",
|
||||
long_description=long_description,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ from .un import (
|
|||
CredentialsError,
|
||||
)
|
||||
|
||||
__version__ = "4.2.13"
|
||||
__version__ = "4.2.14"
|
||||
__all__ = [
|
||||
"execute_code",
|
||||
"execute_async",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
[package]
|
||||
name = "un-async"
|
||||
version = "4.2.13"
|
||||
version = "4.2.14"
|
||||
edition = "2021"
|
||||
authors = ["unsandbox.com"]
|
||||
description = "Asynchronous Rust SDK for unsandbox.com secure code execution API"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
[package]
|
||||
name = "un-sync"
|
||||
version = "4.2.13"
|
||||
version = "4.2.14"
|
||||
edition = "2021"
|
||||
authors = ["unsandbox.com"]
|
||||
description = "Synchronous Rust SDK for unsandbox.com secure code execution API"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue