Skip to content

Conversation

@MilanKomsa
Copy link
Contributor

No description provided.

}

// Validate against GS1 schema
const isValid = this.validateSchema(document);

Check failure

Code scanning / CodeQL

Resources exhaustion from deep object traversal High

Denial of service caused by processing
user input
with
allErrors: true
.

Copilot Autofix

AI 2 days ago

In general, to fix this issue you should avoid enabling allErrors: true when validating untrusted input in production. That prevents Ajv from traversing the entire object and accumulating every possible error, limiting both CPU and memory usage by stopping at the first validation failure (or at least drastically reducing the error surface). If detailed error reporting is needed in non-production environments, you can still enable allErrors conditionally based on an environment flag.

The best fix here, without changing the observable behavior too much, is to make allErrors conditional on an environment variable (for example REST_DEBUG as in the background example, or a more plugin-specific variable). In production, where that variable is not set to a truthy value, Ajv will default to reporting only the first error, mitigating the DoS risk. In development, you still get the rich error list for debugging. Concretely, in packages/plugin-epcis/src/services/EPCISValidationService.ts, update the Ajv configuration in the constructor so that allErrors is not hard-coded to true but instead reads from process.env. No other code needs to change because Ajv’s public interface (compile, validate, and errors) remains the same; only the number of errors populated in this.validateSchema.errors will change.

To implement this, you only need to change the object literal passed to new Ajv in EpcisValidationService’s constructor. No new helper methods or imports are required, as process.env is available in Node.js by default. A typical pattern is:

this.ajv = new Ajv({
  allErrors: process.env.REST_DEBUG === "true",
  strict: false,
  validateFormats: true,
});

This keeps current behavior when REST_DEBUG=true (or whatever flag you choose) and disables multi-error collection otherwise.

Suggested changeset 1
packages/plugin-epcis/src/services/EPCISValidationService.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/plugin-epcis/src/services/EPCISValidationService.ts b/packages/plugin-epcis/src/services/EPCISValidationService.ts
--- a/packages/plugin-epcis/src/services/EPCISValidationService.ts
+++ b/packages/plugin-epcis/src/services/EPCISValidationService.ts
@@ -9,7 +9,9 @@
 
   constructor() {
     this.ajv = new Ajv({
-      allErrors: true,
+      // Avoid collecting all errors in production to prevent potential DoS from deep/large payloads
+      // Enable verbose error collection only when explicitly requested via environment variable
+      allErrors: process.env.REST_DEBUG === "true",
       strict: false,
       validateFormats: true,
     });
EOF
@@ -9,7 +9,9 @@

constructor() {
this.ajv = new Ajv({
allErrors: true,
// Avoid collecting all errors in production to prevent potential DoS from deep/large payloads
// Enable verbose error collection only when explicitly requested via environment variable
allErrors: process.env.REST_DEBUG === "true",
strict: false,
validateFormats: true,
});
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Contributor

@Lexpeartha Lexpeartha left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also document new .env variable and add it to env.d.ts

@MilanKomsa
Copy link
Contributor Author

Please also document new .env variable and add it to env.d.ts
What new .env variable?

@MilanKomsa MilanKomsa requested a review from Lexpeartha January 16, 2026 14:43
@MilanKomsa MilanKomsa dismissed Lexpeartha’s stale review January 16, 2026 14:44

there is no .env variable used in code

Copy link

@Balki-OriginTrail Balki-OriginTrail left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code so beautiful, it made my cry

} as any);
}
// Query publisher for asset status
const response = await fetch(`${publisherUrl}/api/dkg/assets/status/${encodeURIComponent(captureID)}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for the future, we should avoid calling our own server from that server. Instead we should expose the publisher plugin in context

Copy link
Contributor

@zsculac zsculac Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it would be wise to add some timeout here, so the request cannot hang forever.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do next week

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't have to rewrite the plugin to fit, it's a different task. This was just to keep in mind once it is rewritten.

Adding the timeout would be good though and it's quick

console.error("[EPCIS Status] Error:", error);
res.status(500).json({
error: "Failed to get capture status",
message: error.message,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should return generic messages to the user and log the specific message on the server to avoid leaking sensitive data

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please elaborate

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This returns a full backend error to the frontend which is generally not a good practice because it exposes the backend

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while i agree, entire codebase is doing the same pattern, commented out the full error.message for now, but it's a repo wide problem which we should address in the future, will create a backlog task

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we will slowly rewrite this in the codebase, we have to start somewhere :)

the message should still exist though. Sth generic like I mentioned in the first comment. Perhaps: "Internal server error".

Also please do this for all tools in this plugin, multiple tools still return error messages

}

// Send to publisher
const result = await sendToPublisher(document, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be some retries here and a fallback publishing using dkg.asset.create if the publisher plugin fails

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs further discussion, will add it to backlog for rework

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it need further discussion? It's a general good practice to do this

Comment on lines 53 to 55
if (params.ual) {
return this.getEventByUal(params.ual);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if someone passes ual and some exclusive fields they are interested in they will still get everything back

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also we should do ual validation here and return a proper error. Currently if the ual is not valid it just returns prefixes.

You can check how this is done in dkg.js

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commented out for now, isn't important for Friday demo, will be added to rework backlog

${filterClauses.join('\n ')}
}
ORDER BY DESC(?eventTime)
LIMIT 100`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the limit hardcoded? This should not be up to us to decide

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be added to backlog for rework, not important for Friday

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be thinking about the general repo and others who might be using the plugin too. They won't know about this limit and could spend much time debugging why they're not getting all events back

@MilanKomsa MilanKomsa merged commit 2614b51 into main Jan 22, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants