-
Notifications
You must be signed in to change notification settings - Fork 4
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
Limiting file size #105
base: main
Are you sure you want to change the base?
Limiting file size #105
Changes from 5 commits
3264397
f342e8f
7402a98
dad007d
c02e2dd
c14f396
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 |
---|---|---|
|
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. | |
This project adheres to [Semantic Versioning](http://semver.org/). | ||
The format is based on [Keep a Changelog](http://keepachangelog.com/). | ||
|
||
## Version 1.1.9 | ||
|
||
### Added | ||
|
||
- Size validation check for uploaded attachments. | ||
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.
|
||
|
||
## Version 1.1.8 | ||
|
||
### Changed | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ const attachmentIDRegex = /attachments\(.*ID=([^,]+),.*\)/; | |
|
||
cds.on("loaded", function unfoldModel(csn) { | ||
if (!("Attachments" in csn.definitions)) return; | ||
const csnCopy = structuredClone(csn) | ||
const csnCopy = structuredClone(csn); | ||
cds.linked(csnCopy).forall("Composition", (comp) => { | ||
if (comp._target && comp._target["@_is_media_data"] && comp.parent && comp.is2many) { | ||
const parentDefinition = comp.parent.name | ||
|
@@ -31,24 +31,28 @@ cds.once("served", async function registerPluginHandlers() { | |
for (let srv of cds.services) { | ||
if (srv instanceof cds.ApplicationService) { | ||
Object.values(srv.entities).forEach((entity) => { | ||
|
||
for (let elementName in entity.elements) { | ||
if (elementName === "SiblingEntity") continue; // REVISIT: Why do we have this? | ||
const element = entity.elements[elementName], target = element._target; | ||
const element = entity.elements[elementName], | ||
target = element._target; | ||
if (target?.["@_is_media_data"] && target?.drafts) { | ||
DEBUG?.("serving attachments for:", target.name); | ||
|
||
srv.before("READ", [target, target.drafts], validateAttachment); | ||
|
||
srv.after("READ", [target, target.drafts], readAttachment); | ||
|
||
srv.before("PUT", target.drafts, (req) => validateAttachmentSize(req) ); | ||
|
||
|
||
AttachmentsSrv.registerUpdateHandlers(srv, entity, target); | ||
srv.before('NEW', target.drafts, req => { | ||
|
||
srv.before("NEW", target.drafts, (req) => { | ||
req.data.url = cds.utils.uuid(); | ||
req.data.ID = cds.utils.uuid(); | ||
let ext = extname(req.data.filename).toLowerCase().slice(1); | ||
req.data.mimeType = Ext2MimeTyes[ext] || "application/octet-stream"; | ||
req.data.mimeType = | ||
Ext2MimeTyes[ext] || "application/octet-stream"; | ||
}); | ||
} | ||
} | ||
|
@@ -57,34 +61,60 @@ cds.once("served", async function registerPluginHandlers() { | |
} | ||
|
||
async function validateAttachment(req) { | ||
|
||
/* removing case condition for mediaType annotation as in our case binary value and metadata is stored in different database */ | ||
|
||
req?.query?.SELECT?.columns?.forEach((element) => { | ||
if(element.as === '[email protected]' && element.xpr){ | ||
if (element.as === "[email protected]" && element.xpr) { | ||
delete element.xpr; | ||
element.ref = ['mimeType']; | ||
element.ref = ["mimeType"]; | ||
} | ||
}); | ||
|
||
if(req?.req?.url?.endsWith("/content")) { | ||
if (req?.req?.url?.endsWith("/content")) { | ||
const attachmentID = req.req.url.match(attachmentIDRegex)[1]; | ||
const status = await AttachmentsSrv.getStatus(req.target, { ID : attachmentID }); | ||
const scanEnabled = cds.env.requires?.attachments?.scan ?? true | ||
if(scanEnabled && status !== 'Clean') { | ||
req.reject(403, 'Unable to download the attachment as scan status is not clean.'); | ||
const status = await AttachmentsSrv.getStatus(req.target, { | ||
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. you can remove the code formatting |
||
ID: attachmentID, | ||
}); | ||
const scanEnabled = cds.env.requires?.attachments?.scan ?? true; | ||
if (scanEnabled && status !== "Clean") { | ||
req.reject( | ||
403, | ||
"Unable to download the attachment as scan status is not clean." | ||
); | ||
} | ||
} | ||
} | ||
|
||
async function readAttachment([attachment], req) { | ||
if (!req?.req?.url?.endsWith("/content") || !attachment || attachment?.content) return; | ||
let keys = { ID : req.req.url.match(attachmentIDRegex)[1]}; | ||
if ( | ||
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. here also |
||
!req?.req?.url?.endsWith("/content") || | ||
!attachment || | ||
attachment?.content | ||
) | ||
return; | ||
let keys = { ID: req.req.url.match(attachmentIDRegex)[1] }; | ||
let { target } = req; | ||
attachment.content = await AttachmentsSrv.get(target, keys, req); //Dependency -> sending req object for usage in SDM plugin | ||
} | ||
}); | ||
|
||
function validateAttachmentSize(req) { | ||
const contentLengthHeader = req.headers["content-length"]; | ||
let fileSizeInBytes; | ||
|
||
if (contentLengthHeader) { | ||
fileSizeInBytes = Number(contentLengthHeader); | ||
const MAX_FILE_SIZE = 419430400; //400 MB in bytes | ||
if (fileSizeInBytes > MAX_FILE_SIZE) { | ||
return req.reject(403, "File Size limit exceeded beyond 400 MB."); | ||
} | ||
} else { | ||
return req.reject(403, "Invalid Content Size"); | ||
} | ||
} | ||
|
||
module.exports = { validateAttachmentSize }; | ||
|
||
const Ext2MimeTyes = { | ||
aac: "audio/aac", | ||
abw: "application/x-abiword", | ||
|
@@ -144,5 +174,5 @@ const Ext2MimeTyes = { | |
xml: "application/xml", | ||
zip: "application/zip", | ||
txt: "application/txt", | ||
lst: "application/txt" | ||
lst: "application/txt", | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
const { validateAttachmentSize } = require('../../lib/plugin'); | ||
|
||
describe('validateAttachmentSize', () => { | ||
let req; // Define a mock request object | ||
|
||
beforeEach(() => { | ||
req = { | ||
headers: {}, | ||
reject: jest.fn(), // Mocking the reject function | ||
}; | ||
}); | ||
|
||
it('should pass validation for a file size under 400 MB', () => { | ||
req.headers['content-length'] = '51200765'; | ||
|
||
validateAttachmentSize(req); | ||
|
||
expect(req.reject).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should reject for a file size over 400 MB', () => { | ||
req.headers['content-length'] = '20480000000'; | ||
|
||
validateAttachmentSize(req); | ||
|
||
expect(req.reject).toHaveBeenCalledWith(403, 'File Size limit exceeded beyond 400 MB.'); | ||
}); | ||
|
||
it('should reject when content-length header is missing', () => { | ||
validateAttachmentSize(req); | ||
|
||
expect(req.reject).toHaveBeenCalledWith(403, 'Invalid Content Size'); | ||
}); | ||
}); | ||
|
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.
Changelog has to be added in 1.1.8 version itself