Build cross-platform desktop apps with JavaScript, HTML, and CSS
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Security features including safe storage, context isolation, sandboxing, and secure communication patterns.
Secure credential storage using system keychain services.
/**
* Allows access to simple encryption and decryption of strings for storage on the local machine
*/
interface SafeStorage {
/** Returns whether encryption is available */
isEncryptionAvailable(): boolean;
/** Encrypts a string and returns an encrypted buffer */
encryptString(plainText: string): Buffer;
/** Decrypts an encrypted buffer and returns the original string */
decryptString(encrypted: Buffer): string;
/** Sets the item data in the secure storage */
setPassword(service: string, account: string, password: string): void;
/** Retrieves the password from secure storage */
getPassword(service: string, account: string): string;
/** Deletes the password from secure storage */
deletePassword(service: string, account: string): boolean;
/** Returns the item data from secure storage */
findPassword(service: string): string;
}
declare const safeStorage: SafeStorage;Usage Examples:
const { safeStorage } = require('electron');
// Check if encryption is available
if (safeStorage.isEncryptionAvailable()) {
console.log('Safe storage is available');
// Encrypt sensitive data
const sensitiveData = 'user-secret-token';
const encrypted = safeStorage.encryptString(sensitiveData);
// Store encrypted data (you would save this to a file or database)
fs.writeFileSync('encrypted-data.bin', encrypted);
// Later, decrypt the data
const encryptedData = fs.readFileSync('encrypted-data.bin');
const decrypted = safeStorage.decryptString(encryptedData);
console.log('Decrypted:', decrypted);
// Store password in system keychain
safeStorage.setPassword('myapp', 'user@example.com', 'secret-password');
// Retrieve password
const password = safeStorage.getPassword('myapp', 'user@example.com');
console.log('Retrieved password:', password);
} else {
console.log('Safe storage is not available');
}Security through process isolation and controlled API access.
/**
* WebFrame controls the renderer frame's execution context
*/
interface WebFrame extends EventEmitter {
/** Inserts CSS into the current web page and returns a unique key for the inserted CSS */
insertCSS(css: string, options?: InsertCSSOptions): Promise<string>;
/** Removes the inserted CSS from the current web page */
removeInsertedCSS(key: string): Promise<void>;
/** Evaluates code in page */
executeJavaScript(code: string, userGesture?: boolean): Promise<any>;
/** Work like executeJavaScript but evaluates scripts in an isolated context */
executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean): Promise<any>;
/** Set the security origin, content security policy and name of the isolated world */
setIsolatedWorldInfo(worldId: number, info: IsolatedWorldInfo): void;
/** Resources will be loaded from this scheme regardless of the current page's Content Security Policy */
registerURLSchemeAsSecure(scheme: string): void;
/** Resources will be loaded from this scheme regardless of the current page's Content Security Policy */
registerURLSchemeAsBypassingCSP(scheme: string): void;
/** Registers the scheme as secure scheme */
registerURLSchemeAsPrivileged(scheme: string, options?: RegisterURLSchemeAsPrivilegedOptions): void;
/** Changes the zoom factor to the specified factor */
setZoomFactor(factor: number): void;
/** Returns the current zoom factor */
getZoomFactor(): number;
/** Changes the zoom level to the specified level */
setZoomLevel(level: number): void;
/** Returns the current zoom level */
getZoomLevel(): number;
/** Sets a provider for spell checking in input fields and text areas */
setSpellCheckProvider(language: string, provider: SpellCheckProvider): void;
/** The top frame in the frame hierarchy to which frame belongs */
readonly top: WebFrame;
/** The frame which opened frame, null if there's no opener or opener has been closed */
readonly opener: WebFrame | null;
/** The parent frame of frame, the property would be null if frame is the top frame in the frame hierarchy */
readonly parent: WebFrame | null;
/** The first child frame of frame, the property would be null if frame has no children */
readonly firstChild: WebFrame | null;
/** The next sibling frame of frame, the property would be null if frame is the last frame in the frame hierarchy */
readonly nextSibling: WebFrame | null;
/** A unique identifier for the frame */
readonly frameId: number;
/** The name of the frame */
readonly name: string;
/** The operating system pid of the process which owns this frame */
readonly processId: number;
/** The unique identifier for the frame's internal routing */
readonly routingId: number;
/** Whether the frame is out of process */
readonly isOutOfProcess: boolean;
/** The current URL of the frame */
readonly url: string;
}
declare const webFrame: WebFrame;Usage Examples:
// In renderer process with context isolation
const { webFrame } = require('electron');
// Execute code in isolated world
async function executeInIsolatedWorld() {
const worldId = 1000;
// Set up isolated world
webFrame.setIsolatedWorldInfo(worldId, {
securityOrigin: 'chrome-extension://my-extension-id',
csp: "script-src 'self' 'unsafe-inline';",
name: 'My Extension World'
});
// Execute code in isolated world
const result = await webFrame.executeJavaScriptInIsolatedWorld(worldId, [
{
code: `
// This code runs in isolation
window.myExtensionAPI = {
version: '1.0.0',
getData: () => 'isolated data'
};
'Isolated world setup complete';
`
}
]);
console.log('Isolated execution result:', result);
}
// Secure CSS injection
async function injectSecureCSS() {
const css = `
.secure-highlight {
background-color: yellow;
border: 2px solid red;
}
`;
const key = await webFrame.insertCSS(css, {
cssOrigin: 'user'
});
// Store key to remove later
window.injectedCSSKey = key;
}
// Remove injected CSS
async function removeSecureCSS() {
if (window.injectedCSSKey) {
await webFrame.removeInsertedCSS(window.injectedCSSKey);
delete window.injectedCSSKey;
}
}Secure preload script example:
// preload.js - Secure API exposure
const { contextBridge, ipcRenderer } = require('electron');
// Validate and sanitize input
function sanitizeInput(input) {
if (typeof input !== 'string') {
throw new Error('Input must be a string');
}
return input.replace(/[<>]/g, '');
}
// Expose secure API
contextBridge.exposeInMainWorld('secureAPI', {
// Safe methods
platform: process.platform,
version: process.versions.electron,
// Controlled file operations
readFile: (path) => {
// Validate path
if (!path || typeof path !== 'string') {
throw new Error('Invalid file path');
}
// Only allow certain directories
const allowedDirs = ['/app/data/', '/app/config/'];
const isAllowed = allowedDirs.some(dir => path.startsWith(dir));
if (!isAllowed) {
throw new Error('Access denied');
}
return ipcRenderer.invoke('secure-read-file', path);
},
// Safe IPC communication
sendMessage: (channel, data) => {
// Whitelist allowed channels
const allowedChannels = ['user-action', 'settings-update', 'log-message'];
if (!allowedChannels.includes(channel)) {
throw new Error('Channel not allowed');
}
// Sanitize data
const sanitizedData = sanitizeInput(JSON.stringify(data));
return ipcRenderer.invoke('secure-message', channel, JSON.parse(sanitizedData));
},
// Remove all listeners on cleanup
cleanup: () => {
ipcRenderer.removeAllListeners();
}
});CSP implementation for web content security.
/**
* Control the security policy of web content
*/
interface ContentSecurityPolicy {
/** Sets the Content Security Policy header */
setHeader(policy: string): void;
/** Gets the current Content Security Policy */
getHeader(): string | null;
/** Reports CSP violations */
reportViolation(violation: CSPViolation): void;
}
/**
* WebSecurity manages security settings for web content
*/
interface WebSecurity {
/** Disables web security warnings */
disable(): void;
/** Enables web security */
enable(): void;
/** Returns whether web security is enabled */
isEnabled(): boolean;
}Usage Examples:
// Set up Content Security Policy
const { BrowserWindow } = require('electron');
function createSecureWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
enableRemoteModule: false,
sandbox: true,
webSecurity: true,
allowRunningInsecureContent: false,
experimentalFeatures: false,
preload: path.join(__dirname, 'secure-preload.js')
}
});
// Set security headers
win.webContents.session.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': [
"default-src 'self'; " +
"script-src 'self' 'unsafe-inline'; " +
"style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data: https:; " +
"connect-src 'self' https: wss:; " +
"font-src 'self' data:; " +
"object-src 'none'; " +
"base-uri 'self'; " +
"form-action 'self';"
],
'X-Content-Type-Options': ['nosniff'],
'X-Frame-Options': ['DENY'],
'X-XSS-Protection': ['1; mode=block'],
'Referrer-Policy': ['strict-origin-when-cross-origin']
}
});
});
return win;
}
// Handle CSP violations
function setupCSPViolationHandler(session) {
session.webRequest.onBeforeRequest((details, callback) => {
// Log potential security violations
if (details.url.includes('evil-script.js')) {
console.warn('Blocked potentially malicious script:', details.url);
callback({ cancel: true });
return;
}
callback({});
});
}Control renderer process permissions and capabilities.
/**
* Permission management for web content
*/
interface PermissionManager {
/** Request permission for a specific capability */
requestPermission(permission: string, origin: string): Promise<boolean>;
/** Check if permission is granted */
checkPermission(permission: string, origin: string): boolean;
/** Revoke permission */
revokePermission(permission: string, origin: string): void;
/** List all granted permissions */
getGrantedPermissions(origin: string): string[];
}Usage Examples:
// Main process permission handling
const { session } = require('electron');
// Set up permission handlers
session.defaultSession.setPermissionRequestHandler((webContents, permission, callback, details) => {
const { requestingUrl, isMainFrame } = details;
// Define permission policies
const permissionPolicies = {
'notifications': {
allowedOrigins: ['https://myapp.com', 'https://api.myapp.com'],
requiresMainFrame: true
},
'camera': {
allowedOrigins: ['https://myapp.com'],
requiresUserGesture: true
},
'microphone': {
allowedOrigins: ['https://myapp.com'],
requiresUserGesture: true
},
'geolocation': {
allowedOrigins: [],
requiresUserGesture: true
}
};
const policy = permissionPolicies[permission];
if (!policy) {
console.log(`Permission denied: ${permission} (no policy)`);
callback(false);
return;
}
// Check origin
const origin = new URL(requestingUrl).origin;
if (!policy.allowedOrigins.includes(origin)) {
console.log(`Permission denied: ${permission} (origin not allowed)`);
callback(false);
return;
}
// Check if main frame is required
if (policy.requiresMainFrame && !isMainFrame) {
console.log(`Permission denied: ${permission} (not main frame)`);
callback(false);
return;
}
console.log(`Permission granted: ${permission} for ${origin}`);
callback(true);
});
// Set up permission check handler
session.defaultSession.setPermissionCheckHandler((webContents, permission, requestingOrigin, details) => {
// Always allow for our app's origin
if (requestingOrigin === 'https://myapp.com') {
return true;
}
// Block everything else
return false;
});interface WebSource {
code: string;
url?: string;
startLine?: number;
}
interface IsolatedWorldInfo {
securityOrigin?: string;
csp?: string;
name: string;
}
interface RegisterURLSchemeAsPrivilegedOptions {
secure?: boolean;
bypassCSP?: boolean;
allowServiceWorkers?: boolean;
supportsFetchAPI?: boolean;
corsEnabled?: boolean;
stream?: boolean;
}
interface SpellCheckProvider {
spellCheck: (words: string[], callback: (misspeltWords: string[]) => void) => void;
}
interface CSPViolation {
directive: string;
effectiveDirective: string;
originalPolicy: string;
sourceFile: string;
sample: string;
disposition: 'enforce' | 'report';
statusCode: number;
lineNumber: number;
columnNumber: number;
}Install with Tessl CLI
npx tessl i tessl/npm-electron