From e9cf2daf109b9e55fc5e95a2d6a1d7e0f3fd65a5 Mon Sep 17 00:00:00 2001 From: metatroncubeswdev Date: Wed, 14 Oct 2026 10:42:26 -0400 Subject: [PATCH] Add encrypted backup script and security/incident response policy Co-Authored-By: Claude Opus 5.5 --- SECURITY.md | 29 +++++++++++++++++++++++++++++ scripts/backup.sh | 19 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 SECURITY.md create mode 100644 scripts/backup.sh diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..f5fe1c3 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,29 @@ +# Offerlyst security and incident response policy + +Owner: Metatroncube Software Solutions · Contact: info@metatroncubesolutions.com · Last reviewed: 2026-10-14 + +## Data we hold +Shop settings, offers, and for analytics: order ID (keyed hash + AES-256-GCM), order number (AES-256-GCM), +order total and discount. No customer names, emails, phone numbers or addresses. + +## Access control +- Production access (VPS, database, Shopify Partner/Dev Dashboard) is limited to named staff who need it. +- SSH uses key-based login; Shopify and Git accounts use strong unique passwords and two-factor authentication. +- Secrets (`.env`: Shopify secret, database password, `DATA_ENCRYPTION_KEY`) live only on the server and in the + team password manager, never in Git or chat. Rotate them when someone with access leaves. +- Access is reviewed every quarter. + +## Data protection +- In transit: HTTPS only (TLS at Nginx). At rest: order identifiers encrypted in the database; nightly backups + encrypted with AES-256 (`scripts/backup.sh`), kept 14 days. +- Test and production are separate: development uses local databases and Shopify development stores. +- Shop data is deleted 48 hours after uninstall (`shop/redact`). + +## Incident response +1. **Detect and contain** (immediately): revoke exposed credentials, rotate secrets, block the access path, + take the affected service offline if needed. +2. **Assess** (within 24 h): what data, which shops, how many records, time window. Preserve logs. +3. **Notify** (within 72 h of becoming aware): affected merchants by email, and Shopify via the Partner + support channel; regulators where the law requires it. +4. **Recover**: fix the root cause, restore from encrypted backups if needed, verify. +5. **Review** (within 2 weeks): written post-mortem, follow-up actions, update this policy. diff --git a/scripts/backup.sh b/scripts/backup.sh new file mode 100644 index 0000000..5c73c8a --- /dev/null +++ b/scripts/backup.sh @@ -0,0 +1,19 @@ +#!/bin/sh +# Encrypted nightly Postgres backup for Offerlyst. Run from the app folder on the VPS. +# Setup once: openssl rand -hex 32 > /root/.offerlyst-backup-key && chmod 600 /root/.offerlyst-backup-key +# Cron (3 am): 0 3 * * * /opt/bundle-offers-app/scripts/backup.sh >> /var/log/offerlyst-backup.log 2>&1 +# Restore: openssl enc -d -aes-256-cbc -pbkdf2 -pass file:/root/.offerlyst-backup-key -in FILE | gunzip | \ +# docker compose exec -T db psql -U bundle_offers bundle_offers +set -eu +cd "$(dirname "$0")/.." +KEY=/root/.offerlyst-backup-key +DIR=/root/backups +mkdir -p "$DIR" +chmod 700 "$DIR" +OUT="$DIR/offerlyst-$(date +%F).sql.gz.enc" +docker compose exec -T db pg_dump -U bundle_offers bundle_offers | gzip | \ + openssl enc -aes-256-cbc -pbkdf2 -salt -pass "file:$KEY" -out "$OUT" +chmod 600 "$OUT" +# keep 14 days +find "$DIR" -name 'offerlyst-*.sql.gz.enc' -mtime +14 -delete +echo "$(date -Is) backup ok: $OUT"