Downloading Data from a Locked Google Sheet

  • URL Parsing: Extracts the spreadsheetId and gid (tab ID) via Regex from the active window.location.href.
  • API Interception: Bypasses the sheet’s HTML5 <canvas> rendering engine and fetches raw data directly from Google’s internal Visualization API (/gviz/tq?tqx=out:csv).
  • Memory Blobbing: Converts the plaintext CSV response into a local, client-side memory object (Blob) and assigns it a temporary blob: URI.
  • CSP Evasion: Injects a programmatic <a> element into the DOM using document.createElement() and textContent. This circumvents Google’s strict Trusted Types Content Security Policy (CSP) that blocks innerHTML parsing.

Execution Protocol

  1. Navigate to the target Google Sheet.
  2. Select the specific tab required (ensures the gid parameter in the URL is accurate).
  3. Open browser Developer Tools: Cmd+Option+J (Mac) or Ctrl+Shift+J (Windows).
  4. Paste the script into the Console tab and execute (press Enter).
  5. Review the [SheetExtract] verbose console logs for fetch status and payload size.
  6. Click the floating green ⬇️ Download CSV button injected at the bottom right of the viewport.

console.log(“[%s] Init GViz data extraction sequence”, “SheetExtract”);
(async () => {
try {
const match = window.location.href.match(/\/d\/([a-zA-Z0-9-_]+)/);
const gidMatch = window.location.href.match(/gid=([0-9]+)/);

if (!match) {
  console.error("[%s] Fatal: Spreadsheet ID not found in URL", "SheetExtract");
  return;
}

const sheetId = match[1];
const gid = gidMatch ? gidMatch[1] : "0";
const endpoint = `https://docs.google.com/spreadsheets/d/${sheetId}/gviz/tq?tqx=out:csv&gid=${gid}`;

console.log("[%s] Target Endpoint: %s", "SheetExtract", endpoint);
console.log("[%s] Initiating fetch request...", "SheetExtract");

const response = await fetch(endpoint);
if (!response.ok) throw new Error(`HTTP Error ${response.status}`);

const csvText = await response.text();
console.log("[%s] Payload received. Size: %d bytes", "SheetExtract", csvText.length);

const blob = new Blob([csvText], { type: 'text/csv' });
const objectUrl = URL.createObjectURL(blob);

console.log("[%s] Blob URL created: %s", "SheetExtract", objectUrl);
console.log("[%s] Injecting safe DOM node (bypassing TrustedTypes sink)", "SheetExtract");

// Construct safe DOM node
const btn = document.createElement('a');
btn.href = objectUrl;
btn.download = `Sheet_${sheetId}_GID_${gid}.csv`;
btn.textContent = `⬇️ Download CSV (GID: ${gid})`;

// Apply inline styles
Object.assign(btn.style, {
  position: 'fixed',
  bottom: '24px',
  right: '24px',
  padding: '16px 24px',
  backgroundColor: '#188038',
  color: '#ffffff',
  fontFamily: 'Roboto, Arial, sans-serif',
  fontSize: '14px',
  fontWeight: '600',
  textDecoration: 'none',
  borderRadius: '8px',
  boxShadow: '0 4px 6px rgba(0,0,0,0.3)',
  zIndex: '2147483647',
  cursor: 'pointer'
});

document.body.appendChild(btn);
console.log("[%s] Success: Download button injected into viewport bottom-right.", "SheetExtract");

} catch (e) {
console.error(“[%s] Execution failure: %s”, “SheetExtract”, e.message, e);
}
})();

Leave a Reply

Your email address will not be published. Required fields are marked *