diff --git a/src/MsrcSecurityUpdates/MsrcSecurityUpdates.psd1 b/src/MsrcSecurityUpdates/MsrcSecurityUpdates.psd1
index 364c03d..07b9efc 100644
Binary files a/src/MsrcSecurityUpdates/MsrcSecurityUpdates.psd1 and b/src/MsrcSecurityUpdates/MsrcSecurityUpdates.psd1 differ
diff --git a/src/MsrcSecurityUpdates/MsrcSecurityUpdates.tests.ps1 b/src/MsrcSecurityUpdates/MsrcSecurityUpdates.tests.ps1
index aa1572c..ebaef0b 100644
--- a/src/MsrcSecurityUpdates/MsrcSecurityUpdates.tests.ps1
+++ b/src/MsrcSecurityUpdates/MsrcSecurityUpdates.tests.ps1
@@ -5,6 +5,23 @@ $Error.Clear()
Get-Module -Name MsrcSecurityUpdates | Remove-Module -Force -Verbose:$false
Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'MsrcSecurityUpdates.psd1') -Verbose:$false -Force
+Describe 'MSAL Authentication Migration' {
+ It 'Set-MSRCMsalAccessToken function should be available' {
+ Get-Command Set-MSRCMsalAccessToken -ErrorAction SilentlyContinue | Should Not BeNullOrEmpty
+ }
+
+ It 'Module should not have legacy authentication DLL requirements' {
+ $manifest = Import-PowerShellDataFile (Join-Path -Path $PSScriptRoot -ChildPath 'MsrcSecurityUpdates.psd1')
+ $manifest.RequiredAssemblies | Where-Object { $_ -like '*ActiveDirectory*' } | Should BeNullOrEmpty
+ }
+
+ It 'Module should require MSAL.PS module' {
+ $manifest = Import-PowerShellDataFile (Join-Path -Path $PSScriptRoot -ChildPath 'MsrcSecurityUpdates.psd1')
+ $msalRequired = $manifest.RequiredModules | Where-Object { $_.ModuleName -eq 'MSAL.PS' }
+ $msalRequired | Should Not BeNullOrEmpty
+ }
+}
+
Describe 'API version after module loading' {
It '$msrcApiUrl = https://api.msrc.microsoft.com/cvrf/v3.0' {
$msrcApiUrl -eq 'https://api.msrc.microsoft.com/cvrf/v3.0' | Should Be $true
diff --git a/src/MsrcSecurityUpdates/Private/Get-CVRFID.ps1 b/src/MsrcSecurityUpdates/Private/Get-CVRFID.ps1
index fa683b3..6951285 100644
--- a/src/MsrcSecurityUpdates/Private/Get-CVRFID.ps1
+++ b/src/MsrcSecurityUpdates/Private/Get-CVRFID.ps1
@@ -23,10 +23,10 @@ Process {
$RestMethod.Add('ProxyCredential',$global:msrcProxyCredential)
- } elseif ($global:MSRCAdalAccessToken) {
-
- $RestMethod.Headers.Add('Authorization',$($global:MSRCAdalAccessToken.CreateAuthorizationHeader()))
+ } elseif ($script:MSRCMsalAccessToken) {
+ $RestMethod.Headers.Add('Authorization',"Bearer $($script:MSRCMsalAccessToken.AccessToken)")
+
}
try {
diff --git a/src/MsrcSecurityUpdates/Public/Get-KBDownloadUrl.ps1 b/src/MsrcSecurityUpdates/Public/Get-KBDownloadUrl.ps1
index 982223f..6ff598f 100644
--- a/src/MsrcSecurityUpdates/Public/Get-KBDownloadUrl.ps1
+++ b/src/MsrcSecurityUpdates/Public/Get-KBDownloadUrl.ps1
@@ -1,27 +1,27 @@
-Function Get-KBDownloadUrl {
-<#
- .SYNOPSIS
- Takes the kb output from Get-MsrcCvrfAffectedSoftware and builds the html to insert into a document.
-
- .DESCRIPTION
- Takes the kb output from Get-MsrcCvrfAffectedSoftware and builds the html to insert into a document.
-
- .PARAMETER KBArticleObject
- The KB Article object that contains the id, url, and subtype.
-
- .EXAMPLE
- [PSCustomObject]@{ID="kb123456"; URL="microsoft.com"; SubType="Required"} | Get-KBDownloadUrl
-#>
-[CmdletBinding()]
-[OutputType([System.String])]
-Param (
- [Parameter(Mandatory,ValueFromPipeline)]
- [PSCustomObject]$KBArticleObject
-)
-Begin {
- $HTML_TO_RETURN = @()
-}
-Process {
+Function Get-KBDownloadUrl {
+<#
+ .SYNOPSIS
+ Takes the kb output from Get-MsrcCvrfAffectedSoftware and builds the html to insert into a document.
+
+ .DESCRIPTION
+ Takes the kb output from Get-MsrcCvrfAffectedSoftware and builds the html to insert into a document.
+
+ .PARAMETER KBArticleObject
+ The KB Article object that contains the id, url, and subtype.
+
+ .EXAMPLE
+ [PSCustomObject]@{ID="kb123456"; URL="microsoft.com"; SubType="Required"} | Get-KBDownloadUrl
+#>
+[CmdletBinding()]
+[OutputType([System.String])]
+Param (
+ [Parameter(Mandatory,ValueFromPipeline)]
+ [PSCustomObject]$KBArticleObject
+)
+Begin {
+ $HTML_TO_RETURN = @()
+}
+Process {
if (-not($KBArticleObject)){
'None'
} else {
@@ -36,9 +36,9 @@ Process {
$HTML_TO_RETURN += $('{1}' -f $kb.URL, $kb.ID)
}
}
- }
-}
-End {
- $HTML_TO_RETURN -join '
'
-}
+ }
+}
+End {
+ $HTML_TO_RETURN -join '
'
+}
}
\ No newline at end of file
diff --git a/src/MsrcSecurityUpdates/Public/Get-MsrcCvrfAffectedSoftware.ps1 b/src/MsrcSecurityUpdates/Public/Get-MsrcCvrfAffectedSoftware.ps1
index 03d7b87..0db8ec3 100644
--- a/src/MsrcSecurityUpdates/Public/Get-MsrcCvrfAffectedSoftware.ps1
+++ b/src/MsrcSecurityUpdates/Public/Get-MsrcCvrfAffectedSoftware.ps1
@@ -81,7 +81,7 @@ Process {
Where-Object {$_.Type -eq $ThreatsImpactType } |
Where-Object { $_.ProductID -contains $id }
).Description.Value
- );
+ );
Weakness = $v.CWE.Value ;
'Customer Action Required' = if ($customerActionNotes = $v.Notes | Where-Object { $_.Title -eq "Customer Action Required" }) {
$customerActionNotes.Value
diff --git a/src/MsrcSecurityUpdates/Public/Get-MsrcCvrfDocument.ps1 b/src/MsrcSecurityUpdates/Public/Get-MsrcCvrfDocument.ps1
index 2edfff7..c03fe77 100644
--- a/src/MsrcSecurityUpdates/Public/Get-MsrcCvrfDocument.ps1
+++ b/src/MsrcSecurityUpdates/Public/Get-MsrcCvrfDocument.ps1
@@ -80,9 +80,9 @@ Process {
}
- if ($global:MSRCAdalAccessToken) {
-
- $RestMethod.Headers.Add('Authorization', $global:MSRCAdalAccessToken.CreateAuthorizationHeader())
+ if ($script:MSRCMsalAccessToken) {
+
+ $RestMethod.Headers.Add('Authorization', "Bearer $($script:MSRCMsalAccessToken.AccessToken)")
}
diff --git a/src/MsrcSecurityUpdates/Public/Get-MsrcSecurityUpdate.ps1 b/src/MsrcSecurityUpdates/Public/Get-MsrcSecurityUpdate.ps1
index 3f52a49..18ada7b 100644
--- a/src/MsrcSecurityUpdates/Public/Get-MsrcSecurityUpdate.ps1
+++ b/src/MsrcSecurityUpdates/Public/Get-MsrcSecurityUpdate.ps1
@@ -171,9 +171,8 @@ Process {
if ($global:msrcProxyCredential){
$RestMethod.Add('ProxyCredential' , $global:msrcProxyCredential)
}
- if ($global:MSRCAdalAccessToken)
- {
- $RestMethod.Headers.Add('Authorization' , $global:MSRCAdalAccessToken.CreateAuthorizationHeader())
+ if ($script:MSRCMsalAccessToken){
+ $RestMethod.Headers.Add('Authorization' , "Bearer $($script:MSRCMsalAccessToken.AccessToken)")
}
try {
diff --git a/src/MsrcSecurityUpdates/Public/Get-MsrcVulnerabilityReportHtml.ps1 b/src/MsrcSecurityUpdates/Public/Get-MsrcVulnerabilityReportHtml.ps1
index 3bf9f1e..0708bca 100644
--- a/src/MsrcSecurityUpdates/Public/Get-MsrcVulnerabilityReportHtml.ps1
+++ b/src/MsrcSecurityUpdates/Public/Get-MsrcVulnerabilityReportHtml.ps1
@@ -1,5 +1,5 @@
Function Get-MsrcVulnerabilityReportHtml {
-<#
+ <#
.SYNOPSIS
Use a CVRF document to create a Vulnerability summary
@@ -41,41 +41,42 @@ Function Get-MsrcVulnerabilityReportHtml {
It creates a report for specific Vulnerabilities in a CVRF document
#>
-[CmdletBinding()]
-[OutputType([string])]
-Param(
-
- [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
- $Vulnerability,
-
- [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
- $ProductTree,
-
- [Switch]$ShowNoProgress
-)
-Begin{
-
- $HT = @{ ErrorAction = 'Stop'}
-
- $MaximumSeverityType = 3
- $ThreatsImpactType = 0
- $ThreatsExploitStatusType = 1
- $TagType = 7
- $CNAType = 8
- $RemediationsMitigationType = 1
- $RemediationsWorkaroundType = 0
-
- try {
- $JsonMetrics = Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath 'CVSS-Metrics.json' @HT) @HT |
- Out-String @HT| ConvertFrom-Json @HT
-
- $JsonDescriptions = Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath 'CVSS-Descriptions.json'@HT) @HT |
- Out-String @HT| ConvertFrom-Json @HT
- } catch {
- Throw "Failed to get required json files content because $($_.Exception.Message)"
- }
+ [CmdletBinding()]
+ [OutputType([string])]
+ Param(
+
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
+ $Vulnerability,
+
+ [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
+ $ProductTree,
+
+ [Switch]$ShowNoProgress
+ )
+ Begin {
- $css = @'
+ $HT = @{ ErrorAction = 'Stop' }
+
+ $MaximumSeverityType = 3
+ $ThreatsImpactType = 0
+ $ThreatsExploitStatusType = 1
+ $TagType = 7
+ $CNAType = 8
+ $RemediationsMitigationType = 1
+ $RemediationsWorkaroundType = 0
+
+ try {
+ $JsonMetrics = Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath 'CVSS-Metrics.json' @HT) @HT |
+ Out-String @HT | ConvertFrom-Json @HT
+
+ $JsonDescriptions = Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath 'CVSS-Descriptions.json'@HT) @HT |
+ Out-String @HT | ConvertFrom-Json @HT
+ }
+ catch {
+ Throw "Failed to get required json files content because $($_.Exception.Message)"
+ }
+
+ $css = @'
body {
background-color: white;
font-family: sans-serif;
@@ -108,9 +109,9 @@ Begin{
background-color: #C0C0C0;
}
'@
-}
-Process {
- $htmlDocumentTemplate = @'
+ }
+ Process {
+ $htmlDocumentTemplate = @'
@@ -167,27 +168,27 @@ Process {
'@
- $cveListHtmlObjects = @()
-
- $cveSectionHtml = ''
-
- $TotalCVE = $Vulnerability.Count
- $count = 0
- $Vulnerability | ForEach-Object -Process {
- $count++
- $v = $_
- $Progress = @{
- Activity = 'Getting Msrc Vulnerability Html Report'
- Status = "$($count)/$($TotalCVE) => $($v.CVE) "
- PercentComplete = ($count/$TotalCVE*100)
- ErrorAction = 'SilentlyContinue'
- }
- if (-not($ShowNoProgress)) { Write-Progress @Progress }
- Write-Verbose -Message "Dealing with $($_.CVE)"
+ $cveListHtmlObjects = @()
+
+ $cveSectionHtml = ''
+
+ $TotalCVE = $Vulnerability.Count
+ $count = 0
+ $Vulnerability | ForEach-Object -Process {
+ $count++
+ $v = $_
+ $Progress = @{
+ Activity = 'Getting Msrc Vulnerability Html Report'
+ Status = "$($count)/$($TotalCVE) => $($v.CVE) "
+ PercentComplete = ($count / $TotalCVE * 100)
+ ErrorAction = 'SilentlyContinue'
+ }
+ if (-not($ShowNoProgress)) { Write-Progress @Progress }
+ Write-Verbose -Message "Dealing with $($_.CVE)"
- #region CVE Summary Table
+ #region CVE Summary Table
- $cveSummaryTableHtml = @'
+ $cveSummaryTableHtml = @'
'@
- $MaximumSeverity = Switch (
- ($_.Threats | Where-Object {$_.Type -eq $MaximumSeverityType}).Description.Value | Select-Object -Unique
- ) {
- 'Critical' { 'Critical' ; break }
- 'Important' { 'Important' ; break }
- 'Moderate' { 'Moderate' ; break }
- 'Low' { 'Low' ; break }
- 'None' { 'None' ; break }
- default {
- Write-Warning "Could not determine the Maximum Severity from the Threats for $($v.CVE)"
- 'Unknown'
+ $MaximumSeverity = Switch (
+ ($_.Threats | Where-Object { $_.Type -eq $MaximumSeverityType }).Description.Value | Select-Object -Unique
+ ) {
+ 'Critical' { 'Critical' ; break }
+ 'Important' { 'Important' ; break }
+ 'Moderate' { 'Moderate' ; break }
+ 'Low' { 'Low' ; break }
+ 'None' { 'None' ; break }
+ default {
+ Write-Warning "Could not determine the Maximum Severity from the Threats for $($v.CVE)"
+ 'Unknown'
+ }
+ }
+ if (-not($MaximumSeverity)) {
+ $MaximumSeverity = 'Unknown'
}
- }
- if (-not($MaximumSeverity)) {
- $MaximumSeverity = 'Unknown'
- }
- if ($ImpactValues = ($v.Threats | Where-Object { $_.Type -eq $ThreatsImpactType }).Description.Value | Select-Object -Unique) {
- $impactColumn = $ImpactValues -join ',
'
- } else {
- Write-Warning -Message "Could not determine the Impact from the Threats for $($v.CVE)"
- $impactColumn = 'Unknown'
- }
+ if ($ImpactValues = ($v.Threats | Where-Object { $_.Type -eq $ThreatsImpactType }).Description.Value | Select-Object -Unique) {
+ $impactColumn = $ImpactValues -join ',
'
+ }
+ else {
+ Write-Warning -Message "Could not determine the Impact from the Threats for $($v.CVE)"
+ $impactColumn = 'Unknown'
+ }
- $vulnDescriptionColumnTemplate = @'
+ $vulnDescriptionColumnTemplate = @'
CVE Title: {0}
Weakness: {7}
@@ -252,32 +254,33 @@ Process {
'@
- $vulnDescriptionColumn = $vulnDescriptionColumnTemplate -f @(
- # $cveTitle
- $(
- if ($cveTitle = $v.Title.Value) {
- $cveTitle
- } else {
- Write-Warning -Message "Missing Title for $($v.CVE)"
- ($cveTitle = 'Unknown')
- }
- ),
- # $cvssScoreSet
- $(
- #Scores among the affected products can be different. So, just find the most severe.
- $highestBase = 0.0
- $highestCvssScore = $null
- ForEach($score in $v.CvssScoreSets) {
- if ($score.BaseScore -gt $highestBase) {
- $highestBase = $score.BaseScore
- $highestCvssScore = $score
+ $vulnDescriptionColumn = $vulnDescriptionColumnTemplate -f @(
+ # $cveTitle
+ $(
+ if ($cveTitle = $v.Title.Value) {
+ $cveTitle
+ }
+ else {
+ Write-Warning -Message "Missing Title for $($v.CVE)"
+ ($cveTitle = 'Unknown')
+ }
+ ),
+ # $cvssScoreSet
+ $(
+ #Scores among the affected products can be different. So, just find the most severe.
+ $highestBase = 0.0
+ $highestCvssScore = $null
+ ForEach ($score in $v.CvssScoreSets) {
+ if ($score.BaseScore -gt $highestBase) {
+ $highestBase = $score.BaseScore
+ $highestCvssScore = $score
+ }
}
- }
- if (($null -ne $highestCvssScore) -and ($null -ne $highestCvssScore.Vector) -and ($highestCvssScore.Vector.Split('/').Length -gt 1)) {
- $cvssArray = $highestCvssScore.Vector.Split('/')
+ if (($null -ne $highestCvssScore) -and ($null -ne $highestCvssScore.Vector) -and ($highestCvssScore.Vector.Split('/').Length -gt 1)) {
+ $cvssArray = $highestCvssScore.Vector.Split('/')
- $cvssScoreTemplate = @'
+ $cvssScoreTemplate = @'
{0}
@@ -296,131 +299,139 @@ Process {
{2}
'@
- $cvssScoreSet = $cvssScoreTemplate -f @(
- $rowTemplate = '| {1} | {3} |
'
+ $cvssScoreSet = $cvssScoreTemplate -f @(
+ $rowTemplate = '| {1} | {3} |
'
- $baseTags = 'AC', 'AV', 'A', 'C', 'I', 'PR', 'S', 'UI'
- $temporalTags = 'E', 'RC', 'RL'
- $baseRows = ''
- $temporalRows = ''
- for($i = 1; $i -lt $cvssArray.Length; $i++) {
+ $baseTags = 'AC', 'AV', 'A', 'C', 'I', 'PR', 'S', 'UI'
+ $temporalTags = 'E', 'RC', 'RL'
+ $baseRows = ''
+ $temporalRows = ''
+ for ($i = 1; $i -lt $cvssArray.Length; $i++) {
- $element = $cvssArray[$i]
- $split0 = $element.Split(':')[0]
+ $element = $cvssArray[$i]
+ $split0 = $element.Split(':')[0]
- $metric = $JsonMetrics.$split0
- $value = $JsonMetrics.$element
+ $metric = $JsonMetrics.$split0
+ $value = $JsonMetrics.$element
- $metricDescription = $JsonDescriptions.$split0
- $valueDescription = $JsonDescriptions.$element
+ $metricDescription = $JsonDescriptions.$split0
+ $valueDescription = $JsonDescriptions.$element
- $row = '| ' + $metric + ' | '
- $row += '' + $value + ' |
'
+ $row = '| ' + $metric + ' | '
+ $row += '' + $value + ' |
'
- if (($null -ne $metricDescription) -and ($null -ne $valueDescription)) {
- if ($baseTags.Contains($split0)) {
- $baseRows += $rowTemplate -f $metricDescription, $metric, $valueDescription, $value
- } else {
- if ($temporalTags.Contains($split0)) {
- $temporalRows += $rowTemplate -f $metricDescription, $metric, $valueDescription, $value
+ if (($null -ne $metricDescription) -and ($null -ne $valueDescription)) {
+ if ($baseTags.Contains($split0)) {
+ $baseRows += $rowTemplate -f $metricDescription, $metric, $valueDescription, $value
+ }
+ else {
+ if ($temporalTags.Contains($split0)) {
+ $temporalRows += $rowTemplate -f $metricDescription, $metric, $valueDescription, $value
+ }
}
}
}
- }
- $formattedScore = '{0} Highest BaseScore:{1}/TemporalScore:{2}' -f $cvssArray[0], $highestCvssScore.BaseScore, $highestCvssScore.TemporalScore
+ $formattedScore = '{0} Highest BaseScore:{1}/TemporalScore:{2}' -f $cvssArray[0], $highestCvssScore.BaseScore, $highestCvssScore.TemporalScore
- $formattedScore, $baseRows, $temporalRows
- )
- $cvssScoreSet
- } else {
- 'None' -join '
'
- }
- ),
- # $cveFaq
- $(
- if ($cveFaq = ($v.Notes | Where-Object {$_.Title -eq 'FAQ'}).Value) {
- $cveFaq -join '
'
- } else {
- 'None' -join '
'
- }
- ),
- # $cveMitigation
- $(
- if ($cveMitigation = $v.Remediations | Where-Object { $_.Type -eq $RemediationsMitigationType }) {
- $cveMitigation.Description.Value -join '
'
-
- } else {
- 'None' -join '
'
- }
- ),
- # $cveWorkaround
- $(
- if ( $cveWorkaround = ($v.Remediations | Where-Object {$_.Type -eq $RemediationsWorkaroundType }).Description.Value) {
- $cveWorkaround -join '
'
- } else {
- 'None' -join '
'
- }
- ),
- # $Revision
- $(
- $RevisionStrings = @()
- $v.RevisionHistory |
- ForEach-Object {
- $_ | Add-Member -MemberType NoteProperty -Name RevisionDate -Value ([datetime]$_.Date) -Force -PassThru
- } | Sort-Object RevisionDate |
- ForEach-Object {
- if ( $revision = $($_.Number, $_.RevisionDate.ToString('d'), $_.Description.Value) ) {
- $RevisionStrings += $($revision -join '    ')
+ $formattedScore, $baseRows, $temporalRows
+ )
+ $cvssScoreSet
}
- }
- if ( $RevisionStrings ) {
- $RevisionStrings -join '
'
- } else {
- 'Unknown' -join '
'
- }
- ),
- # Executive Summary
- $(
- if ($cveExecSummary = ($v.Notes | Where-Object {$_.Title -eq 'Description'}).Value) {
- $cveExecSummary -join '
'
- } else {
- 'None' -join '
'
- }
- )
- # CWE Weakness
- $(
- if ($cveWeakness = $(if ($v.CWE) { '{0} : {1}' -f "$($v.CWE.ID)","$($v.CWE.Value)"})) {
- $cveWeakness -join '
'
- } else {
- 'N/A' -join '
'
- }
- )
- # Customer Action Required
- $(
- if ($cveCustomerActionRequired = $v.Notes | Where-Object { $_.Title -eq "Customer Action Required" }) {
- $cveCustomerActionRequired.Value -join '
'
- } else {
- 'Yes' -join '
'
- }
- )
+ else {
+ 'None' -join '
'
+ }
+ ),
+ # $cveFaq
+ $(
+ if ($cveFaq = ($v.Notes | Where-Object { $_.Title -eq 'FAQ' }).Value) {
+ $cveFaq -join '
'
+ }
+ else {
+ 'None' -join '
'
+ }
+ ),
+ # $cveMitigation
+ $(
+ if ($cveMitigation = $v.Remediations | Where-Object { $_.Type -eq $RemediationsMitigationType }) {
+ $cveMitigation.Description.Value -join '
'
- )
+ }
+ else {
+ 'None' -join '
'
+ }
+ ),
+ # $cveWorkaround
+ $(
+ if ( $cveWorkaround = ($v.Remediations | Where-Object { $_.Type -eq $RemediationsWorkaroundType }).Description.Value) {
+ $cveWorkaround -join '
'
+ }
+ else {
+ 'None' -join '
'
+ }
+ ),
+ # $Revision
+ $(
+ $RevisionStrings = @()
+ $v.RevisionHistory |
+ ForEach-Object {
+ $_ | Add-Member -MemberType NoteProperty -Name RevisionDate -Value ([datetime]$_.Date) -Force -PassThru
+ } | Sort-Object RevisionDate |
+ ForEach-Object {
+ if ( $revision = $($_.Number, $_.RevisionDate.ToString('d'), $_.Description.Value) ) {
+ $RevisionStrings += $($revision -join '    ')
+ }
+ }
+ if ( $RevisionStrings ) {
+ $RevisionStrings -join '
'
+ }
+ else {
+ 'Unknown' -join '
'
+ }
+ ),
+ # Executive Summary
+ $(
+ if ($cveExecSummary = ($v.Notes | Where-Object { $_.Title -eq 'Description' }).Value) {
+ $cveExecSummary -join '
'
+ }
+ else {
+ 'None' -join '
'
+ }
+ )
+ # CWE Weakness
+ $(
+ if ($cveWeakness = $(if ($v.CWE) { '{0} : {1}' -f "$($v.CWE.ID)", "$($v.CWE.Value)" })) {
+ $cveWeakness -join '
'
+ }
+ else {
+ 'N/A' -join '
'
+ }
+ )
+ # Customer Action Required
+ $(
+ if ($cveCustomerActionRequired = $v.Notes | Where-Object { $_.Title -eq "Customer Action Required" }) {
+ $cveCustomerActionRequired.Value -join '
'
+ }
+ else {
+ 'Yes' -join '
'
+ }
+ )
+ )
- $cveSectionHtml += '{0} - {1}
(top)' -f $v.CVE, $cveTitle
+ $cveSectionHtml += '{0} - {1}
(top)' -f $v.CVE, $cveTitle
- #region CVE Summary List
- $cveListHtmlObjects += [PSCustomObject]@{
- Tag = $($v.Notes | Where-Object type -eq $TagType).Value
- CNA = $($v.Notes | Where-Object type -eq $CNAType).Value
- CVEID = $v.CVE
- CVETitle = $cveTitle
- }
- #endregion
+ #region CVE Summary List
+ $cveListHtmlObjects += [PSCustomObject]@{
+ Tag = $($v.Notes | Where-Object type -eq $TagType).Value
+ CNA = $($v.Notes | Where-Object type -eq $CNAType).Value
+ CVEID = $v.CVE
+ CVETitle = $cveTitle
+ }
+ #endregion
- $cveSectionHtml += $cveSummaryTableHtml -f @(
- @"
+ $cveSectionHtml += $cveSummaryTableHtml -f @(
+ @"
$($_.CVE)
MITRE
@@ -428,14 +439,24 @@ Process {
NVD
Issuing CNA: $($($v.Notes | Where-Object type -eq $CNAType).Value)
"@,
- $vulnDescriptionColumn,
- $MaximumSeverity,
- $impactColumn
- )
- #endregion
-
- #region Exploitability Index Table
- $exploitabilityIndexTableHtml = @'
+ $vulnDescriptionColumn,
+ $MaximumSeverity,
+ $impactColumn
+ )
+ #endregion
+
+ #region Exploitability Index Table
+
+ #Reset exploitability state for this CVE
+ $ExploitStatus = [PSCustomObject]@{
+
+ PubliclyDisclosed = $null
+ Exploited = $null
+ LatestSoftwareRelease = $null
+ OlderSoftwareRelease = $null
+ DenialOfService = $null
+ }
+ $exploitabilityIndexTableHtml = @'
Exploitability Index
The following table provides an exploitability assessment for this vulnerability at the time of original publication.
@@ -456,43 +477,47 @@ Process {
'@
- if ($ExploitStatusThreat = ($v.Threats | Where-Object { $_.Type -eq $ThreatsExploitStatusType } | Select-Object -Last 1).Description.Value) {
- $ExploitStatus = Get-MsrcThreatExploitStatus -ExploitStatusString $ExploitStatusThreat
- } else {
- Write-Warning -Message "Missing ExploitStatus for $($v.CVE)"
- }
-
- $cveSectionHtml += $exploitabilityIndexTableHtml -f @(
- # $LatestSoftwareRelease
- $(
- if ($ExploitStatus.LatestSoftwareRelease) {
- $ExploitStatus.LatestSoftwareRelease
- } else {
- 'Not Found'
- }
- ),
- # $publicly disclosed
- $(
- if ($ExploitStatus.PubliclyDisclosed) {
- $ExploitStatus.PubliclyDisclosed
- } else {
- 'Not Found'
- }
- ),
- # $Exploited
- $(
- if ($ExploitStatus.Exploited) {
- $ExploitStatus.Exploited
- } else {
- 'Not Found'
- }
+ if ($ExploitStatusThreat = ($v.Threats | Where-Object { $_.Type -eq $ThreatsExploitStatusType } | Select-Object -Last 1).Description.Value) {
+ $ExploitStatus = Get-MsrcThreatExploitStatus -ExploitStatusString $ExploitStatusThreat
+ }
+ else {
+ Write-Warning -Message "Missing ExploitStatus for $($v.CVE)"
+ }
+
+ $cveSectionHtml += $exploitabilityIndexTableHtml -f @(
+ # $LatestSoftwareRelease
+ $(
+ if ($ExploitStatus.LatestSoftwareRelease) {
+ $ExploitStatus.LatestSoftwareRelease
+ }
+ else {
+ 'Not Found'
+ }
+ ),
+ # $publicly disclosed
+ $(
+ if ($ExploitStatus.PubliclyDisclosed) {
+ $ExploitStatus.PubliclyDisclosed
+ }
+ else {
+ 'Not Found'
+ }
+ ),
+ # $Exploited
+ $(
+ if ($ExploitStatus.Exploited) {
+ $ExploitStatus.Exploited
+ }
+ else {
+ 'Not Found'
+ }
+ )
)
- )
- #endregion
+ #endregion
- #region Affected Software Table
+ #region Affected Software Table
- $affectedSoftwareTableTemplate = @'
+ $affectedSoftwareTableTemplate = @'
@@ -515,7 +540,7 @@ Process {
'@
- $affectedSoftwareRowTemplate = @'
+ $affectedSoftwareRowTemplate = @'
| {0} |
{1} |
@@ -529,117 +554,127 @@ Process {
'@
- $cveSectionHtml += @'
+ $cveSectionHtml += @'
Affected Software
The following tables list the affected software details for the vulnerability.
'@
- $affectedSoftware = Get-MsrcCvrfAffectedSoftware -Vulnerability $v -ProductTree $ProductTree
- $affectedSoftwareTableHtml = ''
+ $affectedSoftware = Get-MsrcCvrfAffectedSoftware -Vulnerability $v -ProductTree $ProductTree
+ $affectedSoftwareTableHtml = ''
- $affectedSoftware.FullProductName | Sort-Object -Unique | ForEach-Object {
+ $affectedSoftware.FullProductName | Sort-Object -Unique | ForEach-Object {
- $PN = $_
+ $PN = $_
- $affectedSoftware | Where-Object {$_.FullProductName -eq $PN} | ForEach-Object {
+ $affectedSoftware | Where-Object { $_.FullProductName -eq $PN } | ForEach-Object {
- $affectedSoftwareTableHtml += $affectedSoftwareRowTemplate -f @(
- $PN,
- $(
-if ($PN -eq 'Microsoft Edge (Chromium-based)') {
- @(
- '{1} ({2})' -f 'https://learn.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel',
- "$($_.KBArticle.ID)", "$($_.KBArticle.SubType)"
- )
-} else {
- $_.KBArticle | Get-KBDownloadUrl
-}
- ),
- $(
- if (-not($_.Severity)) {
- 'Unknown'
- } else {
- $($_.Severity | Select-Object -Unique) -join '
'
- }
- ),
- $(
- if (-not($_.Impact)) {
- 'Unknown'
- } else {
- $($_.Impact | Select-Object -Unique) -join '
'
- }
- ),
- $(
- if (-not($_.Supercedence)) {
- 'None'
- } else {
- $($_.Supercedence | Select-Object -Unique) -join '
'
- }
- ),
- $(
-
- 'Base: {0}
Temporal: {1}
Vector: {2}
' -f (
- $(
- if(-not($_.CvssScoreSet.base)) {
- 'N/A'
- } else{
- $_.CvssScoreSet.base
- }
- )
+ $affectedSoftwareTableHtml += $affectedSoftwareRowTemplate -f @(
+ $PN,
+ $(
+ if ($PN -eq 'Microsoft Edge (Chromium-based)') {
+ @(
+ '{1} ({2})' -f 'https://learn.microsoft.com/en-us/deployedge/microsoft-edge-relnote-stable-channel',
+ "$($_.KBArticle.ID)", "$($_.KBArticle.SubType)"
+ )
+ }
+ else {
+ $_.KBArticle | Get-KBDownloadUrl
+ }
),
- (
- $(
- if(-not($_.CvssScoreSet.temporal)) {
- 'N/A'
- } else {
- $_.CvssScoreSet.temporal
- }
- )
+ $(
+ if (-not($_.Severity)) {
+ 'Unknown'
+ }
+ else {
+ $($_.Severity | Select-Object -Unique) -join '
'
+ }
),
- (
- $(
- if(-not($_.CvssScoreSet.vector)) {
- 'N/A'
- } else {
- $_.CvssScoreSet.vector
- }
+ $(
+ if (-not($_.Impact)) {
+ 'Unknown'
+ }
+ else {
+ $($_.Impact | Select-Object -Unique) -join '
'
+ }
+ ),
+ $(
+ if (-not($_.Supercedence)) {
+ 'None'
+ }
+ else {
+ $($_.Supercedence | Select-Object -Unique) -join '
'
+ }
+ ),
+ $(
+
+ 'Base: {0}
Temporal: {1}
Vector: {2}
' -f (
+ $(
+ if (-not($_.CvssScoreSet.base)) {
+ 'N/A'
+ }
+ else {
+ $_.CvssScoreSet.base
+ }
+ )
+ ),
+ (
+ $(
+ if (-not($_.CvssScoreSet.temporal)) {
+ 'N/A'
+ }
+ else {
+ $_.CvssScoreSet.temporal
+ }
+ )
+ ),
+ (
+ $(
+ if (-not($_.CvssScoreSet.vector)) {
+ 'N/A'
+ }
+ else {
+ $_.CvssScoreSet.vector
+ }
+ )
)
+ ),
+ $(
+ if (-not($_.FixedBuild)) {
+ 'Unknown'
+ }
+ else {
+ $($_.FixedBuild | Select-Object -Unique) -join '
'
+ }
+ ),
+ $(
+ if (-not($_.RestartRequired)) {
+ 'Unknown'
+ }
+ else {
+ $($_.RestartRequired | Select-Object -Unique) -join '
'
+ }
+ ),
+ $(
+ if (-not($_.'Known Issue')) {
+ 'None'
+ }
+ else {
+ $_.'Known Issue' | Get-KBDownloadUrl
+ }
)
- ),
- $(
- if (-not($_.FixedBuild)) {
- 'Unknown'
- } else {
- $($_.FixedBuild | Select-Object -Unique) -join '
'
- }
- ),
- $(
- if (-not($_.RestartRequired)) {
- 'Unknown'
- } else {
- $($_.RestartRequired | Select-Object -Unique) -join '
'
- }
- ),
- $(
- if (-not($_.'Known Issue')) {
- 'None'
- } else {
- $_.'Known Issue' | Get-KBDownloadUrl
- }
)
- )
+ }
}
- }
- $cveSectionHtml += $affectedSoftwareTableTemplate -f @(
- $v.CVE,
- $affectedSoftwareTableHtml
- )
- #endregion
+ $cveSectionHtml += $affectedSoftwareTableTemplate -f @(
+ $v.CVE,
+ $affectedSoftwareTableHtml
+ )
+ #endregion
- #region Acknowledgments Table
- $acknowledgmentsTableTemplate = @'
+ #region Acknowledgments Table
+ $acknowledgmentsTableTemplate = @'
Acknowledgements
@@ -655,47 +690,48 @@ if ($PN -eq 'Microsoft Edge (Chromium-based)') {
'@
- if ($v.Acknowledgments) {
- $ackVal = ''
- $v.Acknowledgments | ForEach-Object {
+ if ($v.Acknowledgments) {
+ $ackVal = ''
+ $v.Acknowledgments | ForEach-Object {
- if ($_.Name.Value) {
- $ackVal += $_.Name.Value
- $ackVal += '
'
- }
- if ($_.URL) {
- $ackVal += $_.URL
- $ackVal += '
'
+ if ($_.Name.Value) {
+ $ackVal += $_.Name.Value
+ $ackVal += '
'
+ }
+ if ($_.URL) {
+ $ackVal += $_.URL
+ $ackVal += '
'
+ }
+ $ackVal += '
'
}
- $ackVal += '
'
}
- } else {
- Write-Warning -Message "No Acknowledgments for $($v.CVE)"
- $ackVal = 'None'
+ else {
+ Write-Warning -Message "No Acknowledgments for $($v.CVE)"
+ $ackVal = 'None'
+ }
+
+ $cveSectionHtml += $acknowledgmentsTableTemplate -f @(
+ $v.CVE,
+ $ackVal
+ )
+ } -End {
+ Write-Progress -Activity 'Getting Msrc Vulnerability Html Report' -Completed
}
+ #endregion
- $cveSectionHtml += $acknowledgmentsTableTemplate -f @(
- $v.CVE,
- $ackVal
- )
- } -End {
- Write-Progress -Activity 'Getting Msrc Vulnerability Html Report' -Completed
- }
- #endregion
-
- (
- $htmlDocumentTemplate -f @(
- #sort the objects and put them into the table of contents format before injecting into the document template:
- ($( $cveListHtmlObjects | Sort-Object -Property Tag |
- ForEach-Object {
- '| {3} | {0} | {1} | {2} |
' -f $_.Tag,$_.CVEID,$_.CVETitle,$_.CNA
- }) -join "`n"),
- $cveSectionHtml,
- "$($MyInvocation.MyCommand.Version.ToString())",
- $css,$global:msrcApiUrl
+ (
+ $htmlDocumentTemplate -f @(
+ #sort the objects and put them into the table of contents format before injecting into the document template:
+ ($( $cveListHtmlObjects | Sort-Object -Property Tag |
+ ForEach-Object {
+ '| {3} | {0} | {1} | {2} |
' -f $_.Tag, $_.CVEID, $_.CVETitle, $_.CNA
+ }) -join "`n"),
+ $cveSectionHtml,
+ "$($MyInvocation.MyCommand.Version.ToString())",
+ $css, $global:msrcApiUrl
+ )
)
- )
-}
-End {}
+ }
+ End {}
}
diff --git a/src/MsrcSecurityUpdates/Public/Set-MSRCApiKey.ps1 b/src/MsrcSecurityUpdates/Public/Set-MSRCApiKey.ps1
index e2c132a..3b629be 100644
--- a/src/MsrcSecurityUpdates/Public/Set-MSRCApiKey.ps1
+++ b/src/MsrcSecurityUpdates/Public/Set-MSRCApiKey.ps1
@@ -45,9 +45,8 @@ Process {
Write-Verbose -Message "Successfully defined a msrcProxyCredential global variable that points to $($global:msrcProxy)"
}
- if ($global:MSRCAdalAccessToken)
- {
- Remove-Variable -Name MSRCAdalAccessToken -Scope Global
+ if ($script:MSRCMsalAccessToken){
+ Remove-Variable -Name MSRCMsalAccessToken -Scope Script
}
}
}
diff --git a/src/MsrcSecurityUpdates/Public/Set-MsrcAdalAccessToken.ps1 b/src/MsrcSecurityUpdates/Public/Set-MsrcAdalAccessToken.ps1
deleted file mode 100644
index 9ec6c90..0000000
--- a/src/MsrcSecurityUpdates/Public/Set-MsrcAdalAccessToken.ps1
+++ /dev/null
@@ -1,45 +0,0 @@
-Function Set-MSRCAdalAccessToken {
-[CmdletBinding(SupportsShouldProcess)]
-Param()
-Begin {}
-Process {
- if ([AppDomain]::CurrentDomain.SetupInformation.TargetFrameworkName -like "*v5.*") {
- throw ".Net Core v5.x is not currently supported"
- }
-
- if ($PSCmdlet.ShouldProcess('Set the MSRCApiKey using MSRCAdalAccessToken')) {
- Add-Type -Path "$PSScriptRoot/../Microsoft.IdentityModel.Clients.ActiveDirectory.dll" -ErrorAction SilentlyContinue -WarningAction SilentlyContinue
-
- $authority = 'https://login.windows.net/microsoft.onmicrosoft.com/'
-
- $authContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext($authority)
-
- $rUri = New-Object System.Uri -ArgumentList 'https://msrc-api-powershell'
-
- $promptBehavior = [Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Auto
-
-
- $ResourceId = 'https://msrc-api-prod.azurewebsites.net'
-
- $ClientId = 'c7fe3b9e-4d97-462d-ae1b-c16e679be355'
-
- $global:MSRCAdalAccessToken = $null
-
- if ($null -ne $authContext.AcquireToken) {
- $global:MSRCAdalAccessToken = $authContext.AcquireToken($ResourceId, $ClientId, $rUri,$promptBehavior)
- } elseif ($null -ne $authContext.AcquireTokenAsync) {
- $platformParams = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters($promptBehavior)
- $task = $authContext.AcquireTokenAsync($ResourceId, $ClientId, $rUri,$platformParams)
- $task.Wait()
- $global:MSRCAdalAccessToken = $task.Result
- }
-
- if ($null -ne $global:MSRCAdalAccessToken) {
- Write-Verbose -Message "Successfully set your Access Token required by cmdlets of this module. Calls to the MSRC APIs will now use your access token."
- } else {
- throw "Failed Acquiring Access Token!"
- }
- }
-}
-End {}
-}
diff --git a/src/MsrcSecurityUpdates/Public/Set-MsrcMsalAccessToken.ps1 b/src/MsrcSecurityUpdates/Public/Set-MsrcMsalAccessToken.ps1
new file mode 100644
index 0000000..dbf142c
--- /dev/null
+++ b/src/MsrcSecurityUpdates/Public/Set-MsrcMsalAccessToken.ps1
@@ -0,0 +1,55 @@
+Function Set-MSRCMsalAccessToken {
+[CmdletBinding(SupportsShouldProcess)]
+Param(
+ [Parameter()]
+ [Alias('ClientId')]
+ [string]$ID,
+
+ [Parameter()]
+ [string]$TenantId = 'microsoft.onmicrosoft.com',
+
+ [Parameter()]
+ [string]$RedirectUri = 'http://localhost:50000'
+)
+Begin {}
+Process {
+ if ($PSCmdlet.ShouldProcess('Set the MSRCApiKey using MSRCMsalAccessToken')) {
+ # Check if MSAL.PS module is available
+ if (-not (Get-Module -ListAvailable -Name MSAL.PS)) {
+ throw "MSAL.PS module is required. Please install it using: Install-Module -Name MSAL.PS"
+ }
+
+ Import-Module MSAL.PS -ErrorAction Stop
+
+ # Clear any existing cached token
+ $script:MSRCMsalAccessToken = $null
+
+ try {
+ # Use MSAL.PS to acquire token interactively
+ $msalParams = @{
+ ClientId = $ID
+ TenantId = $TenantId
+ Scopes = @("$ID/.default")
+ RedirectUri = $RedirectUri
+ Interactive = $true
+ }
+
+ $tokenResult = Get-MsalToken @msalParams
+
+ if ($tokenResult -and $tokenResult.AccessToken) {
+ # Store only the access token string, not the full object
+ $script:MSRCMsalAccessToken = @{
+ AccessToken = $tokenResult.AccessToken
+ }
+ Write-Verbose -Message "Successfully set your Access Token required by cmdlets of this module. Calls to the MSRC APIs will now use your access token."
+ } else {
+ throw "Failed Acquiring Access Token!"
+ }
+ }
+ catch {
+ throw
+ }
+ }
+}
+End {}
+}
diff --git a/src/README.md b/src/README.md
index fcd8ba3..6ee694b 100644
--- a/src/README.md
+++ b/src/README.md
@@ -44,16 +44,16 @@ Get-Command -Module MsrcSecurityUpdates
CommandType Name Version Source
----------- ---- ------- ------
-Function Get-KBDownloadUrl 1.8.7 MsrcSecurityUpdates
-Function Get-MsrcCvrfAffectedSoftware 1.8.7 MsrcSecurityUpdates
-Function Get-MsrcCvrfCVESummary 1.8.7 MsrcSecurityUpdates
-Function Get-MsrcCvrfDocument 1.8.7 MsrcSecurityUpdates
-Function Get-MsrcCvrfExploitabilityIndex 1.8.7 MsrcSecurityUpdates
-Function Get-MsrcSecurityBulletinHtml 1.8.7 MsrcSecurityUpdates
-Function Get-MsrcSecurityUpdate 1.8.7 MsrcSecurityUpdates
-Function Get-MsrcVulnerabilityReportHtml 1.8.7 MsrcSecurityUpdates
-Function Set-MSRCAdalAccessToken 1.8.7 MsrcSecurityUpdates
-Function Set-MSRCApiKey 1.8.7 MsrcSecurityUpdates
+Function Get-KBDownloadUrl 2.0.0 MsrcSecurityUpdates
+Function Get-MsrcCvrfAffectedSoftware 2.0.0 MsrcSecurityUpdates
+Function Get-MsrcCvrfCVESummary 2.0.0 MsrcSecurityUpdates
+Function Get-MsrcCvrfDocument 2.0.0 MsrcSecurityUpdates
+Function Get-MsrcCvrfExploitabilityIndex 2.0.0 MsrcSecurityUpdates
+Function Get-MsrcSecurityBulletinHtml 2.0.0 MsrcSecurityUpdates
+Function Get-MsrcSecurityUpdate 2.0.0 MsrcSecurityUpdates
+Function Get-MsrcVulnerabilityReportHtml 2.0.1 MsrcSecurityUpdates
+Function Set-MSRCMsalAccessToken 2.0.0 MsrcSecurityUpdates
+Function Set-MSRCApiKey 2.0.0 MsrcSecurityUpdates
```
## Generating a HTML document of Monthly Updates