| 440 | } |
| 441 | |
| 442 | private normalizeAttachments( |
| 443 | attachments: unknown, |
| 444 | ): ChannelAttachment[] | undefined { |
| 445 | if (attachments === undefined) { |
| 446 | return undefined; |
| 447 | } |
| 448 | |
| 449 | if (!Array.isArray(attachments)) { |
| 450 | throw new Error("attachments must be an array"); |
| 451 | } |
| 452 | |
| 453 | const root = path.resolve(this.rootDir); |
| 454 | return attachments.map((attachment, index) => { |
| 455 | if (!attachment || typeof attachment !== "object") { |
| 456 | throw new Error(`attachment ${index + 1} must be an object`); |
| 457 | } |
| 458 | |
| 459 | const maybeAttachment = attachment as Record<string, unknown>; |
| 460 | const filename = |
| 461 | typeof maybeAttachment.filename === "string" |
| 462 | ? maybeAttachment.filename.trim() |
| 463 | : ""; |
| 464 | const localPath = |
| 465 | typeof maybeAttachment.localPath === "string" |
| 466 | ? maybeAttachment.localPath.trim() |
| 467 | : ""; |
| 468 | const mimeType = |
| 469 | typeof maybeAttachment.mimeType === "string" |
| 470 | ? maybeAttachment.mimeType.trim() |
| 471 | : undefined; |
| 472 | const size = |
| 473 | typeof maybeAttachment.size === "number" && |
| 474 | Number.isFinite(maybeAttachment.size) |
| 475 | ? maybeAttachment.size |
| 476 | : undefined; |
| 477 | |
| 478 | if (!filename) { |
| 479 | throw new Error(`attachment ${index + 1} filename is required`); |
| 480 | } |
| 481 | if (!localPath) { |
| 482 | throw new Error(`attachment ${index + 1} localPath is required`); |
| 483 | } |
| 484 | |
| 485 | const resolvedPath = path.resolve(localPath); |
| 486 | const relativePath = path.relative(root, resolvedPath); |
| 487 | if ( |
| 488 | relativePath === ".." || |
| 489 | relativePath.startsWith(`..${path.sep}`) || |
| 490 | path.isAbsolute(relativePath) |
| 491 | ) { |
| 492 | throw new Error( |
| 493 | `attachment ${index + 1} is outside the skillpack root`, |
| 494 | ); |
| 495 | } |
| 496 | |
| 497 | const stats = fs.statSync(resolvedPath); |
| 498 | if (!stats.isFile()) { |
| 499 | throw new Error(`attachment ${index + 1} must point to a file`); |