audit.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. const { hasProjectYarn, hasProjectPnpm, execa } = require('@vue/cli-shared-utils')
  2. const severity = {
  3. critical: 0,
  4. high: 1,
  5. moderate: 2,
  6. low: 3
  7. }
  8. exports.auditProject = async function (cwd) {
  9. try {
  10. if (hasProjectYarn(cwd)) {
  11. const child = await execa('yarn', [
  12. 'audit',
  13. '--json',
  14. '--non-interactive',
  15. '--no-progress'
  16. ], {
  17. cwd,
  18. reject: false
  19. })
  20. if (child.stderr) {
  21. const errLines = child.stderr.split('\n').map(l => l.trim()).filter(l => l)
  22. const error = errLines.find(l => l.startsWith('Error:'))
  23. if (error) {
  24. throw new Error(error.substr('Error:'.length).trim())
  25. }
  26. }
  27. const data = child.stdout
  28. const auditAdvisories = []
  29. const ids = {}
  30. const lines = data.split('\n').filter(l => l.trim()).map(l => JSON.parse(l))
  31. for (const line of lines) {
  32. if (line.type === 'auditAdvisory') {
  33. if (!ids[line.data.advisory.id]) {
  34. auditAdvisories.push(line)
  35. ids[line.data.advisory.id] = true
  36. }
  37. }
  38. }
  39. const details = {
  40. vulnerabilities: [],
  41. summary: {
  42. critical: 0,
  43. high: 0,
  44. moderate: 0,
  45. low: 0
  46. }
  47. }
  48. auditAdvisories.sort((a, b) => severity[a.data.advisory.severity] - severity[b.data.advisory.severity])
  49. let id = 0
  50. for (const { data: { advisory } } of auditAdvisories) {
  51. for (const finding of advisory.findings) {
  52. // const [finding] = advisory.findings
  53. const detail = {
  54. id: id++,
  55. name: advisory.module_name,
  56. version: finding.version,
  57. parents: finding.paths.sort(
  58. (a, b) => a.length - b.length
  59. ).map(
  60. parents => parents.split('>').slice(0, parents.length - 2).map(p => ({
  61. name: p
  62. }))
  63. ),
  64. moreInfo: advisory.url,
  65. severity: advisory.severity,
  66. title: advisory.title,
  67. message: advisory.overview,
  68. versions: {
  69. vulnerable: advisory.vulnerable_versions,
  70. patched: advisory.patched_versions
  71. },
  72. recommendation: advisory.recommendation
  73. }
  74. details.vulnerabilities.push(detail)
  75. details.summary[advisory.severity]++
  76. }
  77. }
  78. const status = {
  79. status: 'ok',
  80. count: details.vulnerabilities.length,
  81. message: null
  82. }
  83. if (status.count) {
  84. status.status = 'attention'
  85. }
  86. for (const n in details.summary) {
  87. if (details.summary) {
  88. status.severity = n
  89. break
  90. }
  91. }
  92. return {
  93. status,
  94. details
  95. }
  96. } else if (hasProjectPnpm(cwd)) {
  97. // TODO pnpm audit
  98. return {
  99. status: {
  100. status: 'error',
  101. message: 'Not implemented for PNPM projects yet'
  102. },
  103. details: null
  104. }
  105. } else {
  106. // TODO NPM audit
  107. return {
  108. status: {
  109. status: 'error',
  110. message: 'Not implemented for NPM projects yet'
  111. },
  112. details: null
  113. }
  114. }
  115. } catch (e) {
  116. return {
  117. status: {
  118. status: 'error',
  119. message: e.message
  120. },
  121. details: null
  122. }
  123. }
  124. }