Browse Source

Nc fix/attachments (#8478)

* fix: sanitize attachment input properly & throw if invalid

* fix: looked up attachments

* fix: avoid extra slash on attachment path

---------

Co-authored-by: mertmit <mertmit99@gmail.com>
pull/8484/head
Pranav C 2 months ago committed by GitHub
parent
commit
5b2ba2cb7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      packages/nocodb-sdk/src/lib/globals.ts
  2. 43
      packages/nocodb/src/db/BaseModelSqlv2.ts
  3. 12
      packages/nocodb/src/helpers/catchError.ts
  4. 6
      packages/nocodb/src/services/attachments.service.ts

1
packages/nocodb-sdk/src/lib/globals.ts

@ -140,6 +140,7 @@ export enum NcErrorType {
INVALID_LIMIT_VALUE = 'INVALID_LIMIT_VALUE', INVALID_LIMIT_VALUE = 'INVALID_LIMIT_VALUE',
INVALID_FILTER = 'INVALID_FILTER', INVALID_FILTER = 'INVALID_FILTER',
INVALID_SHARED_VIEW_PASSWORD = 'INVALID_SHARED_VIEW_PASSWORD', INVALID_SHARED_VIEW_PASSWORD = 'INVALID_SHARED_VIEW_PASSWORD',
INVALID_ATTACHMENT_JSON = 'INVALID_ATTACHMENT_JSON',
NOT_IMPLEMENTED = 'NOT_IMPLEMENTED', NOT_IMPLEMENTED = 'NOT_IMPLEMENTED',
INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR', INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR',
DATABASE_ERROR = 'DATABASE_ERROR', DATABASE_ERROR = 'DATABASE_ERROR',

43
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -5491,7 +5491,13 @@ class BaseModelSqlv2 {
} }
if (d[col.id]?.length) { if (d[col.id]?.length) {
for (const attachment of d[col.id]) { for (let i = 0; i < d[col.id].length; i++) {
if (typeof d[col.id][i] === 'string') {
d[col.id][i] = JSON.parse(d[col.id][i]);
}
const attachment = d[col.id][i];
// we expect array of array of attachments in case of lookup // we expect array of array of attachments in case of lookup
if (Array.isArray(attachment)) { if (Array.isArray(attachment)) {
for (const lookedUpAttachment of attachment) { for (const lookedUpAttachment of attachment) {
@ -6477,17 +6483,34 @@ class BaseModelSqlv2 {
} }
if (column.uidt === UITypes.Attachment) { if (column.uidt === UITypes.Attachment) {
if (data[column.column_name]) { if (data[column.column_name]) {
try {
if (typeof data[column.column_name] === 'string') {
data[column.column_name] = JSON.parse(data[column.column_name]);
}
} catch (e) {
NcError.invalidAttachmentJson(data[column.column_name]);
}
if (Array.isArray(data[column.column_name])) { if (Array.isArray(data[column.column_name])) {
for (let attachment of data[column.column_name]) { const sanitizedAttachments = [];
attachment = extractProps(attachment, [ for (const attachment of data[column.column_name]) {
'url', if (!('url' in attachment) && !('path' in attachment)) {
'path', NcError.unprocessableEntity(
'title', 'Attachment object must contain either url or path',
'mimetype', );
'size', }
'icon', sanitizedAttachments.push(
]); extractProps(attachment, [
'url',
'path',
'title',
'mimetype',
'size',
'icon',
]),
);
} }
data[column.column_name] = JSON.stringify(sanitizedAttachments);
} }
} }
} else if ( } else if (

12
packages/nocodb/src/helpers/catchError.ts

@ -525,6 +525,11 @@ const errorHelpers: {
message: 'Invalid shared view password', message: 'Invalid shared view password',
code: 403, code: 403,
}, },
[NcErrorType.INVALID_ATTACHMENT_JSON]: {
message: (payload: string) =>
`Invalid JSON for attachment field: ${payload}`,
code: 400,
},
[NcErrorType.NOT_IMPLEMENTED]: { [NcErrorType.NOT_IMPLEMENTED]: {
message: (feature: string) => `${feature} is not implemented`, message: (feature: string) => `${feature} is not implemented`,
code: 501, code: 501,
@ -688,6 +693,13 @@ export class NcError {
}); });
} }
static invalidAttachmentJson(payload: string, args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.INVALID_ATTACHMENT_JSON, {
params: payload,
...args,
});
}
static notImplemented(feature: string = 'Feature', args?: NcErrorArgs) { static notImplemented(feature: string = 'Feature', args?: NcErrorArgs) {
throw new NcBaseErrorv2(NcErrorType.NOT_IMPLEMENTED, { throw new NcBaseErrorv2(NcErrorType.NOT_IMPLEMENTED, {
params: feature, params: feature,

6
packages/nocodb/src/services/attachments.service.ts

@ -73,7 +73,11 @@ export class AttachmentsService {
if (!url) { if (!url) {
// then store the attachment path only // then store the attachment path only
// url will be constructed in `useAttachmentCell` // url will be constructed in `useAttachmentCell`
attachment.path = `download/${filePath.join('/')}/${fileName}`; attachment.path = path.join(
'download',
filePath.join('/'),
fileName,
);
attachment.signedPath = await PresignedUrl.getSignedUrl({ attachment.signedPath = await PresignedUrl.getSignedUrl({
path: attachment.path.replace(/^download\//, ''), path: attachment.path.replace(/^download\//, ''),

Loading…
Cancel
Save