coverme-scanner 1.3.2 → 1.4.0
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/dist/prompts/orchestration.md +227 -7
- package/dist/report/generator.d.ts +33 -0
- package/dist/report/generator.d.ts.map +1 -1
- package/dist/report/generator.js +271 -2
- package/dist/report/generator.js.map +1 -1
- package/dist/report/index.d.ts +1 -1
- package/dist/report/index.d.ts.map +1 -1
- package/dist/report/index.js +13 -2
- package/dist/report/index.js.map +1 -1
- package/dist/templates/report-pdf.html +1497 -0
- package/dist/templates/report.html +513 -0
- package/dist/types.d.ts +28 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1630,6 +1630,187 @@ The final report should ONLY contain findings that are:
|
|
|
1630
1630
|
}
|
|
1631
1631
|
},
|
|
1632
1632
|
|
|
1633
|
+
"architectureOverview": {
|
|
1634
|
+
"components": [
|
|
1635
|
+
{
|
|
1636
|
+
"name": "API Server",
|
|
1637
|
+
"type": "service",
|
|
1638
|
+
"description": "Express.js REST API handling all client requests",
|
|
1639
|
+
"trustLevel": "semi-trusted"
|
|
1640
|
+
},
|
|
1641
|
+
{
|
|
1642
|
+
"name": "PostgreSQL",
|
|
1643
|
+
"type": "database",
|
|
1644
|
+
"description": "Primary data store for user and application data",
|
|
1645
|
+
"trustLevel": "trusted"
|
|
1646
|
+
},
|
|
1647
|
+
{
|
|
1648
|
+
"name": "Redis",
|
|
1649
|
+
"type": "cache",
|
|
1650
|
+
"description": "Session storage and rate limiting",
|
|
1651
|
+
"trustLevel": "trusted"
|
|
1652
|
+
},
|
|
1653
|
+
{
|
|
1654
|
+
"name": "External Payment API",
|
|
1655
|
+
"type": "external",
|
|
1656
|
+
"description": "Third-party payment processor",
|
|
1657
|
+
"trustLevel": "untrusted"
|
|
1658
|
+
}
|
|
1659
|
+
],
|
|
1660
|
+
"trustBoundaries": [
|
|
1661
|
+
{
|
|
1662
|
+
"name": "Client to API",
|
|
1663
|
+
"from": "Browser/Mobile",
|
|
1664
|
+
"to": "API Server",
|
|
1665
|
+
"protocol": "HTTPS"
|
|
1666
|
+
},
|
|
1667
|
+
{
|
|
1668
|
+
"name": "API to Database",
|
|
1669
|
+
"from": "API Server",
|
|
1670
|
+
"to": "PostgreSQL",
|
|
1671
|
+
"protocol": "TLS"
|
|
1672
|
+
}
|
|
1673
|
+
],
|
|
1674
|
+
"criticalAssets": [
|
|
1675
|
+
{
|
|
1676
|
+
"name": "User Credentials",
|
|
1677
|
+
"type": "credential",
|
|
1678
|
+
"location": "PostgreSQL users table",
|
|
1679
|
+
"protection": "bcrypt hashed"
|
|
1680
|
+
},
|
|
1681
|
+
{
|
|
1682
|
+
"name": "API Keys",
|
|
1683
|
+
"type": "key",
|
|
1684
|
+
"location": "Environment variables",
|
|
1685
|
+
"protection": "Encrypted at rest"
|
|
1686
|
+
},
|
|
1687
|
+
{
|
|
1688
|
+
"name": "Session Tokens",
|
|
1689
|
+
"type": "token",
|
|
1690
|
+
"location": "Redis",
|
|
1691
|
+
"protection": "HMAC signed"
|
|
1692
|
+
}
|
|
1693
|
+
],
|
|
1694
|
+
"dataFlows": [
|
|
1695
|
+
"User Authentication Flow",
|
|
1696
|
+
"Payment Processing Flow",
|
|
1697
|
+
"Data Export Flow"
|
|
1698
|
+
]
|
|
1699
|
+
},
|
|
1700
|
+
|
|
1701
|
+
"threatModel": [
|
|
1702
|
+
{
|
|
1703
|
+
"id": "T-001",
|
|
1704
|
+
"threat": "SQL Injection via user input",
|
|
1705
|
+
"category": "STRIDE",
|
|
1706
|
+
"strideType": "T",
|
|
1707
|
+
"status": "open",
|
|
1708
|
+
"relatedFindings": ["SEC-001", "DB-002"],
|
|
1709
|
+
"mitigation": "Use parameterized queries",
|
|
1710
|
+
"dreadScore": 8.5
|
|
1711
|
+
},
|
|
1712
|
+
{
|
|
1713
|
+
"id": "T-002",
|
|
1714
|
+
"threat": "Session hijacking via XSS",
|
|
1715
|
+
"category": "STRIDE",
|
|
1716
|
+
"strideType": "S",
|
|
1717
|
+
"status": "partial",
|
|
1718
|
+
"relatedFindings": ["SEC-003"],
|
|
1719
|
+
"mitigation": "CSP headers implemented but not comprehensive",
|
|
1720
|
+
"dreadScore": 7.2
|
|
1721
|
+
},
|
|
1722
|
+
{
|
|
1723
|
+
"id": "T-003",
|
|
1724
|
+
"threat": "Unauthorized data access",
|
|
1725
|
+
"category": "LINDDUN",
|
|
1726
|
+
"status": "mitigated",
|
|
1727
|
+
"relatedFindings": [],
|
|
1728
|
+
"mitigation": "Row-level security implemented",
|
|
1729
|
+
"dreadScore": 3.0
|
|
1730
|
+
}
|
|
1731
|
+
],
|
|
1732
|
+
|
|
1733
|
+
"qualityReview": {
|
|
1734
|
+
"deleteItems": [
|
|
1735
|
+
{
|
|
1736
|
+
"type": "delete",
|
|
1737
|
+
"file": "src/utils/oldHelpers.ts",
|
|
1738
|
+
"lines": 250,
|
|
1739
|
+
"description": "Entire file is dead code - functions never called",
|
|
1740
|
+
"reason": "No imports found in codebase"
|
|
1741
|
+
},
|
|
1742
|
+
{
|
|
1743
|
+
"type": "delete",
|
|
1744
|
+
"file": "src/legacy/auth.js",
|
|
1745
|
+
"lines": 180,
|
|
1746
|
+
"description": "Legacy auth implementation replaced by Clerk",
|
|
1747
|
+
"reason": "Migration completed 6 months ago"
|
|
1748
|
+
}
|
|
1749
|
+
],
|
|
1750
|
+
"mergeItems": [
|
|
1751
|
+
{
|
|
1752
|
+
"type": "merge",
|
|
1753
|
+
"file": "src/utils/validate.ts, src/helpers/validation.ts",
|
|
1754
|
+
"description": "Two files with overlapping validation functions",
|
|
1755
|
+
"reason": "DRY violation - consolidate into single module"
|
|
1756
|
+
}
|
|
1757
|
+
],
|
|
1758
|
+
"simplifyItems": [
|
|
1759
|
+
{
|
|
1760
|
+
"type": "simplify",
|
|
1761
|
+
"file": "src/services/payment.ts",
|
|
1762
|
+
"lines": 450,
|
|
1763
|
+
"description": "Overly complex payment flow with excessive error handling",
|
|
1764
|
+
"reason": "Can be reduced to ~200 lines with proper abstraction"
|
|
1765
|
+
}
|
|
1766
|
+
],
|
|
1767
|
+
"totalLinesRemovable": 680,
|
|
1768
|
+
"percentageOfCodebase": 4.2
|
|
1769
|
+
},
|
|
1770
|
+
|
|
1771
|
+
"actionItems": [
|
|
1772
|
+
{
|
|
1773
|
+
"id": "A-001",
|
|
1774
|
+
"title": "Fix SQL injection in user search",
|
|
1775
|
+
"priority": "immediate",
|
|
1776
|
+
"relatedFindings": ["SEC-001"],
|
|
1777
|
+
"effort": "low",
|
|
1778
|
+
"description": "Replace string concatenation with parameterized query"
|
|
1779
|
+
},
|
|
1780
|
+
{
|
|
1781
|
+
"id": "A-002",
|
|
1782
|
+
"title": "Implement rate limiting on auth endpoints",
|
|
1783
|
+
"priority": "immediate",
|
|
1784
|
+
"relatedFindings": ["AUTH-003"],
|
|
1785
|
+
"effort": "medium",
|
|
1786
|
+
"description": "Add Redis-based rate limiter to /login and /register"
|
|
1787
|
+
},
|
|
1788
|
+
{
|
|
1789
|
+
"id": "A-003",
|
|
1790
|
+
"title": "Add CSP headers",
|
|
1791
|
+
"priority": "high",
|
|
1792
|
+
"relatedFindings": ["SEC-005"],
|
|
1793
|
+
"effort": "low",
|
|
1794
|
+
"description": "Configure helmet with strict CSP policy"
|
|
1795
|
+
},
|
|
1796
|
+
{
|
|
1797
|
+
"id": "A-004",
|
|
1798
|
+
"title": "Clean up dead code",
|
|
1799
|
+
"priority": "medium",
|
|
1800
|
+
"relatedFindings": ["DEAD-001", "DEAD-002"],
|
|
1801
|
+
"effort": "medium",
|
|
1802
|
+
"description": "Remove 680 lines of unused code identified in quality review"
|
|
1803
|
+
},
|
|
1804
|
+
{
|
|
1805
|
+
"id": "A-005",
|
|
1806
|
+
"title": "Implement audit logging",
|
|
1807
|
+
"priority": "long-term",
|
|
1808
|
+
"relatedFindings": ["DATA-002"],
|
|
1809
|
+
"effort": "high",
|
|
1810
|
+
"description": "Add comprehensive audit trail for sensitive operations"
|
|
1811
|
+
}
|
|
1812
|
+
],
|
|
1813
|
+
|
|
1633
1814
|
"summary": {
|
|
1634
1815
|
"total": 10,
|
|
1635
1816
|
"critical": 1,
|
|
@@ -1638,17 +1819,42 @@ The final report should ONLY contain findings that are:
|
|
|
1638
1819
|
"low": 2,
|
|
1639
1820
|
"info": 0,
|
|
1640
1821
|
"mitigatedCount": 5,
|
|
1641
|
-
"falsePositiveCount": 3
|
|
1822
|
+
"falsePositiveCount": 3,
|
|
1823
|
+
"avgConfidence": 0.85
|
|
1642
1824
|
},
|
|
1643
1825
|
|
|
1644
1826
|
"findings": [
|
|
1645
1827
|
{
|
|
1646
1828
|
"id": "SEC-001",
|
|
1647
|
-
"title": "
|
|
1648
|
-
"severity": "
|
|
1829
|
+
"title": "SQL Injection in User Search",
|
|
1830
|
+
"severity": "critical",
|
|
1831
|
+
"category": "security",
|
|
1832
|
+
"file": "src/routes/users.ts",
|
|
1833
|
+
"line": 45,
|
|
1834
|
+
"code": "db.query(`SELECT * FROM users WHERE name LIKE '%${searchTerm}%'`)",
|
|
1835
|
+
"description": "User search parameter is directly concatenated into SQL query without sanitization",
|
|
1836
|
+
"impact": "Attacker can extract entire database contents, modify data, or execute system commands",
|
|
1837
|
+
"attackChain": [
|
|
1838
|
+
{"step": 1, "action": "Send malicious search term: ' OR '1'='1' --", "result": "Query returns all users"},
|
|
1839
|
+
{"step": 2, "action": "Use UNION SELECT to extract data from other tables", "result": "Database schema exposed"},
|
|
1840
|
+
{"step": 3, "action": "Extract password hashes and sensitive data", "result": "Full data breach"}
|
|
1841
|
+
],
|
|
1842
|
+
"recommendation": "Use parameterized queries: db.query('SELECT * FROM users WHERE name LIKE $1', [`%${searchTerm}%`])",
|
|
1843
|
+
"cwe": "CWE-89",
|
|
1844
|
+
"owasp": "A03:2021",
|
|
1845
|
+
"dreadScore": {
|
|
1846
|
+
"damage": 10,
|
|
1847
|
+
"reproducibility": 10,
|
|
1848
|
+
"exploitability": 9,
|
|
1849
|
+
"affectedUsers": 10,
|
|
1850
|
+
"discoverability": 8,
|
|
1851
|
+
"total": 9.4
|
|
1852
|
+
},
|
|
1853
|
+
"status": "open",
|
|
1854
|
+
"crossReferences": ["T-001"],
|
|
1855
|
+
"confidence": 95,
|
|
1649
1856
|
"fixOwner": "developer",
|
|
1650
|
-
"fixType": "code"
|
|
1651
|
-
"...": "other fields"
|
|
1857
|
+
"fixType": "code"
|
|
1652
1858
|
}
|
|
1653
1859
|
],
|
|
1654
1860
|
|
|
@@ -1682,8 +1888,22 @@ The final report should ONLY contain findings that are:
|
|
|
1682
1888
|
],
|
|
1683
1889
|
|
|
1684
1890
|
"positiveObservations": [
|
|
1685
|
-
|
|
1686
|
-
|
|
1891
|
+
{
|
|
1892
|
+
"title": "Strong Authentication Implementation",
|
|
1893
|
+
"description": "Clerk integration with proper session management, MFA support, and secure cookie settings"
|
|
1894
|
+
},
|
|
1895
|
+
{
|
|
1896
|
+
"title": "Comprehensive Input Validation",
|
|
1897
|
+
"description": "Zod schemas used consistently across API endpoints with proper error handling"
|
|
1898
|
+
},
|
|
1899
|
+
{
|
|
1900
|
+
"title": "Secure Database Access",
|
|
1901
|
+
"description": "Prisma ORM with parameterized queries, preventing SQL injection in core modules"
|
|
1902
|
+
},
|
|
1903
|
+
{
|
|
1904
|
+
"title": "Good Error Handling Patterns",
|
|
1905
|
+
"description": "Errors are caught and logged without exposing internal details to clients"
|
|
1906
|
+
}
|
|
1687
1907
|
],
|
|
1688
1908
|
|
|
1689
1909
|
"validationSummary": {
|
|
@@ -56,6 +56,30 @@ interface ExecutiveSummaryData {
|
|
|
56
56
|
architect?: number;
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
+
interface ScanStats {
|
|
60
|
+
filesScanned: number;
|
|
61
|
+
linesOfCode: number;
|
|
62
|
+
agentCount: number;
|
|
63
|
+
avgConfidence: number;
|
|
64
|
+
}
|
|
65
|
+
interface QualityReviewData {
|
|
66
|
+
deleteItems: Array<{
|
|
67
|
+
file: string;
|
|
68
|
+
lines: number;
|
|
69
|
+
description: string;
|
|
70
|
+
}>;
|
|
71
|
+
mergeItems: Array<{
|
|
72
|
+
file: string;
|
|
73
|
+
description: string;
|
|
74
|
+
}>;
|
|
75
|
+
simplifyItems: Array<{
|
|
76
|
+
file: string;
|
|
77
|
+
description: string;
|
|
78
|
+
}>;
|
|
79
|
+
totalLinesRemovable: number;
|
|
80
|
+
percentageOfCodebase: number;
|
|
81
|
+
totalItems: number;
|
|
82
|
+
}
|
|
59
83
|
interface ReportData {
|
|
60
84
|
projectName: string;
|
|
61
85
|
scanDate: string;
|
|
@@ -97,6 +121,9 @@ interface ReportData {
|
|
|
97
121
|
architectureOverviewHtml: string;
|
|
98
122
|
threatModelHtml: string;
|
|
99
123
|
actionItemsHtml: string;
|
|
124
|
+
scanStats?: ScanStats;
|
|
125
|
+
projectTree?: string;
|
|
126
|
+
qualityReview?: QualityReviewData;
|
|
100
127
|
}
|
|
101
128
|
export declare function calculateScore(result: ScanResult): {
|
|
102
129
|
grade: string;
|
|
@@ -116,5 +143,11 @@ export declare function generateHtmlReport(result: ScanResult, outputPath: strin
|
|
|
116
143
|
file?: string;
|
|
117
144
|
rejectionReason: string;
|
|
118
145
|
}>): Promise<void>;
|
|
146
|
+
export declare function generateProfessionalPdf(result: ScanResult, outputPath: string, falsePositives?: Array<{
|
|
147
|
+
id: string;
|
|
148
|
+
title: string;
|
|
149
|
+
file?: string;
|
|
150
|
+
rejectionReason: string;
|
|
151
|
+
}>): Promise<void>;
|
|
119
152
|
export {};
|
|
120
153
|
//# sourceMappingURL=generator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/report/generator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/report/generator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAyC,MAAM,aAAa,CAAC;AAEvG,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,kBAAkB,CAAC,EAAE,KAAK,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,UAAU,SAAS;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,iBAAiB;IACzB,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzE,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,UAAU;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;IACrC,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,cAAc,EAAE,gBAAgB,EAAE,CAAC;IACnC,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7F,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,CAAC,MAAM,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;IAC5E,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IAEnB,wBAAwB,EAAE,MAAM,CAAC;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IAExB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,iBAAiB,CAAC;CACnC;AA2BD,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA0BnF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAkBnE;AA6bD,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CAuB7E;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,MAAM,EAClB,cAAc,GAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAAM,GAChG,OAAO,CAAC,IAAI,CAAC,CAoFf;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,MAAM,EAClB,cAAc,GAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAAM,GAChG,OAAO,CAAC,IAAI,CAAC,CA+Ff;AAwID,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,MAAM,EAClB,cAAc,GAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAAM,GAChG,OAAO,CAAC,IAAI,CAAC,CAgPf"}
|
package/dist/report/generator.js
CHANGED
|
@@ -41,6 +41,7 @@ exports.generateExecutiveSummary = generateExecutiveSummary;
|
|
|
41
41
|
exports.renderTemplate = renderTemplate;
|
|
42
42
|
exports.generatePdfReport = generatePdfReport;
|
|
43
43
|
exports.generateHtmlReport = generateHtmlReport;
|
|
44
|
+
exports.generateProfessionalPdf = generateProfessionalPdf;
|
|
44
45
|
const fs = __importStar(require("fs"));
|
|
45
46
|
const path = __importStar(require("path"));
|
|
46
47
|
const puppeteer_1 = __importDefault(require("puppeteer"));
|
|
@@ -590,12 +591,14 @@ async function generatePdfReport(result, outputPath, falsePositives = []) {
|
|
|
590
591
|
});
|
|
591
592
|
const page = await browser.newPage();
|
|
592
593
|
await page.setContent(renderedHtml, { waitUntil: 'networkidle0' });
|
|
594
|
+
// Get the full page height for continuous scroll PDF
|
|
595
|
+
const bodyHeight = await page.evaluate('document.body.scrollHeight');
|
|
593
596
|
await page.pdf({
|
|
594
597
|
path: outputPath,
|
|
595
|
-
|
|
598
|
+
width: '210mm', // A4 width
|
|
599
|
+
height: `${bodyHeight + 80}px`, // Full content height + margins
|
|
596
600
|
margin: { top: '20mm', right: '20mm', bottom: '20mm', left: '20mm' },
|
|
597
601
|
printBackground: true,
|
|
598
|
-
preferCSSPageSize: true
|
|
599
602
|
});
|
|
600
603
|
await browser.close();
|
|
601
604
|
console.log(`PDF report generated: ${outputPath}`);
|
|
@@ -614,6 +617,33 @@ async function generateHtmlReport(result, outputPath, falsePositives = []) {
|
|
|
614
617
|
const typeGroups = createTypeGroups(result.findings);
|
|
615
618
|
const codeGroup = typeGroups.find(g => g.type === 'code');
|
|
616
619
|
const devopsGroup = typeGroups.find(g => g.type === 'devops');
|
|
620
|
+
// Prepare scan statistics
|
|
621
|
+
const scanStats = (result.filesScanned || result.linesOfCode) ? {
|
|
622
|
+
filesScanned: result.filesScanned || 0,
|
|
623
|
+
linesOfCode: result.linesOfCode || 0,
|
|
624
|
+
agentCount: result.agentsUsed?.length || 0,
|
|
625
|
+
avgConfidence: Math.round((result.summary?.avgConfidence || 0) * 100),
|
|
626
|
+
} : undefined;
|
|
627
|
+
// Prepare quality review data
|
|
628
|
+
const qr = result.qualityReview;
|
|
629
|
+
const qualityReview = qr ? {
|
|
630
|
+
deleteItems: qr.deleteItems?.map(i => ({
|
|
631
|
+
file: i.file,
|
|
632
|
+
lines: i.lines || 0,
|
|
633
|
+
description: i.description,
|
|
634
|
+
})) || [],
|
|
635
|
+
mergeItems: qr.mergeItems?.map(i => ({
|
|
636
|
+
file: i.file,
|
|
637
|
+
description: i.description,
|
|
638
|
+
})) || [],
|
|
639
|
+
simplifyItems: qr.simplifyItems?.map(i => ({
|
|
640
|
+
file: i.file,
|
|
641
|
+
description: i.description,
|
|
642
|
+
})) || [],
|
|
643
|
+
totalLinesRemovable: qr.totalLinesRemovable || 0,
|
|
644
|
+
percentageOfCodebase: qr.percentageOfCodebase || 0,
|
|
645
|
+
totalItems: (qr.deleteItems?.length || 0) + (qr.mergeItems?.length || 0) + (qr.simplifyItems?.length || 0),
|
|
646
|
+
} : undefined;
|
|
617
647
|
const data = {
|
|
618
648
|
projectName: result.projectName,
|
|
619
649
|
scanDate: new Date(result.scanDate).toLocaleDateString('en-US', {
|
|
@@ -652,9 +682,248 @@ async function generateHtmlReport(result, outputPath, falsePositives = []) {
|
|
|
652
682
|
architectureOverviewHtml: generateArchitectureOverviewHtml(result),
|
|
653
683
|
threatModelHtml: generateThreatModelHtml(result),
|
|
654
684
|
actionItemsHtml: generateActionItemsHtml(result),
|
|
685
|
+
// New scan statistics and quality review
|
|
686
|
+
scanStats,
|
|
687
|
+
projectTree: result.projectTree,
|
|
688
|
+
qualityReview,
|
|
655
689
|
};
|
|
656
690
|
const renderedHtml = renderTemplate(templateHtml, data);
|
|
657
691
|
fs.writeFileSync(outputPath, renderedHtml);
|
|
658
692
|
console.log(`HTML report generated: ${outputPath}`);
|
|
659
693
|
}
|
|
694
|
+
function formatDreadScore(score) {
|
|
695
|
+
if (!score)
|
|
696
|
+
return 'N/A';
|
|
697
|
+
return score.total.toFixed(2);
|
|
698
|
+
}
|
|
699
|
+
function getRiskLevel(result) {
|
|
700
|
+
const counts = getSeverityCounts(result.summary);
|
|
701
|
+
if (counts.critical > 0)
|
|
702
|
+
return 'CRITICAL';
|
|
703
|
+
if (counts.high > 0)
|
|
704
|
+
return 'HIGH';
|
|
705
|
+
if (counts.medium > 0)
|
|
706
|
+
return 'MEDIUM';
|
|
707
|
+
return 'LOW';
|
|
708
|
+
}
|
|
709
|
+
function enhanceFinding(f) {
|
|
710
|
+
return {
|
|
711
|
+
...f,
|
|
712
|
+
dreadTotal: formatDreadScore(f.dreadScore),
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
async function generateProfessionalPdf(result, outputPath, falsePositives = []) {
|
|
716
|
+
const templatePath = path.join(__dirname, '..', 'templates', 'report-pdf.html');
|
|
717
|
+
const templateHtml = fs.readFileSync(templatePath, 'utf-8');
|
|
718
|
+
const counts = getSeverityCounts(result.summary);
|
|
719
|
+
const riskLevel = getRiskLevel(result);
|
|
720
|
+
const { grade, value } = calculateScore(result);
|
|
721
|
+
// Prepare findings with enhanced data
|
|
722
|
+
const criticalFindings = result.findings.filter(f => f.severity === 'critical').map(enhanceFinding);
|
|
723
|
+
const highFindings = result.findings.filter(f => f.severity === 'high').map(enhanceFinding);
|
|
724
|
+
const mediumFindings = result.findings.filter(f => f.severity === 'medium').map(enhanceFinding);
|
|
725
|
+
const lowFindings = result.findings.filter(f => f.severity === 'low' || f.severity === 'info').map(enhanceFinding);
|
|
726
|
+
// Prepare executive summary data
|
|
727
|
+
const execSummary = result.executiveSummary;
|
|
728
|
+
const topRisks = typeof execSummary === 'object' && execSummary?.topRisks
|
|
729
|
+
? execSummary.topRisks
|
|
730
|
+
: criticalFindings.slice(0, 3).map(f => f.title);
|
|
731
|
+
const keyPositives = typeof execSummary === 'object' && execSummary?.positives
|
|
732
|
+
? execSummary.positives
|
|
733
|
+
: (result.positiveObservations || []).slice(0, 3).map(o => typeof o === 'string' ? o : o.title || o.description || '');
|
|
734
|
+
// Prepare architecture data
|
|
735
|
+
const arch = result.architectureOverview;
|
|
736
|
+
const architectureComponents = arch?.components?.map(c => ({
|
|
737
|
+
name: c.name,
|
|
738
|
+
type: c.type,
|
|
739
|
+
description: c.description,
|
|
740
|
+
trustLevel: c.trustLevel,
|
|
741
|
+
})) || [];
|
|
742
|
+
const criticalAssets = arch?.criticalAssets?.map(a => ({
|
|
743
|
+
name: a.name,
|
|
744
|
+
type: a.type,
|
|
745
|
+
location: a.location,
|
|
746
|
+
})) || [];
|
|
747
|
+
const entryPoints = arch?.dataFlows || [];
|
|
748
|
+
// Prepare threat model
|
|
749
|
+
const threatModelEntries = result.threatModel?.map(t => ({
|
|
750
|
+
id: t.id,
|
|
751
|
+
threat: t.threat,
|
|
752
|
+
category: t.category,
|
|
753
|
+
strideType: t.strideType,
|
|
754
|
+
status: t.status,
|
|
755
|
+
dreadScore: t.dreadScore,
|
|
756
|
+
relatedFindings: t.relatedFindings?.join(', ') || '',
|
|
757
|
+
})) || [];
|
|
758
|
+
// Prepare quality review
|
|
759
|
+
const qr = result.qualityReview;
|
|
760
|
+
const qualityReview = {
|
|
761
|
+
deleteItems: qr?.deleteItems?.map(i => ({
|
|
762
|
+
file: i.file,
|
|
763
|
+
lines: i.lines || 0,
|
|
764
|
+
description: i.description,
|
|
765
|
+
})) || [],
|
|
766
|
+
mergeItems: qr?.mergeItems?.map(i => ({
|
|
767
|
+
files: i.file,
|
|
768
|
+
description: i.description,
|
|
769
|
+
})) || [],
|
|
770
|
+
simplifyItems: qr?.simplifyItems?.map(i => ({
|
|
771
|
+
file: i.file,
|
|
772
|
+
description: i.description,
|
|
773
|
+
})) || [],
|
|
774
|
+
totalLinesRemovable: qr?.totalLinesRemovable || 0,
|
|
775
|
+
percentageOfCodebase: qr?.percentageOfCodebase || 0,
|
|
776
|
+
};
|
|
777
|
+
// Prepare action items
|
|
778
|
+
const actions = result.actionItems || [];
|
|
779
|
+
const mapActions = (items) => items.map(a => ({
|
|
780
|
+
id: a.id,
|
|
781
|
+
title: a.title,
|
|
782
|
+
effort: a.effort,
|
|
783
|
+
relatedFindings: a.relatedFindings?.join(', ') || '',
|
|
784
|
+
}));
|
|
785
|
+
const immediateActions = mapActions(actions.filter(a => a.priority === 'immediate'));
|
|
786
|
+
const highPriorityActions = mapActions(actions.filter(a => a.priority === 'high'));
|
|
787
|
+
const mediumPriorityActions = mapActions(actions.filter(a => a.priority === 'medium'));
|
|
788
|
+
const longTermActions = mapActions(actions.filter(a => a.priority === 'long-term'));
|
|
789
|
+
// Prepare positive observations
|
|
790
|
+
const positiveObservations = (result.positiveObservations || []).map(o => {
|
|
791
|
+
if (typeof o === 'string') {
|
|
792
|
+
return { title: o, description: '' };
|
|
793
|
+
}
|
|
794
|
+
return { title: o.title || '', description: o.description || '' };
|
|
795
|
+
});
|
|
796
|
+
// Prepare project overview
|
|
797
|
+
const projOverview = result.projectOverview;
|
|
798
|
+
const projectType = projOverview?.type || 'Application';
|
|
799
|
+
const projectDescription = projOverview?.purpose || '';
|
|
800
|
+
const stack = projOverview?.stack?.join(', ') || '';
|
|
801
|
+
const componentsText = projOverview?.keyComponents?.join(', ') || '';
|
|
802
|
+
// Prepare trust boundaries and assets as strings for display
|
|
803
|
+
const trustBoundariesText = arch?.trustBoundaries?.map(b => `${b.from} -> ${b.to} (${b.protocol || 'N/A'})`).join(', ') || '';
|
|
804
|
+
const criticalAssetsText = arch?.criticalAssets?.map(a => `${a.name} (${a.type})`).join(', ') || '';
|
|
805
|
+
const entryPointsText = entryPoints.join(', ') || '';
|
|
806
|
+
// Quality review with total items count
|
|
807
|
+
const qualityReviewWithTotals = {
|
|
808
|
+
...qualityReview,
|
|
809
|
+
totalItems: (qualityReview.deleteItems?.length || 0) +
|
|
810
|
+
(qualityReview.mergeItems?.length || 0) +
|
|
811
|
+
(qualityReview.simplifyItems?.length || 0),
|
|
812
|
+
};
|
|
813
|
+
// Generate executive summary HTML
|
|
814
|
+
const execSummaryText = generateExecutiveSummary(result);
|
|
815
|
+
const executiveSummaryHtml = `<p>${execSummaryText}</p>`;
|
|
816
|
+
// Cover description
|
|
817
|
+
const coverDescription = projectDescription ||
|
|
818
|
+
`Security assessment covering ${result.filesScanned || 0} files with ${result.findings?.length || 0} findings identified.`;
|
|
819
|
+
const data = {
|
|
820
|
+
projectName: result.projectName,
|
|
821
|
+
scanDate: new Date(result.scanDate).toLocaleDateString('en-US', {
|
|
822
|
+
year: 'numeric',
|
|
823
|
+
month: 'long',
|
|
824
|
+
day: 'numeric',
|
|
825
|
+
}),
|
|
826
|
+
scanDateFull: new Date(result.scanDate).toISOString(),
|
|
827
|
+
branch: result.branch,
|
|
828
|
+
commit: result.commit?.substring(0, 8),
|
|
829
|
+
scope: 'Full Security Assessment',
|
|
830
|
+
assessedBy: 'CoverMe Security Scanner',
|
|
831
|
+
version: '2.0',
|
|
832
|
+
// Project Overview
|
|
833
|
+
projectType,
|
|
834
|
+
projectDescription,
|
|
835
|
+
stack,
|
|
836
|
+
componentsText,
|
|
837
|
+
coverDescription,
|
|
838
|
+
// Project Tree and Lines of Code
|
|
839
|
+
projectTree: result.projectTree,
|
|
840
|
+
linesOfCode: result.linesOfCode || 0,
|
|
841
|
+
// Severity counts
|
|
842
|
+
criticalCount: counts.critical,
|
|
843
|
+
highCount: counts.high,
|
|
844
|
+
mediumCount: counts.medium,
|
|
845
|
+
lowCount: counts.low,
|
|
846
|
+
infoCount: counts.info,
|
|
847
|
+
totalFindings: result.findings?.length || 0,
|
|
848
|
+
// Risk
|
|
849
|
+
riskScore: `${grade.toUpperCase()} (${value}/100)`,
|
|
850
|
+
riskLevel,
|
|
851
|
+
// Executive Summary
|
|
852
|
+
executiveSummary: execSummaryText,
|
|
853
|
+
executiveSummaryHtml,
|
|
854
|
+
topRisks,
|
|
855
|
+
keyPositives,
|
|
856
|
+
// Architecture
|
|
857
|
+
architectureComponents,
|
|
858
|
+
criticalAssets: criticalAssetsText,
|
|
859
|
+
trustBoundaries: trustBoundariesText,
|
|
860
|
+
entryPoints: entryPointsText,
|
|
861
|
+
// Findings
|
|
862
|
+
criticalFindings,
|
|
863
|
+
highFindings,
|
|
864
|
+
mediumFindings,
|
|
865
|
+
lowFindings,
|
|
866
|
+
// Threat Model
|
|
867
|
+
threatModel: threatModelEntries,
|
|
868
|
+
// Quality Review
|
|
869
|
+
qualityReview: qualityReviewWithTotals,
|
|
870
|
+
// Resolved Issues (empty for now, can be populated from scan results)
|
|
871
|
+
resolvedIssues: [],
|
|
872
|
+
// Action Items
|
|
873
|
+
immediateActions,
|
|
874
|
+
highPriorityActions,
|
|
875
|
+
mediumPriorityActions,
|
|
876
|
+
longTermActions,
|
|
877
|
+
// Positive Observations
|
|
878
|
+
positiveObservations,
|
|
879
|
+
// Stats
|
|
880
|
+
filesScanned: result.filesScanned || 0,
|
|
881
|
+
agentCount: result.agentsUsed?.length || 0,
|
|
882
|
+
scanDuration: `${Math.round((result.scanDuration || 0) / 1000)} seconds`,
|
|
883
|
+
avgConfidence: Math.round((result.summary?.avgConfidence || 0) * 100),
|
|
884
|
+
};
|
|
885
|
+
// Register Handlebars helpers
|
|
886
|
+
handlebars_1.default.registerHelper('eq', (a, b) => a === b);
|
|
887
|
+
handlebars_1.default.registerHelper('gt', (a, b) => a > b);
|
|
888
|
+
handlebars_1.default.registerHelper('gte', (a, b) => a >= b);
|
|
889
|
+
handlebars_1.default.registerHelper('lt', (a, b) => a < b);
|
|
890
|
+
handlebars_1.default.registerHelper('add', (a, b) => a + b);
|
|
891
|
+
handlebars_1.default.registerHelper('toFixed', (n, digits) => n?.toFixed(digits) || 'N/A');
|
|
892
|
+
const template = handlebars_1.default.compile(templateHtml);
|
|
893
|
+
const renderedHtml = template(data);
|
|
894
|
+
// Generate PDF with proper pagination
|
|
895
|
+
const browser = await puppeteer_1.default.launch({
|
|
896
|
+
headless: true,
|
|
897
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
898
|
+
});
|
|
899
|
+
const page = await browser.newPage();
|
|
900
|
+
await page.setContent(renderedHtml, { waitUntil: 'networkidle0' });
|
|
901
|
+
// A4 PDF with proper page breaks
|
|
902
|
+
await page.pdf({
|
|
903
|
+
path: outputPath,
|
|
904
|
+
format: 'A4',
|
|
905
|
+
margin: {
|
|
906
|
+
top: '20mm',
|
|
907
|
+
right: '15mm',
|
|
908
|
+
bottom: '20mm',
|
|
909
|
+
left: '15mm',
|
|
910
|
+
},
|
|
911
|
+
printBackground: true,
|
|
912
|
+
displayHeaderFooter: true,
|
|
913
|
+
headerTemplate: `
|
|
914
|
+
<div style="width: 100%; font-size: 9px; color: #666; padding: 0 15mm; display: flex; justify-content: space-between;">
|
|
915
|
+
<span>${escapeHtml(result.projectName)} - Security Assessment</span>
|
|
916
|
+
<span>CONFIDENTIAL</span>
|
|
917
|
+
</div>
|
|
918
|
+
`,
|
|
919
|
+
footerTemplate: `
|
|
920
|
+
<div style="width: 100%; font-size: 9px; color: #666; padding: 0 15mm; display: flex; justify-content: space-between;">
|
|
921
|
+
<span>Generated by CoverMe Security Scanner</span>
|
|
922
|
+
<span>Page <span class="pageNumber"></span> of <span class="totalPages"></span></span>
|
|
923
|
+
</div>
|
|
924
|
+
`,
|
|
925
|
+
});
|
|
926
|
+
await browser.close();
|
|
927
|
+
console.log(`Professional PDF report generated: ${outputPath}`);
|
|
928
|
+
}
|
|
660
929
|
//# sourceMappingURL=generator.js.map
|