's scripts.run method introduces severe security risks. This article presents a novel sandboxing proposal designed specifically for the scripts.run method, using ggsrun as the orchestrator to execute code safely and efficiently. By performing in-memory token replacement and uploading a separate, alphabetically-prioritized guard file, this approach achieves robust API-level containment. Guided by ggsrun's automated backup and default rollback lifecycle (exe1), the remote environment is immediately restored, providing a clean, dependency-free security model for AI-driven Workspace automation.
Introduction
The emergence of autonomous AI agents utilizing the Model Context Protocol (MCP) or persistent CLI runtimes has transformed development workflows. These agents can write, test, compile, and execute code statefully to automate operations. However, executing dynamic, LLM-generated code in an enterprise productivity suite like Google Workspace presents severe security challenges.
When an AI agent interacts with Google Workspace to execute Google Apps Script, utilizing the 's scripts.run method without containment is highly risky. To address this, we need a sandboxing solution capable of intercepting and validating security-sensitive operations at the API level before they execute on Google Cloud.
However, implementing and managing such a sandbox manually—handling in-memory token replacement, compiling separate wrapper scripts, uploading files, and cleaning up afterward—adds massive overhead and complexity. This is where ggsrun is positioned: not merely as a runner, but as a high-performance orchestration engine that automates this entire sandboxing lifecycle into a single, seamless, and efficient transaction (the exe1 process).
The Evolution of GAS Sandboxing
To address these security risks, the search for a secure Google Apps Script execution environment has progressed through three major architectural milestones.
First, we explored local emulation in "A Fake Sandbox for Google Apps Script" to run scripts locally against simulated Workspace structures. By parsing the Abstract Syntax Tree (AST) of the generated code and redirecting sensitive Google APIs to local mocks, we proved that strict containment policies could be enforced statically and instantaneously. This mock-based sandbox was highly capable, demonstrating that unverified code could be validated before hitting the cloud.
Second, we moved from local emulation to stateful cloud-based interception. In "A Developer's Guide to Agent Hooks in Antigravity CLI" ).
By performing token replacement and compiling a separate _for_sandbox_gas.gs wrapper file entirely in-memory, this proposed native sandbox provides robust security without local disk changes or external dependencies. In this architecture, ggsrun acts as the vital orchestration layer. It automatically handles the pre-execution remote backup, injects the sandbox wrappers, triggers the 's scripts.run method (or if the process is terminated by the user via Ctrl+C), a deferred rollback automatically restores the remote project to its original state, deleting the temporary _for_sandbox_gas.gs file. If the developer wishes to keep the uploaded files on the remote server, they must explicitly pass the --undeleteScript or --ud flag.
Comparison of Sandboxing Architectures
The table below contrasts simulated mocks, legacy external hooks, and the proposed native built-in sandbox:
| Architectural Metric | gas-fakes (Simulated Mock) | Legacy Agent Hooks (External JS) | Proposed Native Sandbox |
|---|---|---|---|
| Execution Runtime | Synthetic Node.js Mock | Remote GAS Cloud (V8) | Remote GAS Cloud (V8) |
| Stateful Execution | No (Emulated / Stateless) | Yes (Actual Google Workspace) | Yes (Actual Google Workspace) |
| Interception Method | Local JS mock libraries | AST parsing & disk file rewriting | Native Go in-memory parser replacement |
| Disk Mutations | None | High (Rewrites local script files) | None (Purely in-memory code transformation) |
| Dependency Footprint | High (npm install, Node modules) | High (Node.js, acorn, walk, fs) | Zero (Self-contained, static Go binary) |
| Rollback Resilience | N/A | Brittle (Fails on SIGINT/Crash) | Robust (Deferred Go signal trap recovery) |
| Enforcement Scope | Limited to test suites | Locked to Antigravity CLI hooks | Universal (Active across CLI, scripts, & MCP) |
Workflow 1: Legacy Agent Hooks Execution Lifecycle
The legacy hook model relied on Antigravity's client-side hook architecture to intercept the execution tool, parsing and modifying the script files on the developer's local hard drive before sending them to the remote Apps Script project.
Workflow 2: Native ggsrun Sandbox Execution Lifecycle
In the native implementation, ggsrun intercepts calls, loads whitelist rules, backs up remote code in-memory, replaces standard service identifiers, uploads the token-replaced scripts along with a separate _for_sandbox_gas.gs wrapper file to Google Cloud, executes the target function under safe V8-level wraps via the
(Separate Sandbox Script)
// === SANDBOX SECURITY GUARD INJECTED ===
function createSafeWrapper(original, overrides) { ... }
var _wrappedSpreadsheetApp = (function(global) {
var allowedFileIds = ["1SheetId_ExampleXYZ_999"];
return createSafeWrapper(SpreadsheetApp, {
openById: function(id) {
if (!allowedFileIds.includes(id)) {
throw new Error("Sandbox Runtime Blocked: Spreadsheet ID '" + id + "' is not whitelisted.");
}
return SpreadsheetApp.openById(id);
}
});
})(this);
var _wrappedUrlFetchApp = (function(global) {
var allowedUrls = ["https://api.example.com/v1/health"];
var blockedUrls = [];
// (Pattern matching & URL verification logic...)
return createSafeWrapper(UrlFetchApp, {
fetch: function(url, ...args) {
checkUrl(url); // Verifies URL is whitelisted and not blacklisted
return UrlFetchApp.fetch.apply(UrlFetchApp, [url, ...args]);
}
});
})(this);
// === END OF SANDBOX SECURITY GUARD ===
File 2: my_script.gs (User Script with Token Replacement)
// Original Script (Statically Replaced and Safe)
function main() {
var sheet = _wrappedSpreadsheetApp.openById("1SheetId_ExampleXYZ_999");
sheet.appendRow([new Date(), "Connected successfully!"]);
var response = _wrappedUrlFetchApp.fetch("https://api.example.com/v1/health");
return response.getContentText();
}
Anatomy of the Security Wrapper (_for_sandbox_gas.gs)
The heart of the runtime isolation is the static guard script _for_sandbox_gas.gs. This script runs within the remote Google Apps Script V8 compiler environment and wraps native APIs using precise proxy mechanics:
The Interception Flow
Code Example: Internal Wrapping Mechanism
Here is a simplified demonstration of how the sandbox intercepts and wraps the native Google Apps Script classes:
// How the sandbox intercepts and wraps SpreadsheetApp.openById
var _wrappedSpreadsheetApp = (function () {
// 1. Injected Whitelist config from sandbox_config.json
var allowedFileIds = ["1SheetId_ExampleXYZ_999"];
// 2. Clone prototype chain to preserve all native methods and properties
var wrapper = createSafeWrapper(SpreadsheetApp, {
// 3. Override sensitive methods with security checks
openById: function (id) {
if (!allowedFileIds.includes(id)) {
throw new Error(
"Sandbox Runtime Blocked: Accessed file ID '" +
id +
"' is not whitelisted.",
);
}
// 4. Delegate to the original native method if whitelisted
return SpreadsheetApp.openById(id);
},
});
return wrapper;
})();
1. Prototype Chain Cloning (createSafeWrapper)
Google Apps Script native classes (such as SpreadsheetApp or DriveApp) have complex inheritance and custom behaviors. Declaring a naive object mock breaks features or throws internal V8 conversion errors.
To bypass this, createSafeWrapper(original, overrides) takes the original global handle and crawls its entire prototype chain recursively using Object.getPrototypeOf() and Object.getOwnPropertyNames(). It dynamically copies and creates matching properties on the wrapper object. It preserves natural Javascript getters and setters via Object.defineProperty() while applying overriding hooks only on the critical, security-sensitive Methods specified in the overrides map.
2. Google Drive Iterator Wrapping
A typical method to harvest files in Google Drive is calling DriveApp.getFiles() and looping through them. To block unauthorized directory traversal without breaking legitimate script loops, _wrappedDriveApp intercepts getFiles(), getFilesByName(), and searchFiles(). It returns a custom wrapped iterator proxy:
function wrapIterator(iter) {
return {
hasNext: function () {
return iter.hasNext();
},
next: function () {
var item = iter.next();
var id = item.getId();
if (!allowedFileIds.includes(id) && !allowedFolderIds.includes(id)) {
throw new Error(
"Sandbox Runtime Blocked: Accessed resource ID '" +
id +
"' is not whitelisted.",
);
}
return item;
},
};
}
If an AI-generated script attempts to access unauthorized files, the iterator catches the violation immediately at the .next() loop step, throwing a runtime security exception before any file metadata is leaked.
3. Gmail and Mail Egress Locks
To protect user privacy and block outbound spam, the wrappers _wrappedGmailApp and _wrappedMailApp:
- Intercept
sendEmail()andcreateDraft(), verifying the recipient string argument againstallowedEmails. - Explicitly throw "prohibited" exceptions for all inbox traversal operations such as
getInboxThreads(),search(),getSpamThreads(), andgetTrashThreads(). This guarantees that private email histories are safe from scanning or extraction.
4. Outbound URL Fetch Pattern Matching
To enforce rigorous egress network rules, _wrappedUrlFetchApp maps fetch() and fetchAll(). It processes the target URL string against whitelisted (allowedUrls) and blacklisted (blockedUrls) patterns.
The matching engine translates glob wildcards (like https://api.github.com/repos/*) into anchored, case-insensitive V8 regular expressions:
function matchPattern(url, pattern) {
var escaped = pattern.replace(/[-\/\\^$+?.()|[\]{}]/g, "\\$&");
var regexStr = "^" + escaped.replace(/\*/g, ".*") + "$";
var regex = new RegExp(regexStr, "i");
return regex.test(url);
}
Explicit blacklists are verified first; if a URL is explicitly blacklisted, or fails to match any whitelisted wildcards, the outbound fetch is immediately blocked, neutralizing data harvesting pipelines.
5. REST-level Advanced Google Services Scanning
Advanced Apps Script developers often bypass standard higher-level objects like DriveApp or SpreadsheetApp by directly utilizing the REST-based Advanced Google Services (such as calling the raw Drive or Sheets service maps).
The sandbox closes this escape route. It wraps all Advanced Services (Drive, Sheets, Docs, Slides, Gmail, and Calendar) using a dynamic scanner. The wrapper intercepts every service method call and scans incoming argument arrays. If any string matches standard Google ID patterns (such as a 20+ character alphanumeric ID or an email structure), the proxy checks them against the global whitelists. If a match is absent, the execution is instantly terminated, neutralizing advanced REST-level bypass attempts.
Usage (Installation & Setup)
's scripts.run method. We successfully implemented this process, conducted rigorous experiments, and verified its effectiveness in providing robust, whitelist-controlled security and automated rollback containment.
Acknowledgement
- Google Cloud credits are provided for this project. #AgenticArchitect #GoogleAntigravity
SOCIAL SHARE CARD GENERATOR