Skip to content
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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

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


### Added

- Size validation check for uploaded attachments.
Copy link
Contributor

Choose a reason for hiding this comment

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

Added limit for file size ....


## Version 1.1.8

### Changed
Expand Down
68 changes: 49 additions & 19 deletions lib/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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";
});
}
}
Expand All @@ -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, {
Copy link
Contributor

Choose a reason for hiding this comment

The 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 (
Copy link
Contributor

Choose a reason for hiding this comment

The 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",
Expand Down Expand Up @@ -144,5 +174,5 @@ const Ext2MimeTyes = {
xml: "application/xml",
zip: "application/zip",
txt: "application/txt",
lst: "application/txt"
lst: "application/txt",
};
35 changes: 35 additions & 0 deletions tests/unit/validateAttachmentSize.test.js
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');
});
});

Loading