Skip to content

B4. soreto-zoe

← Current State Index · ← Audit README


Summary

soreto-zoe is the background job scheduler and affiliate network integration engine. It runs Bull queues backed by Redis, executes processors (cron jobs) that call affiliate network APIs (Awin, Rakuten, CJ, Partnerize, and 10+ others), and reports results back to reverb-backend via HTTP. It carries the most immediate security risk in the ecosystem due to a leaked GitHub PAT in its package.json.


Key Facts

Property Value
Language TypeScript 5.4.5 (src/) + JavaScript (processors/) — mixed
Node version 16.13.0 — EOL September 2023
Framework Express 4.16.4 + Bull 3.13.0 (job queuing)
Database PostgreSQL via Knex 0.16.3 (schema: zoe)
Queue Bull + Redis
TypeScript pattern AMD modules + namespaces — legacy pattern
CI/CD None ⚠️
Deployment Unknown (likely Heroku — heroku-private-modules present)
Lockfile package-lock.json embedded in package.json (malformed)
Testing None ⚠️

⛔ CRITICAL: Leaked GitHub Personal Access Token

The package.json contains a hardcoded GitHub PAT in a dependency URL:

"soreto-arena": "https://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:x-oauth-basic@github.com/soreto/soreto-arena.git"

This PAT is exposed in: - Every npm install log (local, CI, Heroku build logs) - Every git history commit that included this line - Any log aggregation system connected to CI/CD

Immediate actions required: 1. Revoke this PAT immediately via GitHub → Settings → Developer Settings → Personal Access Tokens 2. Rotate any GitHub tokens associated with the account that created this PAT 3. Audit what repositories this PAT had access to 4. Replace the soreto-arena dependency (see below)

soreto-arena appears to be a fork of bull-arena — a Bull queue monitoring dashboard. Options to replace it: - Use upstream bull-arena from npm directly - Publish the fork as a private npm package to an internal registry - Switch to bull-board (maintained alternative)


TypeScript Architecture: AMD Namespaces (Legacy Pattern)

The src/ directory uses TypeScript AMD modules with namespaces — a pattern from 2014–2016 that is incompatible with modern tooling:

// src/app.ts
namespace Zoe {
  import Load = Zoe.Modules.Load;
  let loader = new Load();
  loader.load();
}

// src/services/baseService.ts
namespace Zoe.Services {
  export class BaseService {
    constructor() { this.startLogger(); }
  }
}

tsconfig.json uses:

{
  "module": "amd",
  "outFile": "dist/app.js"
}

outFile bundles all TypeScript into a single AMD bundle. This approach: - Prevents tree-shaking - Cannot use import/export ES module syntax - Cannot use modern bundlers (Vite, esbuild, Rollup) - Is explicitly deprecated by Microsoft's TypeScript team in favor of ES modules - Produces a single large output file that is hard to debug in production

A second tsconfig (tsconfig.service.json) compiles services to processors/shared/services.js for use by the JavaScript processor files:

{
  "module": "amd",
  "outFile": "processors/shared/services.js"
}

This two-build setup is fragile and unusual.


Hybrid TypeScript/JavaScript Architecture

The project is architecturally split:

src/           ← TypeScript (AMD namespaces)
├── app.ts
├── appSettings.ts
├── database/
├── models/
├── modules/
└── services/

processors/    ← Plain JavaScript (excluded from TS compilation)
├── awin/
├── cj/
├── rakuten/
├── soreto/
├── shared/
│   └── services.js   ← compiled from src/ via tsconfig.service.json
└── ...

The processors are plain .js files that require('reverb') (the local API client) and require('../../shared/services') (the compiled TypeScript service bundle). This means: - Processors have no type checking - Processor bugs are only caught at runtime - Adding a new processor means writing untyped JavaScript


The local_modules/reverb Client

This is the primary evidence of coupling between soreto-zoe and reverb-backend:

// local_modules/reverb/index.js
const start = (options) => {
  return axios.post(`${options.apiBaseUrl}/auth/login`, {}, {
    auth: { username: options.username, password: options.password }
  }).then((result) => {
    let cookies = result.headers['set-cookie'];
    _reverbToken = cookies.find(c => c.includes('reverbToken'));
  });
}

This client: 1. Authenticates to reverb-backend via HTTP Basic auth 2. Extracts and stores the reverbToken session cookie 3. Uses that cookie for all subsequent API calls

It then exposes domain-specific methods that map directly to reverb-backend API endpoints: - postReward.getCampaignVersionsEnabledV2() - postReward.getOrders() - notification.send() - zendesk.leads.create() - etc.

This is an unversioned, hand-crafted HTTP client with no type definitions. Changes to reverb-backend API endpoints silently break soreto-zoe processors at runtime.


Knex Version Gap

Repo Knex Version Release Year
reverb-backend 0.95.7 2021
soreto-zoe 0.16.3 2018

A 3-year version gap. The knex API changed significantly between 0.16 and 0.95. Both repos connect to the same PostgreSQL server but different schemas (reverb vs zoe). Upgrading soreto-zoe's Knex is a prerequisite for the Node version upgrade.


Processor Inventory

The processors/ directory contains integrations for 12+ affiliate networks and several internal processors:

processors/
├── affilinet/
├── awin/             (+ mapper, oneclick)
├── cj/               (+ mapper)
├── commissionfactory/
├── impact/
├── marketplace/
├── newlook/
├── partnerize/
├── pepperjam/
├── rakuten/          (+ emailTemplate, mapper)
├── shareasale/
├── soicos/
├── soreto/
│   ├── alerts/
│   ├── databaseMaintenance/   ← partition management, cleanup
│   ├── email/
│   ├── mandrill/
│   ├── notifications/
│   ├── postReward/            ← harvest.js, reward.js
│   ├── rescue/
│   └── webgains_rescue/
├── tradedoubler/
├── uniqodo/
├── webgains/
└── zendesk/

Observation: This is a well-structured plugin architecture for affiliate integrations. Each network has its own directory with a mapper. However, all processors are untyped JavaScript with zero test coverage.


config.js — Environment Management

Uses a multi-environment JavaScript config file rather than .env:

// config.js
const zoeSettings = { /* defaults */ };

dev  = () => { /* localhost values */ };
test = () => { return null }; // test returns null — no test config
prod = () => { /* reads from process.env */ };

module.exports = envs[process.env.NODE_ENV];

Note: test = () => { return null } — the test config returns null. Any component that tries to access Config.Database.connection in a test context will throw a runtime error, which explains why there are no tests.


Malformed package.json

The package.json contains lockfileVersion and requires fields that belong in package-lock.json:

{
  "name": "soreto-zoe",
  "requires": true,
  "lockfileVersion": 1,
  ...
}

These fields are harmless to npm but indicate the file may have been manually edited or generated incorrectly at some point.


Risks Summary

Risk Severity
GitHub PAT in package.json ⛔ CRITICAL
Node 16 EOL 🔴 HIGH
Knex 0.16.3 (2018) 🔴 HIGH
No CI/CD 🔴 HIGH
No tests 🔴 HIGH
AMD/namespace TypeScript pattern 🟡 MEDIUM
Processors entirely untyped 🟡 MEDIUM
puppeteer@9.0.0 (significantly outdated) 🟡 MEDIUM
bull@3.13.0 (current is BullMQ 5.x) 🟡 MEDIUM
Unversioned local_modules/reverb client 🟡 MEDIUM
Test config returns null 🟡 MEDIUM