chore: bump version to 4.2.14

This commit is contained in:
russell@unturf.com 2026-01-23 09:26:25 -05:00
parent 47db8c101a
commit e0bb0cb6bb
14 changed files with 226 additions and 29 deletions

View file

@ -1 +1 @@
4.2.13
4.2.14

View file

@ -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) {

View file

@ -72,7 +72,7 @@
* </ol>
*
* @author Permacomputer Project
* @version 4.2.13
* @version 4.2.14
*/
import javax.crypto.Mac

View file

@ -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",

View file

@ -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;

View file

@ -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",

View file

@ -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-...');

View file

@ -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)

View file

@ -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

View file

@ -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",

View file

@ -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,

View file

@ -27,7 +27,7 @@ from .un import (
CredentialsError,
)
__version__ = "4.2.13"
__version__ = "4.2.14"
__all__ = [
"execute_code",
"execute_async",

View file

@ -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"

View file

@ -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"