Skip to content

Instantly share code, notes, and snippets.

@tanaikech
Last active September 1, 2025 13:14
Show Gist options
  • Save tanaikech/0896697c24cf4b9e1b1038303cf734ae to your computer and use it in GitHub Desktop.
Save tanaikech/0896697c24cf4b9e1b1038303cf734ae to your computer and use it in GitHub Desktop.
A Fake-Sandbox for Google Apps Script: A Feasibility Study on Securely Executing Code Generated by Gemini CLI

A Fake-Sandbox for Google Apps Script: A Feasibility Study on Securely Executing Code Generated by Gemini CLI

Abstract

Generating Google Apps Script (GAS) with Gemini CLI from natural language introduces security risks due to broad permissions. This report investigates a "Fake-Sandbox" using the gas-fakes library, translating GAS calls into granularly-scoped API requests to securely execute scripts created from user prompts.

Introduction

1. Background: Generative AI and the Challenge of Secure Script Execution

The emergence of Generative AI now makes it possible to generate executable scripts directly from natural language instructions, particularly through interfaces like the Gemini CLI. For locally executable languages such as JavaScript (Node.js) and Python, code generated from a simple prompt can be run directly. However, Google Apps Script (GAS) presents a unique challenge as it operates within Google's server-side infrastructure. Executing locally generated GAS code requires the remote invocation of a server-side function via the scripts.run method of the Apps Script API. This process highlights the critical need for a sandbox environment to manage permissions effectively and mitigate the risks associated with executing code generated from natural language, which can sometimes produce unintended or insecure outcomes.

2. Existing Sandbox Strategies and Their Inherent Limitations

Implementing a sandbox—an isolated, secure environment for code execution—for Google Apps Script involves several potential strategies, each with significant drawbacks:

  • Dedicated Execution Accounts: A highly secure method involves creating a dedicated Google account solely for script execution. This approach effectively contains any potential impact within the confines of that account. However, the administrative overhead and cost associated with managing numerous accounts render this solution impractical for large-scale deployments.

  • Service Accounts: While service accounts are a standard for programmatic authentication within Google Cloud, they are not currently permitted to directly execute GAS functions through the Apps Script API, making this a non-viable option.

  • Permission Scoping with OAuth: A more promising strategy involves restricting permissions using OAuth scopes. The https://www.googleapis.com/auth/drive.file scope is particularly appealing, as it limits an application's access to only those files it creates or that a user explicitly opens with that application. This approach, however, introduces a fundamental trade-off. Core GAS services, such as SpreadsheetApp, often require broad scopes (e.g., https://www.googleapis.com/auth/spreadsheets) that grant access to all of a user's spreadsheets. This conflict between granular permissions and the requirements of high-level GAS services makes it difficult to adhere to the principle of least privilege.

  • Execution Interception: In theory, a wrapper script could intercept the execution of a Google Apps Script to enforce restrictions on specific files and methods. By doing so, it would be possible to run the script in a more controlled manner. However, no such mechanism for intercepting and sandboxing GAS execution currently exists.

3. A Novel Approach: A Fake-Sandbox with gas-fakes

The Node.js package gas-fakes, developed by Bruce McPherson, offers a potential solution to this dilemma. This library functions as a local runtime environment for GAS, translating calls to standard GAS services (e.g., SpreadsheetApp.openById("###")) into their equivalent Google API requests (e.g., Google Sheets API). This abstraction allows developers to write scripts using familiar GAS syntax while the underlying operations are executed via Google APIs that support more granular permission controls.

This mechanism makes it possible to operate on a specific spreadsheet using standard GAS methods while restricting access to designated files and methods. This approach effectively decouples the developer experience from the underlying permission model, combining the security benefits of fine-grained permissions with the development efficiency of native GAS services. This capability suggests its potential as a "Fake-Sandbox" for executing code generated from natural language with tightly controlled access.

4. Objective of This Report

This report aims to examine the feasibility of constructing this Fake-Sandbox environment for Google Apps Script. We will investigate the use of the gas-fakes library as a means to safely execute scripts generated from natural language prompts using Gemini CLI by intercepting and managing their execution. While gas-fakes does not yet have comprehensive coverage of all GAS classes and methods, our preliminary findings suggest that this approach represents a promising pathway toward secure and scalable automation with Google Apps Script.

