id: gmail
name: Gmail
version: 1.0.0
description: "Gmail API + Directory API — mailbox search/read/send/trash/labels, attachments retrieval, vacation auto-reply, filters, forwarding addresses, delegates, and Workspace user administration; message ingestion (get_incidents) with an OCSF mapper; service-account auth with domain-wide delegation. Runs on a remote engine. Requires the Python 'PyJWT' and 'cryptography' libraries on the engine host (pip install pyjwt cryptography)."
changelog: "1.0.0 — Initial release: mailbox commands (search, get mail/thread, send with attachments, trash/permanent delete, label moves, attachments download, labels list), vacation auto-reply get/set, mail filters (add/list/remove), forwarding addresses and auto-forwarding, delegates, Workspace user administration (list/get/create/delete, password reset, directory visibility), and message ingestion with a bundled OCSF mapper."
category: email

# Authentication: Google service account with domain-wide delegation.
# Every command builds a JWT (RS256, signed locally with the service account
# private key from service_account_json), sets `sub` to the mailbox being
# impersonated, exchanges it at https://oauth2.googleapis.com/token
# (grant_type=jwt-bearer) for an access token, then calls the Gmail /
# Admin SDK Directory REST APIs with that bearer token.
# The service account's client ID must be granted the Gmail scopes (and the
# Directory scopes for user-administration commands) in the Google Workspace
# Admin console: Security → API controls → Domain-wide delegation.
config_schema:
  properties:
    service_account_json:
      type: string
      description: "Google service account key file content (full JSON) — the account must have domain-wide delegation enabled"
      x-soar-sensitive: true
    user_id:
      type: string
      description: "Default mailbox to impersonate — also the admin account for user-administration commands"
  required:
    - service_account_json
    - user_id

