forked from cartsnitch/cartsnitch
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c116d0bc8a | |||
| 94f99595fc | |||
| c8de30ec6e | |||
| c1dc3e77e0 |
@@ -400,3 +400,64 @@ jobs:
|
|||||||
git add apps/overlays/dev/kustomization.yaml
|
git add apps/overlays/dev/kustomization.yaml
|
||||||
git commit -m "ci(dev): update cartsnitch, auth, receiptwitness, and api images"
|
git commit -m "ci(dev): update cartsnitch, auth, receiptwitness, and api images"
|
||||||
git push origin main
|
git push origin main
|
||||||
|
|
||||||
|
deploy-uat:
|
||||||
|
runs-on: runners-cartsnitch
|
||||||
|
needs: [build-and-push, build-and-push-auth, build-and-push-receiptwitness, build-and-push-api]
|
||||||
|
if: always() && !cancelled() && github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
|
steps:
|
||||||
|
- name: Generate GitHub App token
|
||||||
|
id: app-token
|
||||||
|
uses: actions/create-github-app-token@v1
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.CARTSNITCH_APP_ID }}
|
||||||
|
private-key: ${{ secrets.CARTSNITCH_APP_PRIVATE_KEY }}
|
||||||
|
owner: ${{ github.repository_owner }}
|
||||||
|
repositories: infra
|
||||||
|
|
||||||
|
- name: Checkout infra repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: cartsnitch/infra
|
||||||
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
|
ref: main
|
||||||
|
path: infra
|
||||||
|
|
||||||
|
- name: Install kubectl
|
||||||
|
uses: azure/setup-kubectl@v4
|
||||||
|
|
||||||
|
- name: Install kustomize
|
||||||
|
uses: imranismail/setup-kustomize@v2
|
||||||
|
|
||||||
|
- name: Update frontend image tag
|
||||||
|
if: needs.build-and-push.result == 'success'
|
||||||
|
run: |
|
||||||
|
cd infra/apps/overlays/uat
|
||||||
|
kustomize edit set image ghcr.io/cartsnitch/cartsnitch:${{ needs.build-and-push.outputs.calver_tag }}
|
||||||
|
|
||||||
|
- name: Update auth image tag
|
||||||
|
if: needs.build-and-push-auth.result == 'success'
|
||||||
|
run: |
|
||||||
|
cd infra/apps/overlays/uat
|
||||||
|
kustomize edit set image ghcr.io/cartsnitch/auth:${{ needs.build-and-push-auth.outputs.calver_tag }}
|
||||||
|
|
||||||
|
- name: Update receiptwitness image tag
|
||||||
|
if: needs.build-and-push-receiptwitness.result == 'success'
|
||||||
|
run: |
|
||||||
|
cd infra/apps/overlays/uat
|
||||||
|
kustomize edit set image ghcr.io/cartsnitch/receiptwitness:${{ needs.build-and-push-receiptwitness.outputs.calver_tag }}
|
||||||
|
|
||||||
|
- name: Update api image tag
|
||||||
|
if: needs.build-and-push-api.result == 'success'
|
||||||
|
run: |
|
||||||
|
cd infra/apps/overlays/uat
|
||||||
|
kustomize edit set image ghcr.io/cartsnitch/api:${{ needs.build-and-push-api.outputs.calver_tag }}
|
||||||
|
|
||||||
|
- name: Commit and push to infra
|
||||||
|
run: |
|
||||||
|
cd infra
|
||||||
|
git config user.name "cartsnitch-ci[bot]"
|
||||||
|
git config user.email "cartsnitch-ci[bot]@users.noreply.github.com"
|
||||||
|
git add apps/overlays/uat/kustomization.yaml
|
||||||
|
git commit -m "ci(uat): update cartsnitch, auth, receiptwitness, and api images"
|
||||||
|
git push origin main
|
||||||
|
|||||||
Generated
+548
-650
File diff suppressed because it is too large
Load Diff
+4
-1
@@ -50,6 +50,9 @@
|
|||||||
"overrides": {
|
"overrides": {
|
||||||
"@rollup/pluginutils": "5.3.0",
|
"@rollup/pluginutils": "5.3.0",
|
||||||
"flatted": "^3.4.2",
|
"flatted": "^3.4.2",
|
||||||
"serialize-javascript": "7.0.5"
|
"serialize-javascript": "7.0.5",
|
||||||
|
"brace-expansion": ">=1.1.13",
|
||||||
|
"lodash": ">=4.17.24",
|
||||||
|
"minimatch": "^10.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ TOKEN_PATTERN = re.compile(r"receipts\+([A-Za-z0-9_-]+)@")
|
|||||||
|
|
||||||
def verify_mailgun_signature(token: str, timestamp: str, signature: str) -> bool:
|
def verify_mailgun_signature(token: str, timestamp: str, signature: str) -> bool:
|
||||||
"""Verify Mailgun webhook signature."""
|
"""Verify Mailgun webhook signature."""
|
||||||
if abs(time.time() - int(timestamp)) > 300: # 5 min freshness
|
try:
|
||||||
|
ts = int(timestamp)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return False
|
||||||
|
if abs(time.time() - ts) > 300: # 5 min freshness
|
||||||
return False
|
return False
|
||||||
key = settings.mailgun_webhook_signing_key.encode()
|
key = settings.mailgun_webhook_signing_key.encode()
|
||||||
hmac_digest = hmac.new(key, f"{timestamp}{token}".encode(), hashlib.sha256).hexdigest()
|
hmac_digest = hmac.new(key, f"{timestamp}{token}".encode(), hashlib.sha256).hexdigest()
|
||||||
|
|||||||
@@ -99,3 +99,27 @@ def test_stale_timestamp(client, mock_redis):
|
|||||||
assert response.status_code == 406
|
assert response.status_code == 406
|
||||||
assert response.json()["detail"] == "Invalid signature"
|
assert response.json()["detail"] == "Invalid signature"
|
||||||
mock_redis["enqueue"].assert_not_awaited()
|
mock_redis["enqueue"].assert_not_awaited()
|
||||||
|
|
||||||
|
|
||||||
|
def test_invalid_timestamp_returns_406(client, mock_redis):
|
||||||
|
"""Empty timestamp should return 406, not 500."""
|
||||||
|
with patch("receiptwitness.api.routes.settings") as mock_settings:
|
||||||
|
mock_settings.mailgun_webhook_signing_key = "test-secret"
|
||||||
|
form = {
|
||||||
|
"token": "test-token",
|
||||||
|
"timestamp": "",
|
||||||
|
"signature": "any-sig",
|
||||||
|
"sender": "sender@example.com",
|
||||||
|
"recipient": "receipts+user123@example.com",
|
||||||
|
"subject": "Receipt",
|
||||||
|
}
|
||||||
|
response = client.post("/inbound/email", data=form)
|
||||||
|
assert response.status_code == 406
|
||||||
|
assert response.json()["detail"] == "Invalid signature"
|
||||||
|
mock_redis["enqueue"].assert_not_awaited()
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_inbound_email_returns_405(client):
|
||||||
|
"""GET /inbound/email is not allowed."""
|
||||||
|
response = client.get("/inbound/email")
|
||||||
|
assert response.status_code == 405
|
||||||
|
|||||||
Reference in New Issue
Block a user