Conceptual Workflow

The following diagram illustrates the process of executing a Google Apps Script generated by the Gemini CLI within the fake-sandbox environment:

sequenceDiagram
    participant User
    participant Gemini_CLI as Gemini CLI
    participant Local as Local Environment (Node.js)
    participant GSF as gas-fakes Library
    participant G_API as Google APIs <br/>(e.g., Sheets API)
    participant G_App as Google Workspace App <br/>(e.g., Google Sheet)

    User->>Gemini_CLI: 1. Provides a prompt in natural language <br/> "Get the value from cell C1 in Sheet B of Spreadsheet A"
    Gemini_CLI->>Gemini_CLI: 2. Generates Google Apps Script code based on the prompt
    Gemini_CLI->>Local: 3. Executes the generated script in the local environment
    Local->>Local: 4. Script enables sandbox mode <br/> `ScriptApp.__behavior.sandBoxMode = true`
    Local->>GSF: 5. Invokes a GAS method call from the script <br/> e.g., SpreadsheetApp.openById('...').getRange(...)
    GSF->>GSF: 6. Intercepts the GAS call and translates it <br/> into a corresponding Google API request
    GSF->>G_API: 7. Makes an authenticated API call using credentials from the local environment
    G_API->>G_API: 8. Verifies credentials and scope permissions
    G_API->>G_App: 9. Executes the operation on the target application <br/> (e.g., reads the specific cell in the Google Sheet)
    G_App-->>G_API: 10. Returns the requested data (e.g., cell value)
    G_API-->>GSF: 11. Returns the raw API response (JSON)
    GSF->>GSF: 12. Formats the API response to mimic <br/> the expected GAS object or return type
    GSF-->>Local: 13. Returns the formatted result to the running script
    Local-->>User: 14. Displays the final output of the script to the user
Loading

Practical Implementation

1. Install Gemini CLI

Instructions for installing the Gemini CLI can be found at https://github.com/google-gemini/gemini-cli.

Gemini CLI Configuration

To execute Google Apps Script within the fake-sandbox, you need to configure the Gemini CLI. Add the following text to the GEMINI.md file located in the .gemini directory:

# Generate and run Google Apps Script

Generate the script by referring to the sample scripts at "StackOverflow" obtained using a Google search. If an error occurs, modify the script by referring to StackOverflow again.

In order to run the script of Google Apps Script, use "@mcpher/gas-fakes". The base of the script is as follows: "BaseScript". Google Apps Script is safely run in the sandbox. As additional information, in the current stage, gas-fakes cannot use `Logger.log`. So, please use `console.log` instead of it.

<BaseScript>
```javascript
import "@mcpher/gas-fakes/main.js"
ScriptApp.__behavior.sandBoxMode = true;

//---
// Generated Google Apps Script
//---

ScriptApp.__behavior.trash();
```
</BaseScript>

Please replace the comment lines in "BaseScript" with the generated Google Apps Script. And, run the script by `node [filename.js]`.

The prompt Generate the script by referring to the sample scripts at "StackOverflow" obtained using a Google search. is included to guide Gemini towards generating code that uses current and effective methods.

2. Install gas-fakes

You can find instructions for installing gas-fakes at https://github.com/brucemcpherson/gas-fakes.

3. Example Use Cases

Run the Gemini CLI by entering $ gemini in your terminal.

Example 1: Create Spreadsheet and Populate with Values

Prompt:

Create the following application using Google Apps Script and run the script.
1. Create a new Google Spreadsheet as "temp spreadsheet".
2. Insert a new sheet as "temp" to the created Spreadsheet.
3. Put a value of `[["header1", "header2", "header3"], ["a2", "b2","c2"]]` into the "temp" sheet.

Result: The Gemini CLI will generate and execute a script that safely performs these actions within the fake-sandbox.

The generated script is as follows. The core logic within the createAndPopulateSpreadsheet function is also compatible with the standard Google Apps Script editor.

