← back to Sample Followup Sweep

lib/george-transport.js

57 lines

'use strict';

const http = require('node:http');
const { assertPreSendCompliance, REQUIRED_FROM } = require('./pre-send-gate');

const SENDABLE_PATHS = new Set(['/api/send', '/api/drafts']);

function assertSendablePayload(payload) {
  assertPreSendCompliance({
    from: payload?.account === 'info' ? REQUIRED_FROM : '',
    subject: payload?.subject,
    body: payload?.body,
  });
  return payload;
}

function createGmailDraftArtifact(payload) {
  return assertSendablePayload(payload);
}

// TK-11409: strip George's internal provenance banner from every vendor-facing message.
// George's withSourceFooter() prepends a visible "From job: node · <timestamp>" block INSIDE the
// message body unless the caller passes no_source_tag. Nothing in this pipeline was passing it, so
// that banner shipped to vendors on every chase — verified present in the drafts AND in chases
// already SENT on 09/01 and 09/04. Every path through this transport is a vendor-facing letter
// (/api/send and /api/drafts are the only sendable paths), so the flag belongs HERE rather than at
// each of the four call sites: a per-caller flag is a step someone must remember, and this ticket
// is a catalogue of what happens when a safety step depends on being remembered.
function stripSourceBanner(payload) {
  if (payload && typeof payload === 'object' && payload.no_source_tag === undefined) {
    payload.no_source_tag = true;
  }
  return payload;
}

async function georgeRequest({ path, payload, headers = {}, requestImpl = http.request }) {
  if (!SENDABLE_PATHS.has(path)) throw new Error(`Unsupported sendable George path: ${path}`);
  assertSendablePayload(payload); // Must run before serialization, credential reads, or network setup.
  stripSourceBanner(payload);
  const body = JSON.stringify(payload);
  return new Promise((resolve) => {
    const req = requestImpl({
      host: '127.0.0.1', port: 9850, path, method: 'POST',
      headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), ...headers },
    }, (response) => {
      let responseBody = '';
      response.on('data', (chunk) => { responseBody += chunk; });
      response.on('end', () => resolve({ statusCode: response.statusCode || 0, body: responseBody }));
    });
    req.on('error', (error) => resolve({ statusCode: 0, body: error.message }));
    req.write(body);
    req.end();
  });
}

module.exports = { assertSendablePayload, createGmailDraftArtifact, georgeRequest, stripSourceBanner, SENDABLE_PATHS };