coverme-security-scanner 3.7.3 → 3.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/coverme.js CHANGED
File without changes
File without changes
File without changes
@@ -402,6 +402,9 @@ export class PDFGenerator {
402
402
  this.y = this.doc.y + spacing.paragraph;
403
403
  }
404
404
  if (report.architecture?.components?.length) {
405
+ // Start components table on new page to keep it together
406
+ this.newPage();
407
+ this.y = spacing.page.top;
405
408
  this.subTitle('Components');
406
409
  this.renderSimpleTable(['Component', 'Technology', 'Description'], report.architecture.components.map(c => [c.name, c.technology, c.description]), [100, 150, 245]);
407
410
  }
@@ -434,7 +437,6 @@ export class PDFGenerator {
434
437
  this.y += 18;
435
438
  });
436
439
  }
437
- this.checkPageBreak();
438
440
  }
439
441
  // ─────────────────────────────────────────────────────────────────
440
442
  // Network
@@ -614,15 +616,20 @@ export class PDFGenerator {
614
616
  const boxPadding = 12;
615
617
  const boxInnerWidth = layout.content.width - (boxPadding * 2);
616
618
  const style = colors.severity[finding.severity];
617
- // ID in severity color, title in black
619
+ // ID in severity color, title in black - allow wrapping for long titles
620
+ const titleText = `[${finding.id}] ${finding.title}`;
621
+ const titleHeight = this.doc
622
+ .font(fonts.weights.bold)
623
+ .fontSize(fonts.sizes.body)
624
+ .heightOfString(titleText, { width: layout.content.width });
618
625
  this.doc
619
626
  .font(fonts.weights.bold)
620
627
  .fontSize(fonts.sizes.body)
621
628
  .fillColor(style.text)
622
629
  .text(`[${finding.id}]`, spacing.page.margin, this.y, { continued: true })
623
630
  .fillColor(colors.text.primary)
624
- .text(` ${finding.title}`);
625
- this.y += 18;
631
+ .text(` ${finding.title}`, { width: layout.content.width - 60 });
632
+ this.y = this.doc.y + 4;
626
633
  // Cross-references and DREAD score line
627
634
  const hasRefs = finding.relatedFindings?.length || finding.dreadScore || finding.cwe;
628
635
  if (hasRefs) {
@@ -1141,53 +1148,73 @@ export class PDFGenerator {
1141
1148
  this.y += 20;
1142
1149
  }
1143
1150
  renderSimpleTable(headers, rows, colWidths) {
1144
- const rowHeight = 24;
1151
+ const minRowHeight = 24;
1145
1152
  const x = spacing.page.margin;
1146
- // Header
1147
- this.doc
1148
- .rect(x, this.y, layout.content.width, rowHeight)
1149
- .fill(colors.table.header);
1150
- let colX = x;
1151
- headers.forEach((header, i) => {
1153
+ const cellPadding = 8;
1154
+ // Helper to render table header
1155
+ const renderHeader = () => {
1152
1156
  this.doc
1153
- .font(fonts.weights.bold)
1154
- .fontSize(fonts.sizes.small)
1155
- .fillColor(colors.table.headerText)
1156
- .text(header, colX + 8, this.y + 7, {
1157
- width: colWidths[i] - 16,
1157
+ .rect(x, this.y, layout.content.width, minRowHeight)
1158
+ .fill(colors.table.header);
1159
+ let colX = x;
1160
+ headers.forEach((header, i) => {
1161
+ this.doc
1162
+ .font(fonts.weights.bold)
1163
+ .fontSize(fonts.sizes.small)
1164
+ .fillColor(colors.table.headerText)
1165
+ .text(header, colX + cellPadding, this.y + 7, {
1166
+ width: colWidths[i] - (cellPadding * 2),
1167
+ });
1168
+ colX += colWidths[i];
1158
1169
  });
1159
- colX += colWidths[i];
1160
- });
1161
- this.y += rowHeight;
1162
- // Rows
1170
+ this.y += minRowHeight;
1171
+ };
1172
+ // Initial header
1173
+ renderHeader();
1174
+ // Rows - calculate dynamic height based on content
1163
1175
  rows.forEach((row, rowIndex) => {
1164
- this.checkPageBreak(rowHeight + 20);
1176
+ // Calculate the height needed for this row based on longest cell
1177
+ let maxCellHeight = minRowHeight;
1178
+ row.forEach((cell, i) => {
1179
+ const cellWidth = colWidths[i] - (cellPadding * 2);
1180
+ const textHeight = this.doc
1181
+ .font(fonts.weights.normal)
1182
+ .fontSize(fonts.sizes.small)
1183
+ .heightOfString(cell, { width: cellWidth });
1184
+ const cellHeight = Math.max(minRowHeight, textHeight + (cellPadding * 2));
1185
+ maxCellHeight = Math.max(maxCellHeight, cellHeight);
1186
+ });
1187
+ // Check if we need a page break - if so, re-render header on new page
1188
+ const needsPageBreak = this.y > layout.page.height - spacing.page.bottom - maxCellHeight - 20;
1189
+ if (needsPageBreak) {
1190
+ this.newPage();
1191
+ renderHeader();
1192
+ }
1165
1193
  // Alternating background
1166
1194
  if (rowIndex % 2 === 1) {
1167
1195
  this.doc
1168
- .rect(x, this.y, layout.content.width, rowHeight)
1196
+ .rect(x, this.y, layout.content.width, maxCellHeight)
1169
1197
  .fill(colors.table.altRow);
1170
1198
  }
1171
1199
  // Border
1172
1200
  this.doc
1173
- .moveTo(x, this.y + rowHeight)
1174
- .lineTo(x + layout.content.width, this.y + rowHeight)
1201
+ .moveTo(x, this.y + maxCellHeight)
1202
+ .lineTo(x + layout.content.width, this.y + maxCellHeight)
1175
1203
  .strokeColor(colors.table.border)
1176
1204
  .lineWidth(0.5)
1177
1205
  .stroke();
1178
- colX = x;
1206
+ let colX = x;
1179
1207
  row.forEach((cell, i) => {
1180
1208
  this.doc
1181
1209
  .font(fonts.weights.normal)
1182
1210
  .fontSize(fonts.sizes.small)
1183
1211
  .fillColor(colors.text.primary)
1184
- .text(cell, colX + 8, this.y + 7, {
1185
- width: colWidths[i] - 16,
1186
- ellipsis: true,
1212
+ .text(cell, colX + cellPadding, this.y + 7, {
1213
+ width: colWidths[i] - (cellPadding * 2),
1187
1214
  });
1188
1215
  colX += colWidths[i];
1189
1216
  });
1190
- this.y += rowHeight;
1217
+ this.y += maxCellHeight;
1191
1218
  });
1192
1219
  this.y += spacing.paragraph;
1193
1220
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coverme-security-scanner",
3
- "version": "3.7.3",
3
+ "version": "3.7.4",
4
4
  "description": "AI-powered security assessment reports with beautiful PDF output",
5
5
  "type": "module",
6
6
  "bin": {