import "@mcpher/gas-fakes/main.js";
ScriptApp.__behavior.sandBoxMode = true;

//---
// Generated Google Apps Script
//---

function createAndPopulateSpreadsheet() {
  // 1. Create a new Google Spreadsheet as "temp spreadsheet".
  const ss = SpreadsheetApp.create("temp spreadsheet");
  console.log(`Spreadsheet created: ${ss.getName()} (ID: ${ss.getId()})`);

  // 2. Insert a new sheet as "temp" to the created Spreadsheet.
  // Get the default sheet and rename it, or create a new one and delete the default.
  // Let's create a new one and delete the default for clarity.
  const tempSheet = ss.insertSheet("temp");
  console.log(`Created new sheet: ${tempSheet.getName()}`);

  // Delete the default "Sheet1" if it exists
  const defaultSheet = ss.getSheetByName("Sheet1");
  if (defaultSheet) {
    ss.deleteSheet(defaultSheet);
    console.log("Deleted default 'Sheet1'.");
  }

  // 3. Put a value of [["header1", "header2", "header3"], ["a2", "b2","c2"]] into the "temp" sheet.
  const data = [
    ["header1", "header2", "header3"],
    ["a2", "b2", "c2"],
  ];

  const range = tempSheet.getRange(1, 1, data.length, data[0].length);
  range.setValues(data);
  console.log("Values have been added to the 'temp' sheet.");
  console.log(`URL: ${ss.getUrl()}`);
}

createAndPopulateSpreadsheet();

ScriptApp.__behavior.trash();

Example 2: Create Spreadsheet and Move to a Folder

Prompt:

Create the following application using Google Apps Script and run the script.
1. Create a new Google Spreadsheet as "temp spreadsheet".
2. Create a new folder of "tempFolder".
3. Move the Spreadsheet to the folder.

Result: The script is generated and executed securely, demonstrating file and folder manipulation.

The generated script can also be used in the Google Apps Script editor.

import "@mcpher/gas-fakes/main.js";
ScriptApp.__behavior.sandBoxMode = true;

//---
// Generated Google Apps Script
//---

function createSpreadsheetCreateFolderMoveFile() {
  const spreadsheetName = "temp spreadsheet";
  const folderName = "tempFolder";

  // 1. Create a new folder
  const newFolder = DriveApp.createFolder(folderName);
  console.log(`Folder '${folderName}' created with ID: ${newFolder.getId()}`);

  // 2. Create a new spreadsheet in the root (initially)
  const newSpreadsheet = SpreadsheetApp.create(spreadsheetName);
  console.log(
    `Spreadsheet '${spreadsheetName}' created with ID: ${newSpreadsheet.getId()}`
  );

  // 3. Get the file object of the newly created spreadsheet
  const file = DriveApp.getFileById(newSpreadsheet.getId());

  // 4. Move the spreadsheet to the new folder
  file.moveTo(newFolder);
  console.log(
    `Spreadsheet '${spreadsheetName}' moved to folder '${folderName}'.`
  );

  // Optional: Log URLs for verification
  console.log(`New Folder URL: ${newFolder.getUrl()}`);
  console.log(`New Spreadsheet URL: ${newSpreadsheet.getUrl()}`);
}

// Call the main function
createSpreadsheetCreateFolderMoveFile();

ScriptApp.__behavior.trash();

Example 3: Create, Populate, and Organize a Spreadsheet

Prompt:

Create the following application using Google Apps Script and run the script.
1. Create a new Google Spreadsheet as "temp spreadsheet".
2. Insert a new sheet as "temp" to the created Spreadsheet.
3. Put a value of `[["header1", "header2", "header3"], ["a2", "b2","c2"]]` into the "temp" sheet.
4. Freeze the 1st row and 1st column of the "temp" sheet.
5. Create a new folder of "tempFolder".
6. Move the Spreadsheet to the folder.

Result: This more complex workflow is also handled securely by the fake-sandbox.

The generated script is as follows:

import "@mcpher/gas-fakes/main.js";
ScriptApp.__behavior.sandBoxMode = true;

