From 203d5f024e27bb1f0780a896469f71b020523fce Mon Sep 17 00:00:00 2001 From: OneClickPostFactory Date: Tue, 8 Sep 2026 06:18:53 -0700 Subject: [PATCH 1/2] Inspect Cloudflare staging access without resource changes --- .../workflows/inspect-cloudflare-staging.yml | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/workflows/inspect-cloudflare-staging.yml diff --git a/.github/workflows/inspect-cloudflare-staging.yml b/.github/workflows/inspect-cloudflare-staging.yml new file mode 100644 index 0000000..a7bbbe4 --- /dev/null +++ b/.github/workflows/inspect-cloudflare-staging.yml @@ -0,0 +1,71 @@ +name: Inspect Cloudflare staging access + +on: + workflow_dispatch: + push: + branches: [release/inspect-staging-access-20260908] + paths: ['.github/workflows/inspect-cloudflare-staging.yml'] + +permissions: + contents: read + +jobs: + inspect: + if: github.repository == 'OneClickPostFactory/social-agents' + runs-on: ubuntu-latest + timeout-minutes: 3 + steps: + - name: Read existing account resources without exposing credentials + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: | + node --input-type=module <<'NODE' + const token = process.env.CLOUDFLARE_API_TOKEN; + const account = process.env.CLOUDFLARE_ACCOUNT_ID; + if (!token || !account) { + console.log(JSON.stringify({ access: 'blocked', token_configured: Boolean(token), account_configured: Boolean(account) })); + process.exit(1); + } + if (!/^[a-f0-9]{32}$/i.test(account)) { + console.log('Configured account identifier is invalid.'); + process.exit(1); + } + let failed = false; + for (const resource of ['workers/scripts', 'containers/applications']) { + try { + const response = await fetch('https://api.cloudflare.com/client/v4/accounts/' + account + '/' + resource, { + method: 'GET', + redirect: 'error', + headers: { Authorization: 'Bearer ' + token }, + signal: AbortSignal.timeout(20000) + }); + const body = await response.json(); + if (!response.ok || body.success === false) { + console.log(JSON.stringify({ resource, status: response.status, error_codes: (body.errors || []).map(e => e.code).filter(c => typeof c === 'number') })); + failed = true; + continue; + } + const rows = Array.isArray(body.result) ? body.result : Array.isArray(body) ? body : null; + if (!rows) { + console.log(JSON.stringify({ resource, status: response.status, result: 'unrecognised_shape' })); + failed = true; + continue; + } + console.log(JSON.stringify({ + resource, + status: response.status, + returned_count: rows.length, + pagination: body.result_info ? { page: body.result_info.page, total_pages: body.result_info.total_pages, total_count: body.result_info.total_count } : null, + matching_resources: rows.filter(r => /oneclick|social.agents|staging/i.test(resource === 'workers/scripts' ? r.id : r.name || '')).map(r => ({ + name: resource === 'workers/scripts' ? r.id : r.name, + ...(resource === 'containers/applications' ? { instances: r.instances, max_instances: r.max_instances } : {}) + })) + })); + } catch { + console.log(JSON.stringify({ resource, access: 'request_failed' })); + failed = true; + } + } + process.exitCode = failed ? 1 : 0; + NODE From 45f2013a0e16701435d76f838c2fc8536afcdc3c Mon Sep 17 00:00:00 2001 From: OneClickPostFactory Date: Tue, 8 Sep 2026 06:43:52 -0700 Subject: [PATCH 2/2] Inspect staging database isolation and cron configuration --- .../workflows/inspect-cloudflare-staging.yml | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/.github/workflows/inspect-cloudflare-staging.yml b/.github/workflows/inspect-cloudflare-staging.yml index a7bbbe4..6d24648 100644 --- a/.github/workflows/inspect-cloudflare-staging.yml +++ b/.github/workflows/inspect-cloudflare-staging.yml @@ -69,3 +69,57 @@ jobs: } process.exitCode = failed ? 1 : 0; NODE + + - name: Verify deployed staging isolation without revealing binding values + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: | + node --input-type=module <<'NODE' + const account = process.env.CLOUDFLARE_ACCOUNT_ID; + const token = process.env.CLOUDFLARE_API_TOKEN; + if (!token || !/^[a-f0-9]{32}$/i.test(account || '')) process.exit(1); + let failed = false; + const databaseOrigins = new Map(); + for (const script of ['oneclickpostfactory-agent', 'oneclickpostfactory-agent-collector-staging']) { + for (const suffix of ['settings', 'schedules']) { + try { + const response = await fetch('https://api.cloudflare.com/client/v4/accounts/' + account + '/workers/scripts/' + script + '/' + suffix, { + method: 'GET', redirect: 'error', + headers: { Authorization: 'Bearer ' + token }, + signal: AbortSignal.timeout(20000) + }); + const body = await response.json(); + if (!response.ok || body.success === false) { + console.log(JSON.stringify({ script, resource: suffix, status: response.status, error_codes: (body.errors || []).map(e => e.code).filter(c => typeof c === 'number') })); + failed = true; + continue; + } + if (suffix === 'settings') { + if (!Array.isArray(body.result?.bindings)) throw new Error('shape'); + const bindings = body.result.bindings; + const db = bindings.find(b => b.name === 'SUPABASE_URL' && b.type === 'plain_text'); + if (db?.text) { + try { databaseOrigins.set(script, new URL(db.text).origin); } catch {} + } + console.log(JSON.stringify({ + script, resource: suffix, status: response.status, + compatibility_date: body.result.compatibility_date, + bindings: bindings.map(b => ({ name: b.name, type: b.type })), + database_target_readable: databaseOrigins.has(script) + })); + } else { + if (!Array.isArray(body.result?.schedules)) throw new Error('shape'); + console.log(JSON.stringify({ script, resource: suffix, status: response.status, cron_count: body.result.schedules.length })); + } + } catch { + console.log(JSON.stringify({ script, resource: suffix, result: 'inspection_failed' })); + failed = true; + } + } + } + const prod = databaseOrigins.get('oneclickpostfactory-agent'); + const staging = databaseOrigins.get('oneclickpostfactory-agent-collector-staging'); + console.log(JSON.stringify({ database_isolation: prod && staging ? (prod === staging ? 'shared_with_production' : 'different_target_requires_validation') : 'unverified_target_not_visible' })); + process.exitCode = failed ? 1 : 0; + NODE