diff --git a/.env.template b/.env.template index ba1cbdc2..c7df2ea8 100644 --- a/.env.template +++ b/.env.template @@ -1,3 +1,4 @@ # CyberChef Cloud Operations Tests # Copy this file to `.env` and fill in your valid GCP API Key if you want to run the Nightwatch browser tests against live Cloud APIs. CYBERCHEF_GCP_TEST_KEY="YOUR_API_KEY_HERE" +CLIENT_ID="YOUR_CLIENT_ID_HERE" diff --git a/.gitignore b/.gitignore index a161bb1b..f049c572 100755 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ src/node/index.mjs tests/browser/output/* .node-version .env +.antigravity diff --git a/docs/AddingCloudOperations.md b/docs/AddingCloudOperations.md new file mode 100644 index 00000000..150629bf --- /dev/null +++ b/docs/AddingCloudOperations.md @@ -0,0 +1,122 @@ +# Guide: Adding New Google Cloud Operations to CyberChef + +With the introduction of the Google Identity Services (GIS) Web Application PKCE flow and strict Content Security Policies (CSP), adding new Google Cloud operations requires a few extra steps compared to standard CyberChef operations. + +This guide outlines the exact process for creating, securing, and testing a new Google Cloud integration. + +## 1. Create the Operation File +Create your new operation in `src/core/operations/` (e.g., `GCloudVisionAnalyze.mjs`). +Include the standard CyberChef operation scaffolding and ensure you import the authentication utilities from `GoogleCloud.mjs`: + +```javascript +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import { applyGCPAuth } from "../lib/GoogleCloud.mjs"; + +class GCloudVisionAnalyze extends Operation { + constructor() { + super(); + this.name = "GCloud Vision Analyze"; + this.module = "Cloud"; + this.description = "Analyzes an image using Google Cloud Vision."; + this.infoURL = "https://cloud.google.com/vision/docs"; + this.inputType = "ArrayBuffer"; + this.outputType = "JSON"; + this.args = [ + /* Your args */ + ]; + } +``` + +## 2. Leverage `applyGCPAuth` +**Do not** add authentication fields (like API keys or tokens) directly into your new operation's `args`. +Instead, instruct the user (via the `description`) to place the `Authenticate Google Cloud` operation at the top of their recipe. + +Inside your `run` method, use `applyGCPAuth` to seamlessly inject the credentials. This handles API Keys, Personal Access Tokens (PATs), and the GIS OAuth Tokens interchangeably: + +```javascript + async run(input, args) { + let url = "https://vision.googleapis.com/v1/images:annotate"; + let headers = { "Content-Type": "application/json" }; + + // 1. Inject Auth into URL or Headers + try { + [url, headers] = applyGCPAuth(url, headers); + } catch (e) { + throw new OperationError(e.message); + } + + // 2. Perform Fetch + const response = await fetch(url, { + method: "POST", + headers: headers, + body: JSON.stringify({ /* payload */ }) + }); + + // 3. Handle response... + } +``` + +## 3. Update Content Security Policy (CSP) +CyberChef utilizes strict CSP headers to prevent XSS attacks from exfiltrating the OAuth tokens stored in `sessionStorage`. +If your new operation contacts a new Google API endpoint, **the request will be blocked by the browser** unless you whitelist it. + +1. Open `src/web/html/index.html`. +2. Locate the `` tag in the `
`. +3. Find the `connect-src` directive. +4. Append your new API endpoint (e.g., `https://vision.googleapis.com`). + +Example: +```html + +``` + +## 4. Testing End-to-End (Nightwatch) + +Because the PKCE OAuth flow relies on a popup protected by anti-bot measures (reCAPTCHA) from Google Identity Services, **it cannot be fully automated** with headless browsers like Nightwatch. + +For E2E tests, you **must use** either an API Key or a PAT (`CYBERCHEF_GCP_TEST_KEY` or `CYBERCHEF_GCP_TEST_TOKEN`). + +1. Open `tests/browser/03_cloud_ops.js`. +2. Create your test suite. +3. Use the PAT or API Key fallback logic present in other tests to inject credentials into your dummy recipe: + +```javascript + "GCloud Vision Analyze: Translates image to text": function (browser) { + let testToken = process.env.CYBERCHEF_GCP_TEST_TOKEN; + + // Fallback to gcloud SDK if CYBERCHEF_GCP_TEST_TOKEN isn't set manually + if (!testToken || testToken === "YOUR_OAUTH_TOKEN_HERE") { + try { + testToken = require("child_process").execSync("gcloud auth print-access-token", { stdio: "pipe", encoding: "utf-8" }).trim(); + } catch (e) { + console.log("No valid token found. Skipping live Vision API test."); + return; + } + } + + browserUtils.loadRecipeConfig(browser, [ + { + op: "Authenticate Google Cloud", + args: [ + "Personal Access Token (PAT)", + { option: "UTF8", string: testToken }, + "cyberchefcloud", + true + ] + }, + { + op: "GCloud Vision Analyze", + args: [] + } + ], "Dummy Input data..."); + + browser.waitForElementNotVisible("#snackbar-container", 6000); + browserUtils.bake(browser); + browser.pause(5000); + + // Assert output + } +``` + +Run tests with `npm run test:browser` or `npx nightwatch tests/browser/03_cloud_ops.js` (while `npm run start` is running locally). diff --git a/docs/GoogleCloudSetup.md b/docs/GoogleCloudSetup.md index cb291136..fc8ded46 100644 --- a/docs/GoogleCloudSetup.md +++ b/docs/GoogleCloudSetup.md @@ -1,37 +1,58 @@ # Google Cloud Setup Guide for CyberChef -To use Google Cloud capabilities (like Google Translate) within CyberChef, you need to configure a Google Cloud Project and obtain an authentication string (either an API Key or an OAuth Token). +To use Google Cloud capabilities (like Google Translate, Speech-to-Text, or Cloud Storage) within CyberChef, you need to configure a Google Cloud Project and obtain an authentication string. We strongly recommend using **OAuth 2.0 (Web Application: PKCE)** as it is the most secure method for browser-based applications like CyberChef. -## Method 1: API Key (Recommended for simplicity) +By setting up a personal Cloud Project, your CyberChef instance communicates directly with your Google Cloud without a middleman. -1. **Create a Project:** - - Go to the [Google Cloud Console](https://console.cloud.google.com/). - - Click on the project dropdown at the top and select **New Project**. - - Name your project (e.g., `cyberchefcloud`) and click **Create**. +## Method 1: OAuth 2.0 (Web Application: PKCE) - Highly Recommended -2. **Enable the API:** - - In the Cloud Console search bar, type **"Cloud Translation API"** and select it. - - Click **Enable**. - - *(Note: You will need to have billing enabled on your Google Cloud account for the Translation API, even for the free tier).* +This method uses your Google account and Google Identity Services to grant CyberChef temporary, secure access to your Google Cloud Project. Because CyberChef is a client-side architecture web app without a backend server, we use the Authorization Code Flow with Proof Key for Code Exchange (PKCE). -3. **Create the API Key:** - - Navigate to **APIs & Services > Credentials** in the left sidebar. - - Click **+ CREATE CREDENTIALS** at the top and select **API Key**. - - Your API Key will be generated. Copy this key; you will need it for the CyberChef "GCP Auth String" input. +### Step 1: Create a Project +1. Go to the [Google Cloud Console](https://console.cloud.google.com/). +2. Click on the project dropdown at the top and select **New Project**. +3. Name your project (e.g., `cyberchef-personal`) and click **Create**. -4. **Secure the API Key (CRITICAL):** - - Since CyberChef runs entirely in your browser, your API Key will be visible to anyone you share your CyberChef recipe with or who inspects the network traffic. you **MUST** restrict it. - - Click on the newly created API Key to edit its settings. - - Under **Application restrictions**, select **Websites**. - - Under **Website restrictions**, click **ADD**. - - Enter the URLs where your CyberChef instance is hosted (e.g., `https://gchq.github.io/CyberChef/*` or `http://localhost:8080/*` for local testing). - - Under **API restrictions**, select **Restrict key**. - - Check the box for **Cloud Translation API**. - - Click **SAVE**. +### Step 2: Enable Required APIs +You must enable the APIs for any operation you intend to use. +1. In the Cloud Console search bar, type the name of the API and select it, then click **Enable**. + - For **Google Translate**: Enable "Cloud Translation API" (requires billing enabled). + - For **Speech-to-Text**: Enable "Cloud Speech-to-Text API". + - For **Read/List Bucket**: Enable "Cloud Storage JSON API" (usually enabled by default). -## Method 2: Temporary OAuth Token (Recommended for Security) +### Step 3: Configure the OAuth Consent Screen +1. Navigate to **APIs & Services > OAuth consent screen** in the left sidebar. +2. Select **External** user type and click **Create**. +3. Fill in the required fields (App Name, User Support Email, Developer Contact Info). You can use your own email for all of these. Click **Save and Continue**. +4. Skip the **Scopes** section by clicking **Save and Continue**. +5. **CRITICAL STEP: Configure Test Users**. + - Navigate to **APIs & Services > OAuth consent screen** > **Audience** > **Test Users** > **Add Users** + - Type your own Google email address here (the one you use to login to the Cloud Console). + - *(Since we will leave the app in "Testing" state forever, only users listed here can authenticate).* + - **WARNING**: Do **NOT** click "Publish App". Leave the publishing status as "Testing". If you publish it, Google will require your app to undergo a verification process. -If you have the Google Cloud SDK (`gcloud`) installed locally and you are authorized in your project, you can generate a short-lived token to use instead of an API Key. This is much more secure because the token expires automatically. +### Step 4: Create OAuth Client ID +1. Navigate to **APIs & Services > Credentials** in the left sidebar. +2. Click **+ CREATE CREDENTIALS** at the top and select **OAuth client ID**. +3. For **Application type**, select **Web application**. +4. Name it something like "CyberChef PKCE Client". +5. Under **Authorized JavaScript origins**, click **ADD URI**. + - For local testing, add: `http://localhost:8080` + - For production, add your deployed CyberChef URL (e.g., `https://gchq.github.io`). +6. Click **Create**. +7. A box will appear with your **Client ID**. Copy this string. *(Note: You do not need the Client Secret).* + +### Step 5: Authenticate in CyberChef +1. In CyberChef, add the **Authenticate Google Cloud** operation to the TOP of your recipe. +2. Set "Auth Type" to **OAuth 2.0 (Web Application: PKCE)**. +3. Paste your Client ID into the "Credentials" box. +4. Click **Bake**. CyberChef will popup a secure Google Login window. Your session token is now securely stored in your browser's Session Storage for the duration of the tab being open. + +--- + +## Method 2: Temporary Personal Access Token (PAT) + +If you have the Google Cloud SDK (`gcloud`) installed locally and you are authorized in your project, you can generate a short-lived token to use. This is secure because the token expires automatically. 1. Ensure you are logged into your `gcloud` CLI: ```bash @@ -41,4 +62,26 @@ If you have the Google Cloud SDK (`gcloud`) installed locally and you are author ```bash gcloud auth print-access-token ``` -3. Copy the output token and paste it into the CyberChef "GCP Auth String" input, making sure to change the "Auth Type" dropdown to **OAuth Token**. +3. In CyberChef's **Authenticate Google Cloud** operation, set "Auth Type" to **Personal Access Token (PAT)** and paste the token output into the Credentials box. + +--- + +## Method 3: API Key (For Simple APIs only) + +*Warning: API Keys are not recommended for operations that access private data like Cloud Storage. They are best suited for simple APIs like Translation.* + +1. **Create the API Key:** + - Navigate to **APIs & Services > Credentials** in the left sidebar. + - Click **+ CREATE CREDENTIALS** at the top and select **API Key**. + - Your API Key will be generated. Copy this key. + +2. **Secure the API Key (CRITICAL):** + - Since CyberChef runs entirely in your browser, your API Key will be visible to anyone you share your CyberChef recipe with or who inspects the network traffic. you **MUST** restrict it. + - Click on the newly created API Key to edit its settings. + - Under **Application restrictions**, select **Websites**. + - Under **Website restrictions**, click **ADD**. + - Enter the URLs where your CyberChef instance is hosted (e.g., `https://gchq.github.io/CyberChef/*` or `http://localhost:8080/*` for local testing). + - Under **API restrictions**, select **Restrict key** and check the relevant API (e.g., **Cloud Translation API**). + - Click **SAVE**. + +3. In CyberChef's **Authenticate Google Cloud** operation, set "Auth Type" to **API Key** and paste the key into the Credentials box. diff --git a/docs/LocalTesting.md b/docs/LocalTesting.md index c4e94878..83de3b7f 100644 --- a/docs/LocalTesting.md +++ b/docs/LocalTesting.md @@ -58,7 +58,9 @@ npm run test CyberChef is equipped with Nightwatch for end-to-end testing of features running in a real Chromium browser. This is extremely useful for Cloud Operations that cannot be easily tested within the headless NodeJS environment. -### Setting up API Keys +**Important Note on Authentication:** Google Identity Services (GIS), the library used for the secure **OAuth 2.0 (Web Application: PKCE)** flow, contains strict anti-automation reCAPTCHA logic. Because of this, it is nearly impossible to automate the Google Login popup using standard E2E frameworks like Nightwatch. Therefore, all automated Nightwatch tests utilize either **API Keys** or **Personal Access Tokens (PATs)** to authenticate. Manual testing is still required to verify the PKCE Popup flow works as expected for human users. + +### Setting up Credentials To prevent live API keys from being leaked in CI/CD, the Cloud API browser tests rely on local environment variables: 1. Copy the `.env.template` file to `.env`: @@ -66,6 +68,7 @@ To prevent live API keys from being leaked in CI/CD, the Cloud API browser tests cp .env.template .env ``` 2. Open `.env` and replace `YOUR_API_KEY_HERE` with your actual Google Cloud API key for the `CYBERCHEF_GCP_TEST_KEY` variable. Ensure the API Key restrictions at console.cloud.google.com are set up to accept requests from `http://localhost:8080/*`. +3. If tests require a PAT, you must update `CYBERCHEF_GCP_TEST_TOKEN` with a fresh token obtained via `gcloud auth print-access-token` before running the tests. ### Running Nightwatch Tests diff --git a/docs/Troubleshooting.md b/docs/Troubleshooting.md new file mode 100644 index 00000000..7c3da17b --- /dev/null +++ b/docs/Troubleshooting.md @@ -0,0 +1,36 @@ +# Google Cloud Troubleshooting Guide + +## Error: "401 Anonymous caller" or "403 Permission 'storage.objects.list' denied" + +If you successfully authenticated with the **Authenticate Google Cloud** operation but received a `401` or `403` error when trying to run an operation like **GCloud List Bucket** or **GCloud Read File**, the issue is **Identity and Access Management (IAM)**. + +Even though your token is valid (Authentication), your Google Account does not have the necessary permissions (Authorization) to perform that specific action on that specific resource. + +### How to Fix IAM Permissions + +1. **Verify Your Google Login Account** + Make sure the email address you selected in the Google Login popup is the *exact same* email address that holds permissions in your Google Cloud Project. + +2. **Grant Storage Roles** + To read from Cloud Storage, your Google Account must be granted a role that contains the required permissions (like `storage.objects.list` and `storage.objects.get`). + + - Go to the [Google Cloud Console](https://console.cloud.google.com). + - Navigate to **IAM & Admin > IAM**. + - Find your email address in the list of principals. + - Click the pencil icon (Edit principal) next to your name. + - Click **Add Another Role**. + - To fully utilize the CyberChef Cloud Storage operations, add the **Storage Object Viewer** role (or **Storage Object Admin** if you plan to write files later). + - Click **Save**. + + *Note: IAM changes can take anywhere from 60 seconds to a few minutes to propagate across Google's infrastructure.* + +3. **Check Bucket-Level Permissions** + If the bucket belongs to a different project than your OAuth Client ID, or has "fine-grained" access control instead of "Uniform" access control, you may need to grant yourself permission directly on the bucket itself. + - Navigate to **Cloud Storage > Buckets**. + - Click on the bucket you are trying to read. + - Go to the **Permissions** tab. + - Click **Grant Access**. + - Add your Google email address and assign the **Storage Object Viewer** role. + +4. **Retry the Operation** + After waiting two minutes for IAM propagation, refresh CyberChef (this forces a new session and drops the old token). Re-authenticate with **Authenticate Google Cloud**, then bake the recipe again. diff --git a/src/core/lib/GoogleCloud.mjs b/src/core/lib/GoogleCloud.mjs index a8ff268c..04284c54 100644 --- a/src/core/lib/GoogleCloud.mjs +++ b/src/core/lib/GoogleCloud.mjs @@ -6,17 +6,38 @@ import OperationError from "../errors/OperationError.mjs"; +/** + * Global store for GCP credentials in this web worker session. + */ +globalThis.__gcpAuthStore = globalThis.__gcpAuthStore || null; + +/** + * Retrieves the currently active GCP credentials. + * @returns {Object|null} { authType, authString, quotaProject } + */ +export function get_gcp_credentials() { + if (globalThis.__gcpAuthStore) { + return globalThis.__gcpAuthStore; + } + return null; +} + +/** + * Sets the active GCP credentials for the web worker session. + * @param {Object} credObj { authType, authString, quotaProject } + */ +export function set_gcp_credentials(credObj) { + globalThis.__gcpAuthStore = credObj; +} + /** * Lists objects in a GCS bucket under a given prefix. * * @param {string} bucket - The GCS bucket name (without gs://). * @param {string} prefix - The folder prefix to filter by (e.g. "audio/"). - * @param {string} authType - "API Key" or "OAuth Token". - * @param {Object|string} authStringObj - The auth credential. - * @param {string} quotaProject - Optional quota project for ADC OAuth tokens. * @returns {Promise