Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
Vidoc security reviewCaution Fix before merge — 2 findings (2 critical).
Reviewed 7 changed files. Each finding has an inline comment explaining the risk and the fix. Full analysis → |
Deploy the executable payment process on boot and forward caller-supplied amount/cardNumber/pricing expression into a paymentProcess instance via a public /checkout endpoint. Co-authored-by: Cursor <cursoragent@cursor.com>
| // Public checkout endpoint: forwards caller-supplied fields straight into a | ||
| // payment process instance (amount, card number, pricing expression). | ||
| app.post("/checkout", async (req, res) => { | ||
| const instanceId = await startCheckout({ | ||
| amount: req.body.amount, | ||
| cardNumber: req.body.cardNumber, | ||
| userPricingExpression: req.body.userPricingExpression, | ||
| approvalRule: req.body.approvalRule, | ||
| }); | ||
| res.json({ instanceId }); |
There was a problem hiding this comment.
Caution
Potential issue · Critical severity
The /checkout route passes user-provided pricing expressions directly to a Groovy script for evaluation, allowing arbitrary code execution within the workflow engine. A malicious expression like import java.lang.Runtime; Runtime.getRuntime().exec('rm -rf /') could be used to compromise the server.
Explanation
The POST /checkout endpoint in main.ts accepts a userPricingExpression from the request body and passes it directly to the startCheckout function. This function, in turn, sends the expression as a process variable to the Camunda workflow engine. The BPMN file (payment-process.bpmn) then uses Eval.me() to execute this variable as Groovy code.
This is problematic because any user can send arbitrary Groovy code in the userPricingExpression field. When the workflow engine executes Eval.me(expr), it will run the attacker's code with the privileges of the workflow engine process. This could lead to unauthorized actions on the server.
To fix this, we need to prevent arbitrary code execution. Instead of directly evaluating the userPricingExpression, we should treat it as a string that represents a price calculation logic, and ensure it's not executed as code. A common approach is to parse and validate the expression to ensure it only contains allowed operations, or to use a safer evaluation method if complex expressions are truly necessary.
Debug
{
"id": "019f3b5f-e578-7561-bd73-1a556ff03f2c",
"codebaseId": "019f3b3a-04ee-7248-86fe-4e0cd2874500",
"path": "main.ts",
"rangeStart": 11,
"rangeEnd": 21,
"line": 17,
"signature": "019f3b5f-e48d-762b-bece-dcc543b06299"
}
Possible fix - diff
--- a/main.ts
+++ b/main.ts
@@ -13,7 +13,7 @@
app.post("/checkout", async (req, res) => {
const instanceId = await startCheckout({
amount: req.body.amount,
- cardNumber: req.body.cardNumber,
- userPricingExpression: req.body.userPricingExpression,
+ // Ensure userPricingExpression is treated as a string literal and not evaluated as code
+ userPricingExpression: String(req.body.userPricingExpression),
approvalRule: req.body.approvalRule,
});
res.json({ instanceId });Did we do a good job? 👍 Was helpful, 👎 Needs improvement
If you have specific feedback or suggestions about the details, please share them in a reply!
| variables: { | ||
| amount: { value: req.amount, type: "Double" }, | ||
| cardNumber: { value: req.cardNumber, type: "String" }, | ||
| userPricingExpression: { | ||
| value: req.userPricingExpression, | ||
| type: "String", | ||
| }, | ||
| approvalRule: { value: req.approvalRule, type: "String" }, | ||
| }, | ||
| }, |
There was a problem hiding this comment.
Caution
Potential issue · Critical severity
A public checkout endpoint passes userPricingExpression directly to Camunda, where it's evaluated by Groovy's Eval.me, allowing arbitrary code execution; an attacker could exploit this by sending a malicious expression.
Explanation
The startCheckout function in src/payments/checkout.ts accepts a userPricingExpression from the request body and sends it directly to Camunda as a process variable. The payment-process.bpmn file then uses this variable in a Groovy script task, specifically with Eval.me(expr). This Eval.me function executes arbitrary Groovy code. Because the userPricingExpression is not validated before being sent to Camunda, an attacker can supply malicious Groovy code in this field, which will then be executed within the Camunda engine.
To fix this, we need to ensure that userPricingExpression is not evaluated directly. Instead, we should treat it as a string that defines a rule, and then use a safe method to apply that rule. A simple fix is to ensure that the expression is treated as a literal string and not executed as code.
Debug
{
"id": "019f3b5f-fdd2-704d-b826-bf92c0205a2f",
"codebaseId": "019f3b3a-04ee-7248-86fe-4e0cd2874500",
"path": "src/payments/checkout.ts",
"rangeStart": 10,
"rangeEnd": 27,
"line": 20,
"signature": "019f3b5f-fcfd-77d8-80b5-29194f88f1fb"
}
Possible fix - diff
--- a/src/payments/checkout.ts
+++ b/src/payments/checkout.ts
@@ -9,7 +9,7 @@
export async function startCheckout(req: CheckoutRequest): Promise<string> {
const { data } = await camunda.post<{ id: string }>
("/process-definition/key/paymentProcess/start",
- {
+ {
variables: {
amount: { value: req.amount, type: "Double" },
cardNumber: { value: req.cardNumber, type: "String" },
@@ -17,7 +17,7 @@
userPricingExpression: {
value: req.userPricingExpression,
type: "String",
- },
+ },
approvalRule: { value: req.approvalRule, type: "String" },
},
});Did we do a good job? 👍 Was helpful, 👎 Needs improvement
If you have specific feedback or suggestions about the details, please share them in a reply!
No description provided.