-
Notifications
You must be signed in to change notification settings - Fork 17
epcis plugin V1 #22
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?
epcis plugin V1 #22
Conversation
| } | ||
|
|
||
| // Validate against GS1 schema | ||
| const isValid = this.validateSchema(document); |
Check failure
Code scanning / CodeQL
Resources exhaustion from deep object traversal High
user input
allErrors: true
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 7 days ago
In general, the fix is to avoid using allErrors: true when validating user-controlled input in production. Instead, only enable allErrors conditionally (e.g., based on an environment variable or a debug flag) so production validates only until the first error and does not allocate unbounded error arrays.
The best fix here is to change the Ajv instantiation in EpcisValidationService to make allErrors conditional on an environment variable (for example EPCIS_DEBUG), mirroring the recommended pattern from the background section. This keeps current functionality for debugging (developers can still see all validation errors when they explicitly enable debug mode), but prevents the denial-of-service risk in normal operation. No other logic needs to change, because Ajv’s errors array is still populated when validation fails; it will just contain fewer entries in non-debug mode.
Concretely, in packages/plugin-epcis/src/services/EPCISValidationService.ts, update the constructor where new Ajv is called: replace allErrors: true with allErrors: process.env["EPCIS_DEBUG"] === "true". This is the only code change needed. No changes are required in packages/plugin-epcis/src/index.ts, and no new imports or helper methods are necessary.
-
Copy modified line R12
| @@ -9,7 +9,7 @@ | ||
|
|
||
| constructor() { | ||
| this.ajv = new Ajv({ | ||
| allErrors: true, | ||
| allErrors: process.env["EPCIS_DEBUG"] === "true", | ||
| strict: false, | ||
| validateFormats: true, | ||
| }); |
Lexpeartha
left a comment
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.
Please also document new .env variable and add it to env.d.ts
|
there is no .env variable used in code
Balki-OriginTrail
left a comment
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.
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)}`); |
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.
Note for the future, we should avoid calling our own server from that server. Instead we should expose the publisher plugin in context
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.
Also, it would be wise to add some timeout here, so the request cannot hang forever.
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.
will do next week
| console.error("[EPCIS Status] Error:", error); | ||
| res.status(500).json({ | ||
| error: "Failed to get capture status", | ||
| message: error.message, |
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.
We should return generic messages to the user and log the specific message on the server to avoid leaking sensitive data
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.
please elaborate
| const { captureID } = req.params; | ||
| const publisherUrl = process.env.PUBLISHER_URL || "http://localhost:9200"; | ||
|
|
||
| const captureIdPattern = /^[0-9]+$/; |
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.
Is there a char limit on this id?
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.
no, should we have it?
| ): Promise<{ id: number; status: string; attemptCount: number }> { | ||
| const publisherUrl = process.env.PUBLISHER_URL || "http://localhost:9200"; | ||
|
|
||
| const response = await fetch(`${publisherUrl}/api/dkg/assets`, { |
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.
also no timeout. Also, why are the options hardcoded to "private" and "2". The user should be able to pass those values
|
|
||
| // POST /epcis/capture - Accept EPCISDocument and queue for publishing | ||
| api.post( | ||
| "/epcis/capture", |
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.
maybe we could add a size limit or the file that is sent here?
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.
there is a middleware limit of 1gb, we can add some smaller limit if needed
| } | ||
|
|
||
| // Send to publisher | ||
| const result = await sendToPublisher(document, { |
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.
There should be some retries here and a fallback publishing using dkg.asset.create if the publisher plugin fails
| if (!document || typeof document !== "object") { | ||
| return { | ||
| valid: false, | ||
| errors: ["Document must be a valid JSON object"], | ||
| }; | ||
| } | ||
|
|
||
| const doc = document as EPCISDocument; | ||
|
|
||
| // Check for required type | ||
| if (doc.type !== "EPCISDocument") { | ||
| return { | ||
| valid: false, | ||
| errors: [`Invalid type: expected "EPCISDocument", got "${doc.type}"`], | ||
| }; | ||
| } | ||
|
|
||
| // Get event list from either location | ||
| const eventList = doc.eventList || doc.epcisBody?.eventList; | ||
|
|
||
| if (!eventList || !Array.isArray(eventList) || eventList.length === 0) { | ||
| return { | ||
| valid: false, | ||
| errors: ["EPCISDocument must contain at least one event in eventList or epcisBody.eventList"], | ||
| }; | ||
| } |
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.
Why are we doing manual validation if ajv does that for us? Or is ajv not reliable enough?
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.
relict from previous iteration will remove it, also there was a possiblity to make epcis standard more loose so it accepts different forms as partners might require that depending on their business logic
| } | ||
|
|
||
| // Get event list from either location | ||
| const eventList = doc.eventList || doc.epcisBody?.eventList; |
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.
According to the epcis json schema eventList can only be found in epcisBody.eventList, not directly in root. So if such a wrongfully formatted doc came throught this it would pass this check but fail on ajv check, which would be quite confusing.
| if (params.ual) { | ||
| return this.getEventByUal(params.ual); | ||
| } |
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.
if someone passes ual and some exclusive fields they are interested in they will still get everything back
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.
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
| from: from as string, | ||
| to: to as string, |
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.
There should be some validation of these vars before we pass them to the query builder
| ${filterClauses.join('\n ')} | ||
| } | ||
| ORDER BY DESC(?eventTime) | ||
| LIMIT 100`; |
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.
Why is the limit hardcoded? This should not be up to us to decide
|
|
||
| res.json({ | ||
| success: true, | ||
| query: sparqlQuery, |
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.
why are we returning the query?
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.
why not? i find it nicely informative
| // Namespace prefixes for EPCIS queries | ||
| const PREFIXES = ` | ||
| PREFIX epcis: <https://gs1.github.io/EPCIS/> | ||
| PREFIX kam: <https://kam.example.com/epcis/> |
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.
does kam need to be here?
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.
will be removed
|
|
||
| // BizLocation filter | ||
| if (params.bizLocation) { | ||
| wherePatterns.push(`?event epcis:bizLocation "${escapeSparql(params.bizLocation)}" .`); |
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.
I checked in the epcis json schema and it seems like the bizLocation expects the ID property, so it should probably be params.bizLocation.id
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.
will be changed
No description provided.