- 2026.07.29 What Breaks When Apps Script Projects Sprawl
- 2026.07.16 Why a Daily Apps Script Trigger Suddenly Stops
- 2026.07.15 What Breaks When Customer Data Lives in Browser Storage
- 2026.07.07 When the Browser Throws Your Data Away: The Green Checkmark Lies
- 2026.07.07 Installed ≠ Working: the Backup Trigger Never Ran Once
- 2026.07.07 GAS Has No Gradual Rollout: One 'Correct' Check Locks Out Every User
- 2026.07.07 Sheets Turns Your Text Into a Date: When '2026-06' Blocks Completion Forever
- 2026.06.18 An App Where Many Write to One Sheet: Concurrency, Duplicates, and Export Design
- 2026.06.12 Don't Read Cells One at a Time: Service Calls Are the Real Cost
- 2026.06.11 Stuffing JSON or Images into a Sheet Cell Will Break It
- 2026.06.10 One Deploy Setting Decides Your Security Model: Execute as / Access You are here
- 2026.06.09 The 6-Minute GAS Limit: Chunk and Resume Before It Kills You
- 2026.06.08 PropertiesService Is Not a Database: the 9KB Wall
- 2026.06.07 Why Saving a Screen as an Image Freezes on Mobile
- 2026.06.06 Concurrent Writes to the Same Sheet Break: LockService and the 30-Run Ceiling
- 2026.06.05 Your /exec URL Must Not Change on Every Redeploy
- 2026.06.04 External Calls Fail Sometimes: Exponential Backoff and a Retry Budget
- 2026.06.03 HtmlService Apps: Shell First, Then Async Load and Mobile Back
- 2026.06.02 When to Graduate from GAS: Limit Signals and Moving to an External DB
- 2026.06.01 Adding a Brain and an Engine Room to GAS: Vertex AI and Cloud Run
One Deploy Setting Decides Your Security Model: Execute as / Access
The 'execute as' × 'who can access' combination sets data permissions and whether your code is exposed. Public apps need their own app-level auth.
A web app’s two deploy choices: “whose permissions it runs as” and “who can access it”: set the security model. Decide before coding and write it down, or you get a permissions accident later.
| Execute as | Meaning | Watch out |
|---|---|---|
| Me (deployer) | Always runs with deployer’s permissions | Wrong for apps needing per-user separation |
| User accessing | Runs with the visitor’s permissions | Needs user OAuth consent + missing-scope handling |
Why it matters
Deploy as “Me” to “Anyone” and anyone who visits can touch sheets and mail with the deployer’s permissions. Without separate input validation and auth, a public endpoint is wide open. The cost is a data leak or abuse.
Honest pitfall: source exposure
Deploying to “Anyone with a Google account” requires project sharing, so the customer can read your source. If closed source is a contract term, go with “Me + Anyone” plus your own app-level auth (email code + CacheService expiry, etc.).
Guard a public doPost with tiers
function doPost(e) {
try {
const body = JSON.parse(e.postData.contents || "{}");
if (!ALLOWED.has(body.action)) throw new Error("unknown action");
// never dispatch a function name straight from the payload (allowlist only)
return json({ ok: true, result: handle(body) });
} catch (err) {
return json({ ok: false, error: String(err.message || err) });
}
}
- minimal: API key + handler allowlist + input validation (internal / low-risk)
- standard: + timestamp/nonce replay prevention + AuditLog (writing ops data)
- hardened: + HMAC signature + per-function ACL + rate limit (public / sensitive)
Deeper: CORS via simple request
Calling GAS from an external browser, default to text/plain;charset=utf-8 instead of application/json to avoid preflight. The server parses e.postData.contents directly and returns errors as a JSON body, not via HTTP status.
One line to keep: fix the executor, access scope, and auth before deploying: and never run a function name straight from the payload.
Frequently asked questions
- What happens if you deploy Execute as Me to Anyone?
- Anyone who visits can touch sheets and mail with the deployer's permissions. Without separate input validation and auth, the public endpoint is wide open to abuse or data leaks.
- Does deploying to Anyone with a Google account expose your source code?
- Yes. That option requires project sharing, so visitors can read your source. If closed source is a contract term, use Me + Anyone combined with your own app-level auth instead.
- What is the minimum protection for a public doPost endpoint?
- The minimal tier requires an API key, a handler allowlist, and input validation. Never dispatch a function name taken directly from the payload; only call allowlisted handlers.
- 2026.07.29 What Breaks When Apps Script Projects Sprawl
- 2026.07.16 Why a Daily Apps Script Trigger Suddenly Stops
- 2026.07.15 What Breaks When Customer Data Lives in Browser Storage
- 2026.07.07 When the Browser Throws Your Data Away: The Green Checkmark Lies
- 2026.07.07 Installed ≠ Working: the Backup Trigger Never Ran Once
- 2026.07.07 GAS Has No Gradual Rollout: One 'Correct' Check Locks Out Every User
- 2026.07.07 Sheets Turns Your Text Into a Date: When '2026-06' Blocks Completion Forever
- 2026.06.18 An App Where Many Write to One Sheet: Concurrency, Duplicates, and Export Design
- 2026.06.12 Don't Read Cells One at a Time: Service Calls Are the Real Cost
- 2026.06.11 Stuffing JSON or Images into a Sheet Cell Will Break It
- 2026.06.10 One Deploy Setting Decides Your Security Model: Execute as / Access You are here
- 2026.06.09 The 6-Minute GAS Limit: Chunk and Resume Before It Kills You
- 2026.06.08 PropertiesService Is Not a Database: the 9KB Wall
- 2026.06.07 Why Saving a Screen as an Image Freezes on Mobile
- 2026.06.06 Concurrent Writes to the Same Sheet Break: LockService and the 30-Run Ceiling
- 2026.06.05 Your /exec URL Must Not Change on Every Redeploy
- 2026.06.04 External Calls Fail Sometimes: Exponential Backoff and a Retry Budget
- 2026.06.03 HtmlService Apps: Shell First, Then Async Load and Mobile Back
- 2026.06.02 When to Graduate from GAS: Limit Signals and Moving to an External DB
- 2026.06.01 Adding a Brain and an Engine Room to GAS: Vertex AI and Cloud Run