commands:
  # ── Ingestion ─────────────────────────────────────────────────────────────
  - id: get_incidents
    name: gmail-get-incidents
    description: "Fetch messages from a mailbox for ingestion (Gmail search query + time watermark). Returns {result:[flattened messages]}; use result as the alert rule results path."
    risk: read
    inputs_schema:
      properties:
        query: { type: string, description: "Gmail search query (e.g. is:unread in:inbox has:attachment)" }
        after: { type: string, description: "Lower time bound — epoch seconds, epoch ms or ISO8601 (incremental fetch watermark, appended as after:<epoch>)" }
        max: { type: number, description: "Maximum messages to fetch (default 50)" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: []
    outputs_schema: { properties: {} }
    ingest:
      results_path: result
      dedup_key: id
      incremental_field: after

  # ── Mailbox ───────────────────────────────────────────────────────────────
  - id: search
    name: gmail-search
    description: "Search messages with a Gmail query and return them flattened (headers, bodies, attachment metadata) plus a next_page_token for pagination."
    risk: read
    inputs_schema:
      properties:
        query: { type: string, description: "Gmail search query (e.g. from:alice@example.com subject:invoice newer_than:7d)" }
        max_results: { type: number, description: "Maximum messages to return (default 100, capped at 500)" }
        page_token: { type: string, description: "Page token from a previous call (next_page_token)" }
        label_id: { type: string, description: "Restrict to a single label ID (e.g. INBOX, SPAM or a user label ID from gmail-list-labels)" }
        include_spam_trash: { type: boolean, description: "Include messages from SPAM and TRASH" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: []
    outputs_schema: { properties: {} }
  - id: get_mail
    name: gmail-get-mail
    description: "Retrieve a single message by ID, flattened (subject/from/to/cc, text and HTML bodies, attachment metadata)."
    risk: read
    inputs_schema:
      properties:
        message_id: { type: string, description: "Gmail message ID" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: [message_id]
    outputs_schema: { properties: {} }
  - id: get_thread
    name: gmail-get-thread
    description: "Retrieve a conversation thread by ID with every message flattened."
    risk: read
    inputs_schema:
      properties:
        thread_id: { type: string, description: "Gmail thread ID" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: [thread_id]
    outputs_schema: { properties: {} }
  - id: send_mail
    name: gmail-send-mail
    description: "Send an email from the impersonated mailbox — plain text and/or HTML body, optional single attachment (base64), reply threading via In-Reply-To/References."
    inputs_schema:
      properties:
        to: { type: string, description: "Recipients, comma-separated" }
        cc: { type: string, description: "Cc recipients, comma-separated" }
        bcc: { type: string, description: "Bcc recipients, comma-separated" }
        subject: { type: string, description: "Message subject" }
        body: { type: string, description: "Plain-text body" }
        html_body: { type: string, description: "HTML body (sent alongside the plain-text part when both are given)" }
        reply_to: { type: string, description: "Reply-To header" }
        in_reply_to: { type: string, description: "In-Reply-To header (Message-ID of the message being answered — see message_id_header)" }
        references: { type: string, description: "References header (thread Message-ID chain)" }
        attachment_name: { type: string, description: "Attachment file name (with attachment_base64)" }
        attachment_base64: { type: string, description: "Attachment content, base64-encoded" }
        user_id: { type: string, description: "Sender mailbox to impersonate (default from instance config)" }
      required: [to, subject]
    outputs_schema: { properties: {} }
  - id: delete_mail
    name: gmail-delete-mail
    description: "Move a message to Trash, or delete it permanently (bypasses Trash) when permanent is true."
    inputs_schema:
      properties:
        message_id: { type: string, description: "Gmail message ID" }
        permanent: { type: boolean, description: "Permanently delete instead of moving to Trash (irreversible)" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: [message_id]
    outputs_schema: { properties: {} }
  - id: move_mail
    name: gmail-move-mail
    description: "Add and/or remove labels on a message (move between folders — e.g. remove INBOX, add SPAM or a quarantine label)."
    inputs_schema:
      properties:
        message_id: { type: string, description: "Gmail message ID" }
        add_labels: { type: string, description: "Label IDs to add, comma-separated" }
        remove_labels: { type: string, description: "Label IDs to remove, comma-separated" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: [message_id]
    outputs_schema: { properties: {} }
  - id: get_attachments
    name: gmail-get-attachments
    description: "Download every attachment of a message; returns name, MIME type, size and base64 content for each."
    risk: read
    inputs_schema:
      properties:
        message_id: { type: string, description: "Gmail message ID" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: [message_id]
    outputs_schema: { properties: {} }
  - id: list_labels
    name: gmail-list-labels
    description: "List the mailbox's labels (system and user labels with their IDs)."
    risk: read
    inputs_schema:
      properties:
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: []
    outputs_schema: { properties: {} }

  # ── Auto-reply (vacation responder) ───────────────────────────────────────
  - id: autoreply_get
    name: gmail-autoreply-get
    description: "Get the mailbox's vacation auto-reply settings."
    risk: read
    inputs_schema:
      properties:
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: []
    outputs_schema: { properties: {} }
  - id: autoreply_set
    name: gmail-autoreply-set
    description: "Enable, update or disable the mailbox's vacation auto-reply."
    inputs_schema:
      properties:
        enable: { type: boolean, description: "Enable the auto-reply (default true; false disables it)" }
        response_subject: { type: string, description: "Auto-reply subject" }
        response_body: { type: string, description: "Auto-reply plain-text body" }
        response_body_html: { type: string, description: "Auto-reply HTML body" }
        contacts_only: { type: boolean, description: "Only reply to senders in the user's contacts" }
        domain_only: { type: boolean, description: "Only reply to senders in the same domain" }
        start_time: { type: string, description: "Start time — epoch ms or ISO8601" }
        end_time: { type: string, description: "End time — epoch ms or ISO8601" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: []
    outputs_schema: { properties: {} }

  # ── Filters ───────────────────────────────────────────────────────────────
  - id: add_filter
    name: gmail-add-filter
    description: "Create a mail filter on the mailbox (criteria: from/to/subject/query/has_attachment; actions: add/remove labels, forward)."
    inputs_schema:
      properties:
        from: { type: string, description: "Criteria — sender address" }
        to: { type: string, description: "Criteria — recipient address" }
        subject: { type: string, description: "Criteria — subject contains" }
        query: { type: string, description: "Criteria — Gmail search query" }
        has_attachment: { type: boolean, description: "Criteria — only messages with attachments" }
        add_labels: { type: string, description: "Action — label IDs to add, comma-separated" }
        remove_labels: { type: string, description: "Action — label IDs to remove, comma-separated (e.g. INBOX to archive)" }
        forward: { type: string, description: "Action — forward to this address (must be a verified forwarding address)" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: []
    outputs_schema: { properties: {} }
  - id: list_filters
    name: gmail-list-filters
    description: "List the mailbox's mail filters."
    risk: read
    inputs_schema:
      properties:
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: []
    outputs_schema: { properties: {} }
  - id: remove_filter
    name: gmail-remove-filter
    description: "Delete a mail filter by ID."
    inputs_schema:
      properties:
        filter_id: { type: string, description: "Filter ID (from gmail-list-filters)" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: [filter_id]
    outputs_schema: { properties: {} }

  # ── Forwarding ────────────────────────────────────────────────────────────
  - id: forwarding_add
    name: gmail-forwarding-add
    description: "Register a forwarding address on the mailbox (Google may require verification before it becomes usable)."
    inputs_schema:
      properties:
        forwarding_email: { type: string, description: "Forwarding address to register" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: [forwarding_email]
    outputs_schema: { properties: {} }
  - id: forwarding_list
    name: gmail-forwarding-list
    description: "List the mailbox's registered forwarding addresses and their verification status."
    risk: read
    inputs_schema:
      properties:
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: []
    outputs_schema: { properties: {} }
  - id: forwarding_remove
    name: gmail-forwarding-remove
    description: "Delete a forwarding address from the mailbox."
    inputs_schema:
      properties:
        forwarding_email: { type: string, description: "Forwarding address to remove" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: [forwarding_email]
    outputs_schema: { properties: {} }
  - id: forwarding_update
    name: gmail-forwarding-update
    description: "Enable auto-forwarding of the mailbox to a verified forwarding address, with a disposition for the forwarded copy."
    inputs_schema:
      properties:
        forwarding_email: { type: string, description: "Verified forwarding address to auto-forward to" }
        disposition: { type: string, description: "What happens to the forwarded message in the mailbox: leaveInInbox, archive, trash or markRead" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: [forwarding_email]
    outputs_schema: { properties: {} }

  # ── Delegates ─────────────────────────────────────────────────────────────
  - id: delegate_add
    name: gmail-delegate-add
    description: "Grant a delegate access to the mailbox (delegate can read, send and delete on the owner's behalf)."
    inputs_schema:
      properties:
        delegate_email: { type: string, description: "Delegate's email address (same Workspace domain)" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: [delegate_email]
    outputs_schema: { properties: {} }
  - id: delegate_remove
    name: gmail-delegate-remove
    description: "Revoke a delegate's access to the mailbox."
    inputs_schema:
      properties:
        delegate_email: { type: string, description: "Delegate's email address to revoke" }
        user_id: { type: string, description: "Mailbox to impersonate (default from instance config)" }
      required: [delegate_email]
    outputs_schema: { properties: {} }

  # ── Workspace user administration (Directory API — runs as the admin) ────
  - id: list_users
    name: gmail-list-users
    description: "List Workspace users in a domain (Directory API, impersonates the configured admin account)."
    risk: read
    inputs_schema:
      properties:
        domain: { type: string, description: "Domain to list (defaults to the admin account's domain)" }
        query: { type: string, description: "Directory search query (e.g. email:john* or name:'Jane Doe')" }
        max_results: { type: number, description: "Maximum users per page (default 100, max 500)" }
        page_token: { type: string, description: "Page token from a previous call" }
        show_deleted: { type: boolean, description: "List recently deleted users instead of active ones" }
      required: []
    outputs_schema: { properties: {} }
  - id: get_user
    name: gmail-get-user
    description: "Get a Workspace user by primary email, alias or unique ID (Directory API)."
    risk: read
    inputs_schema:
      properties:
        user_key: { type: string, description: "User's primary email, alias email or unique ID" }
      required: [user_key]
    outputs_schema: { properties: {} }
  - id: create_user
    name: gmail-create-user
    description: "Create a Workspace user (Directory API)."
    inputs_schema:
      properties:
        email: { type: string, description: "New user's primary email address" }
        first_name: { type: string, description: "Given name" }
        last_name: { type: string, description: "Family name" }
        password: { type: string, description: "Initial password" }
      required: [email, first_name, last_name, password]
    outputs_schema: { properties: {} }
  - id: delete_user
    name: gmail-delete-user
    description: "Delete a Workspace user (Directory API)."
    inputs_schema:
      properties:
        user_key: { type: string, description: "User's primary email, alias email or unique ID" }
      required: [user_key]
    outputs_schema: { properties: {} }
  - id: set_password
    name: gmail-set-password
    description: "Reset a Workspace user's password (Directory API) — a common containment step for a compromised account."
    inputs_schema:
      properties:
        user_key: { type: string, description: "User's primary email, alias email or unique ID" }
        password: { type: string, description: "New password" }
      required: [user_key, password]
    outputs_schema: { properties: {} }
  - id: hide_user
    name: gmail-hide-user
    description: "Set a Workspace user's global directory visibility (Directory API)."
    inputs_schema:
      properties:
        user_key: { type: string, description: "User's primary email, alias email or unique ID" }
        visible: { type: boolean, description: "true to show the user in the global directory, false to hide" }
      required: [user_key]
    outputs_schema: { properties: {} }

  - id: test_connection
    name: gmail-test-connection
    description: "Verify the service account credentials and delegation by fetching the configured mailbox's profile (used by the Test button)."
    risk: read
    inputs_schema:
      properties: {}
      required: []
    outputs_schema: { properties: {} }

ingestion:
  command: get_incidents
  mapper: get_incidents
  default_incident_type: "Gmail Message"
