-
Notifications
You must be signed in to change notification settings - Fork 24
fix: protect Stellar wallet lookups #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| export default { | ||
| testEnvironment: "node", | ||
| transform: {}, | ||
| setupFiles: ["<rootDir>/test/jest.setup.js"], | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ import { | |
| submitTransaction, | ||
| verifyTransaction, | ||
| verifyPaymentOperations, | ||
| validateSignedPaymentXdr, | ||
| NETWORK, | ||
| getExplorerUrl, | ||
| PLATFORM_WALLET_PUBLIC_KEY, | ||
|
|
@@ -188,7 +189,8 @@ export const initializePayment = async (req, res) => { | |
| network: NETWORK, | ||
| status: "pending", | ||
| settlement: settlementMode, | ||
| stellarTxHash: paymentTx.hash, // Temporary hash, will be replaced with actual | ||
| expectedHash: paymentTx.hash, | ||
| memo, | ||
| ...(feeSplit && { | ||
| platformFee: { | ||
| feePercent: feeSplit.feePercent, | ||
|
|
@@ -275,7 +277,51 @@ export const submitPayment = async (req, res) => { | |
| }); | ||
| } | ||
|
|
||
| // Update status to submitted | ||
| // Build expected payments to validate XDR BEFORE submit | ||
| let expectedPayments = transaction.platformFee?.platformAmount | ||
| ? [ | ||
| { | ||
| destination: transaction.creatorWallet, | ||
| amount: transaction.platformFee.creatorAmount, | ||
| }, | ||
| { | ||
| destination: transaction.platformFee.platformWallet, | ||
| amount: transaction.platformFee.platformAmount, | ||
| }, | ||
| ] | ||
| : [ | ||
| { | ||
| destination: transaction.creatorWallet, | ||
| amount: transaction.amount, | ||
| }, | ||
| ]; | ||
|
Comment on lines
+280
to
+297
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Keep the advertised SEP-7 flow aligned with fee-split validation. For a fee split, this requires creator and platform transfers, but initialization still returns a SEP-7 URI for one full-price transfer. Wallets following that URI will be rejected for a missing platform payment, and the pending transaction is marked failed. Generate an equivalent multi-operation request, or omit the SEP-7 link when a split applies; add a regression test. As per path instructions, “New or changed endpoints need Jest coverage.” 🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| // Validate signed XDR contents (memo, payments, optional source) | ||
| try { | ||
| validateSignedPaymentXdr( | ||
| signedXdr, | ||
| expectedPayments, | ||
| transaction.memo, | ||
| transaction.buyerWallet, | ||
| true | ||
| ); | ||
| } catch (validationError) { | ||
| transaction.status = "failed"; | ||
| transaction.failureReason = `validation_failed: ${validationError.message}`; | ||
| await transaction.save({ session }); | ||
| await session.commitTransaction(); | ||
| paymentsFailed.inc({ type: "purchase", reason: "validation_failed" }); | ||
|
|
||
| logger.error(`Transaction ${transactionId} validation failed:`, validationError.message); | ||
|
|
||
| return res.status(400).json({ | ||
| success: false, | ||
| message: "Signed transaction does not match expected payment details", | ||
| error: validationError.message, | ||
| }); | ||
|
Comment on lines
+317
to
+321
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win Use one validation-error envelope across Stellar submissions. Both new branches put
As per path instructions, “New or changed endpoints need Jest coverage” and require consistent response shapes 📍 Affects 2 files
🤖 Prompt for AI AgentsSource: Path instructions |
||
| } | ||
|
|
||
| // Update status to submitted after validation | ||
| transaction.status = "submitted"; | ||
| transaction.submittedAt = new Date(); | ||
| await transaction.save({ session }); | ||
|
|
@@ -304,7 +350,7 @@ export const submitPayment = async (req, res) => { | |
|
|
||
| // Verify on-chain that the creator (and platform, when a fee was applied) | ||
| // actually received the expected USDC amounts | ||
| const expectedPayments = transaction.platformFee?.platformAmount | ||
| expectedPayments = transaction.platformFee?.platformAmount | ||
| ? [ | ||
| { | ||
| destination: transaction.creatorWallet, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,12 +4,6 @@ import mongoose from "mongoose"; | |
| const transactionSchema = new mongoose.Schema( | ||
| { | ||
| // Transaction identification | ||
| stellarTxHash: { | ||
| type: String, | ||
| required: true, | ||
| unique: true, | ||
| index: true, | ||
| }, | ||
| stellarLedger: { | ||
| type: Number, | ||
| }, | ||
|
|
@@ -46,6 +40,14 @@ const transactionSchema = new mongoose.Schema( | |
| required: true, | ||
| }, | ||
|
|
||
| // Transaction hash from the Stellar network, only set after confirmation | ||
| stellarTxHash: { | ||
| type: String, | ||
| unique: true, | ||
| sparse: true, | ||
| index: true, | ||
| }, | ||
|
Comment on lines
+43
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
: "${MONGO_URI:?Set MONGO_URI to inspect the deployed database}"
mongosh "$MONGO_URI" --quiet --eval '
db.getCollectionNames().forEach((name) => {
const indexes = db.getCollection(name).getIndexes()
.filter((index) => index.key && index.key.stellarTxHash === 1);
if (indexes.length) printjson({ collection: name, indexes });
});
'Repository: Deen-Bridge/dnb-backend Length of output: 233 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Inspect the Transaction model and any index-related migration/setup code.
git ls-files 'src/models/Transaction.js' 'src/**/*migration*' 'src/**/*index*' '.env.example' | sed -n '1,200p'
printf '\n--- Transaction.js outline ---\n'
ast-grep outline src/models/Transaction.js --view expanded
printf '\n--- relevant index/migration references ---\n'
rg -n "syncIndexes|createIndexes|ensureIndex|stellarTxHash|unique: true|sparse: true|autoIndex|index\(" src .env.exampleRepository: Deen-Bridge/dnb-backend Length of output: 2880 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n--- Transaction.js ---\n'
cat -n src/models/Transaction.js | sed -n '1,220p'
printf '\n--- startup / index syncing references ---\n'
rg -n "syncIndexes|ensureIndexes|ensureIndex|autoIndex|init\(\)|mongoose\.connect|set\('autoIndex'|dropIndexes|collection\.createIndex" srcRepository: Deen-Bridge/dnb-backend Length of output: 5537 🏁 Script executed: #!/bin/bash
set -euo pipefail
sed -n '1,180p' src/config/db.js
printf '\n---\n'
rg -n "syncIndexes|ensureIndexes|dropIndex|dropIndexes|createIndex|autoIndex" srcRepository: Deen-Bridge/dnb-backend Length of output: 3686 Rebuild the 🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| // Item being purchased (not applicable to donations) | ||
| itemType: { | ||
| type: String, | ||
|
|
@@ -90,6 +92,13 @@ const transactionSchema = new mongoose.Schema( | |
| enum: ["testnet", "mainnet"], | ||
| required: true, | ||
| }, | ||
| expectedHash: { | ||
| type: String, | ||
| index: true, | ||
| }, | ||
| memo: { | ||
| type: String, | ||
| }, | ||
|
|
||
| // Platform fee split (only set when a fee was applied at build time) | ||
| platformFee: { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import { MongoMemoryServer } from 'mongodb-memory-server'; | ||
|
|
||
| process.env.JWT_SECRET = process.env.JWT_SECRET || 'test-secret'; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win Move test configuration out of JavaScript. Both sites silently inject configuration rather than requiring the documented test environment, allowing CI to run with an undeclared JWT secret or database target.
As per path instructions, “all configuration belongs in environment variables documented in .env.example.” 📍 Affects 2 files
🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| // Start an in-memory MongoDB for tests | ||
| const mongod = await MongoMemoryServer.create(); | ||
| process.env.MONGO_URI = mongod.getUri(); | ||
| global.__MONGOD__ = mongod; | ||
|
Comment on lines
+6
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
rg -n -C3 'MongoMemoryServer|__MONGOD__|globalTeardown|setupFilesAfterEnv|\.stop\(' \
jest.config.js test srcRepository: Deen-Bridge/dnb-backend Length of output: 614 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n## files\n'
git ls-files | rg '(^|/)(jest\.config\..*|package\.json|test/|src/)' | sed -n '1,200p'
printf '\n## teardown-related search\n'
rg -n -C3 'MongoMemoryServer|__MONGOD__|globalTeardown|setupFilesAfterEnv|setupFiles|teardown|\.stop\(' .
printf '\n## jest setup file\n'
cat -n test/jest.setup.js
printf '\n## jest config candidates\n'
for f in $(git ls-files | rg '(^|/)jest\.config\..*$|(^|/)package\.json$'); do
echo "--- $f"
cat -n "$f" | sed -n '1,220p'
doneRepository: Deen-Bridge/dnb-backend Length of output: 5716 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n## mongoose connection handling in tests\n'
rg -n -C2 'mongoose\.connect|mongoose\.disconnect|disconnect\(|close\(\)|__MONGOD__|MongoMemoryServer' test src
printf '\n## test bootstrap files\n'
for f in test/*.js; do
echo "--- $f"
cat -n "$f" | sed -n '1,240p'
doneRepository: Deen-Bridge/dnb-backend Length of output: 44250 Move the MongoMemoryServer lifecycle to teardown. 🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Do not embed test configuration defaults in source.
The fallback JWT secret and Mongo connection string mask missing test/CI configuration and violate the environment-only configuration rule. This shares the same root cause as
test/jest.setup.jsLine 3 and is consolidated below.As per path instructions, “all configuration belongs in environment variables documented in .env.example.”
🤖 Prompt for AI Agents
Source: Path instructions