function main() {
  try {
    // 1. Create a new Google Spreadsheet as "temp spreadsheet".
    const spreadsheetName = "temp spreadsheet";
    const spreadsheet = SpreadsheetApp.create(spreadsheetName);
    const spreadsheetId = spreadsheet.getId();
    console.log(
      `Created spreadsheet: ${spreadsheetName} (ID: ${spreadsheetId})`
    );

    // 2. Insert a new sheet as "temp" to the created Spreadsheet.
    const newSheetName = "temp";
    const sheet = spreadsheet.insertSheet(newSheetName);
    console.log(`Inserted new sheet: ${newSheetName}`);

    // Remove the default "Sheet1" if it exists and is empty
    const defaultSheet = spreadsheet.getSheetByName("Sheet1");
    if (defaultSheet) {
      spreadsheet.deleteSheet(defaultSheet);
      console.log("Deleted default 'Sheet1'.");
    }

    // 3. Put a value of `[["header1", "header2", "header3"], ["a2", "b2","c2"]]` into the "temp" sheet.
    const data = [
      ["header1", "header2", "header3"],
      ["a2", "b2", "c2"],
    ];
    sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
    console.log("Inserted values into the sheet.");

    // 4. Freeze the 1st row and 1st column of the "temp" sheet.
    sheet.setFrozenRows(1);
    sheet.setFrozenColumns(1);
    console.log("Frozen first row and first column.");

    // 5. Create a new folder of "tempFolder".
    const folderName = "tempFolder";
    const folder = DriveApp.createFolder(folderName);
    const folderId = folder.getId();
    console.log(`Created new folder: ${folderName} (ID: ${folderId})`);

    // 6. Move the Spreadsheet to the folder.
    const file = DriveApp.getFileById(spreadsheetId);
    file.moveTo(folder);
    console.log(
      `Moved spreadsheet '${spreadsheetName}' to folder '${folderName}'.`
    );
  } catch (e) {
    console.error("An error occurred: " + e.toString());
  }
}

main(); // Call the main function
ScriptApp.__behavior.trash();

Example 4: Create a Document and Add a Paragraph

Prompt:

Create the following application using Google Apps Script and run the script.
1. Create a new Google Document as "temp document".
2. Add a paragraph as a text of "sample".

Result: This example demonstrates that the fake-sandbox can also handle other Google Workspace applications like Google Docs.

The generated script is as follows:

import "@mcpher/gas-fakes/main.js";
ScriptApp.__behavior.sandBoxMode = true;

//---
// Generated Google Apps Script
//---

function createDocumentAndAddText() {
  const docTitle = "temp document";
  const textContent = "sample";

  try {
    const doc = DocumentApp.create(docTitle);
    const body = doc.getBody();
    body.appendParagraph(textContent);
    console.log(
      `Successfully created document: ${doc.getName()} (ID: ${doc.getId()})`
    );
    console.log(`Document URL: ${doc.getUrl()}`);
  } catch (e) {
    console.error(`Failed to create document: ${e.message}`);
  }
}

// Call the function to execute the script
createDocumentAndAddText();

ScriptApp.__behavior.trash();

Summary

This feasibility study demonstrates a promising approach to securely executing Google Apps Script code generated by AI. The key takeaways are as follows:

  • Addresses a Critical Security Gap: The "Fake-Sandbox" concept directly addresses the security risks of executing AI-generated GAS. By translating GAS calls into API requests, it becomes possible to restrict the script's access to specific files and methods, a significant improvement over the broad permissions typically required by native GAS services.
  • Leverages gas-fakes for Translation: The gas-fakes library is the core component of this approach, translating high-level GAS service calls into specific, permission-controlled Google API requests.
  • Maintains Developer Experience: Scripts can be written using familiar GAS syntax, decoupling the development process from the underlying security implementation.
  • Enables Secure AI-Powered Automation: By integrating with the Gemini CLI, this method allows users to safely automate Google Workspace tasks using natural language prompts.
  • Feasibility and Future Potential: While the gas-fakes library's coverage is not yet exhaustive, this study confirms the viability of the fake-sandbox approach and highlights its potential for broader application as the library evolves.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment