initial commit
This commit is contained in:
commit
d1cfc3cc70
8 changed files with 383 additions and 0 deletions
24
LICENSE
Normal file
24
LICENSE
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
Copyright (C) 2017 Marius Schellenberger
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* The names of the authors and/or contributors may not be used to
|
||||||
|
endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL ston1th BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
9
README.md
Normal file
9
README.md
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# msrcgo - a simple API client and JSON decoder for the Microsoft Security Bulletins
|
||||||
|
|
||||||
|
For examples see: `cmd/main.go`
|
||||||
|
|
||||||
|
Or simply call it: `go run cmd/main.go <your api key>`
|
||||||
|
|
||||||
|
To get your API-Key, navigate here and log in with your microsoft account:
|
||||||
|
|
||||||
|
https://portal.msrc.microsoft.com/en-us/developer
|
||||||
23
cmd/curl_examples.sh
Executable file
23
cmd/curl_examples.sh
Executable file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
key="<your api key>"
|
||||||
|
url="https://api.msrc.microsoft.com"
|
||||||
|
apiversion="?api-version=2016-08-01"
|
||||||
|
updates="/Updates"
|
||||||
|
|
||||||
|
#all updates
|
||||||
|
curl -H "api-key: ${key}" ${url}${updates}${apiversion}
|
||||||
|
|
||||||
|
#updates by id
|
||||||
|
id=2016-Sep
|
||||||
|
#id=CVE-1234-5678
|
||||||
|
#id=2016
|
||||||
|
curl -H "api-key: ${key}" "${url}${updates}('${id}')${apiversion}"
|
||||||
|
|
||||||
|
#by id
|
||||||
|
cvrfid=2017-Jan
|
||||||
|
#cvrfid=CVE-1234-5678
|
||||||
|
#cvrfid=2016
|
||||||
|
cvrf="/cvrf/"
|
||||||
|
|
||||||
|
curl -H "Accept: application/json" -H "api-key: ${key}" "${url}${cvrf}${cvrfid}${apiversion}"
|
||||||
19
cmd/main.go
Normal file
19
cmd/main.go
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.giftfish.de/ston1th/msrcgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
apikey := os.Args[1]
|
||||||
|
c := msrcgo.NewClient(apikey)
|
||||||
|
all, err := c.GetAllUpdates()
|
||||||
|
fmt.Println(all, err)
|
||||||
|
jan, err := c.GetUpdateID("2017-Jan")
|
||||||
|
fmt.Println(jan, err)
|
||||||
|
cvrf, err := c.GetCVRF("2017-Jan")
|
||||||
|
fmt.Println(cvrf, err)
|
||||||
|
}
|
||||||
3
doc.go
Normal file
3
doc.go
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
// Package msrcgo provides a simple API client and JSON decoder for the Microsoft Security Bulletins
|
||||||
|
|
||||||
|
package msrcgo
|
||||||
92
http.go
Normal file
92
http.go
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
package msrcgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
APIVersion = "?api-version=2016-08-01"
|
||||||
|
UpdatesURI = "/Updates"
|
||||||
|
CvrfURI = "/cvrf/"
|
||||||
|
URL = "https://api.msrc.microsoft.com"
|
||||||
|
|
||||||
|
APIKeyHeader = "api-key"
|
||||||
|
AcceptHeader = "Accept"
|
||||||
|
AcceptValue = "application/json"
|
||||||
|
GET = "GET"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client represents the API client
|
||||||
|
type Client struct {
|
||||||
|
key string
|
||||||
|
allUpdates *http.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a new Client instance
|
||||||
|
func NewClient(key string) *Client {
|
||||||
|
all, _ := http.NewRequest(GET, URL+UpdatesURI+APIVersion, nil)
|
||||||
|
all.Header.Set(AcceptHeader, AcceptValue)
|
||||||
|
all.Header.Set(APIKeyHeader, key)
|
||||||
|
return &Client{key, all}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllUpdates returns the Update struct (a list of all bulletins)
|
||||||
|
func (c *Client) GetAllUpdates() (*Update, error) {
|
||||||
|
return c.getUpdate(c.allUpdates)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUpdateID returns a Update struct of the given id
|
||||||
|
// Example id: "2016-Sep"
|
||||||
|
func (c *Client) GetUpdateID(id string) (*Update, error) {
|
||||||
|
req, _ := http.NewRequest(GET, URL+UpdatesURI+"('"+id+"')"+APIVersion, nil)
|
||||||
|
req.Header.Set(AcceptHeader, AcceptValue)
|
||||||
|
req.Header.Set(APIKeyHeader, c.key)
|
||||||
|
return c.getUpdate(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCVRF returns the CVRF struct of the given id
|
||||||
|
// Example id: "2016-Sep"
|
||||||
|
func (c *Client) GetCVRF(id string) (*CVRF, error) {
|
||||||
|
req, _ := http.NewRequest(GET, URL+CvrfURI+id+APIVersion, nil)
|
||||||
|
req.Header.Set(AcceptHeader, AcceptValue)
|
||||||
|
req.Header.Set(APIKeyHeader, c.key)
|
||||||
|
return c.getCVRF(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) request(req *http.Request) (body io.ReadCloser, err error) {
|
||||||
|
resp, err := (&http.Client{}).Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
b, e := ioutil.ReadAll(resp.Body)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if e == nil {
|
||||||
|
err = errors.New(string(b))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
body = resp.Body
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getUpdate(req *http.Request) (u *Update, err error) {
|
||||||
|
body, err := c.request(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer body.Close()
|
||||||
|
return DecodeUpdate(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getCVRF(req *http.Request) (u *CVRF, err error) {
|
||||||
|
body, err := c.request(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer body.Close()
|
||||||
|
return DecodeCVRF(body)
|
||||||
|
}
|
||||||
127
json.go
Normal file
127
json.go
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
package msrcgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecodeUpdate takes an io.Reader and returns the decoded Update struct
|
||||||
|
func DecodeUpdate(r io.Reader) (u *Update, err error) {
|
||||||
|
u = new(Update)
|
||||||
|
err = json.NewDecoder(r).Decode(u)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update represents the MSRC Updates
|
||||||
|
type Update struct {
|
||||||
|
OdataContext string `json:"@odata.context"`
|
||||||
|
Value []struct {
|
||||||
|
ID string `json:"ID"`
|
||||||
|
Alias string `json:"Alias"`
|
||||||
|
DocumentTitle string `json:"DocumentTitle"`
|
||||||
|
Severity interface{} `json:"Severity"`
|
||||||
|
InitialReleaseDate time.Time `json:"InitialReleaseDate"`
|
||||||
|
CurrentReleaseDate time.Time `json:"CurrentReleaseDate"`
|
||||||
|
CvrfURL string `json:"CvrfUrl"`
|
||||||
|
} `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeCVRF takes an io.Reader and returns the decoded CVRF struct
|
||||||
|
func DecodeCVRF(r io.Reader) (cvrf *CVRF, err error) {
|
||||||
|
cvrf = new(CVRF)
|
||||||
|
err = json.NewDecoder(r).Decode(cvrf)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CVRF represents a single Security Bulletin
|
||||||
|
type CVRF struct {
|
||||||
|
DocumentTitle struct {
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"DocumentTitle"`
|
||||||
|
DocumentType struct {
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"DocumentType"`
|
||||||
|
DocumentPublisher struct {
|
||||||
|
ContactDetails struct {
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"ContactDetails"`
|
||||||
|
IssuingAuthority struct {
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"IssuingAuthority"`
|
||||||
|
Type int `json:"Type"`
|
||||||
|
} `json:"DocumentPublisher"`
|
||||||
|
DocumentTracking struct {
|
||||||
|
Identification struct {
|
||||||
|
ID struct {
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"ID"`
|
||||||
|
Alias struct {
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"Alias"`
|
||||||
|
} `json:"Identification"`
|
||||||
|
Status int `json:"Status"`
|
||||||
|
Version string `json:"Version"`
|
||||||
|
RevisionHistory []struct {
|
||||||
|
Number string `json:"Number"`
|
||||||
|
Date string `json:"Date"`
|
||||||
|
Description struct {
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"Description"`
|
||||||
|
} `json:"RevisionHistory"`
|
||||||
|
InitialReleaseDate string `json:"InitialReleaseDate"`
|
||||||
|
CurrentReleaseDate string `json:"CurrentReleaseDate"`
|
||||||
|
} `json:"DocumentTracking"`
|
||||||
|
DocumentNotes []struct {
|
||||||
|
Title string `json:"Title"`
|
||||||
|
Audience string `json:"Audience"`
|
||||||
|
Type int `json:"Type"`
|
||||||
|
Ordinal string `json:"Ordinal"`
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"DocumentNotes"`
|
||||||
|
ProductTree struct {
|
||||||
|
FullProductName []struct {
|
||||||
|
ProductID string `json:"ProductID"`
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"FullProductName"`
|
||||||
|
} `json:"ProductTree"`
|
||||||
|
Vulnerability []struct {
|
||||||
|
Title struct {
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"Title"`
|
||||||
|
Notes []struct {
|
||||||
|
Type int `json:"Type"`
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"Notes"`
|
||||||
|
DiscoveryDateSpecified bool `json:"DiscoveryDateSpecified"`
|
||||||
|
ReleaseDateSpecified bool `json:"ReleaseDateSpecified"`
|
||||||
|
CVE string `json:"CVE"`
|
||||||
|
ProductStatuses []struct {
|
||||||
|
ProductID []string `json:"ProductID"`
|
||||||
|
Type int `json:"Type"`
|
||||||
|
} `json:"ProductStatuses"`
|
||||||
|
CVSSScoreSets []struct {
|
||||||
|
BaseScore float64 `json:"BaseScore"`
|
||||||
|
TemporalScore float64 `json:"TemporalScore"`
|
||||||
|
Vector string `json:"Vector"`
|
||||||
|
ProductID []string `json:"ProductID"`
|
||||||
|
} `json:"CVSSScoreSets"`
|
||||||
|
Remediations []struct {
|
||||||
|
Description struct {
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"Description"`
|
||||||
|
URL string `json:"URL"`
|
||||||
|
ProductID []string `json:"ProductID"`
|
||||||
|
Type int `json:"Type"`
|
||||||
|
DateSpecified bool `json:"DateSpecified"`
|
||||||
|
AffectedFiles []interface{} `json:"AffectedFiles"`
|
||||||
|
} `json:"Remediations"`
|
||||||
|
Acknowledgments []struct {
|
||||||
|
Name []struct {
|
||||||
|
Value string `json:"Value"`
|
||||||
|
} `json:"Name"`
|
||||||
|
URL []string `json:"URL"`
|
||||||
|
} `json:"Acknowledgments"`
|
||||||
|
Ordinal string `json:"Ordinal"`
|
||||||
|
} `json:"Vulnerability"`
|
||||||
|
}
|
||||||
86
xml.go
Normal file
86
xml.go
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
package msrcgo
|
||||||
|
|
||||||
|
//NOTE XML is not yet supported
|
||||||
|
|
||||||
|
type xmlcvrf struct {
|
||||||
|
DocumentTitle string `xml:"DocumentTitle"`
|
||||||
|
DocumentType string `xml:"DocumentType"`
|
||||||
|
DocumentPublisher struct {
|
||||||
|
Type string `xml:"Type,attr"`
|
||||||
|
ContactDetails string `xml:"ContactDetails"`
|
||||||
|
IssuingAuthority string `xml:"IssuingAuthority"`
|
||||||
|
} `xml:"DocumentPublisher"`
|
||||||
|
DocumentTracking struct {
|
||||||
|
Identification struct {
|
||||||
|
ID string `xml:"ID"`
|
||||||
|
Alias string `xml"Alias"`
|
||||||
|
} `xml:"Identification"`
|
||||||
|
Status string `xml:"Status"`
|
||||||
|
Version string `xml:"Version"`
|
||||||
|
RevisionHistory struct {
|
||||||
|
Revision []struct {
|
||||||
|
Number int `xml:"Number"`
|
||||||
|
Date string `xml:"Date"`
|
||||||
|
Description string `xml:"Description"`
|
||||||
|
} `xml:"Revision"`
|
||||||
|
} `xml:"RevisionHistory"`
|
||||||
|
InitialReleaseDate string `xml:"InitialReleaseDate"`
|
||||||
|
CurrentReleaseDate string `xml:"CurrentReleaseDate"`
|
||||||
|
} `xml:"DocumentTracking"`
|
||||||
|
DocumentNotes struct {
|
||||||
|
Note []struct {
|
||||||
|
Title string `xml:"Title,attr"`
|
||||||
|
Audience string `xml:"Audience,attr"`
|
||||||
|
Type string `xml:"Type,attr"`
|
||||||
|
Ordinal string `xml:"Ordinal,attr"`
|
||||||
|
Note string `xml:",chardata"`
|
||||||
|
} `xml:"Note"`
|
||||||
|
} `xml:"DocumentNotes"`
|
||||||
|
ProductTree struct {
|
||||||
|
FullProductName []struct {
|
||||||
|
ProductID string `xml:"ProductID,attr"`
|
||||||
|
FullProductName string `xml:",chardata"`
|
||||||
|
} `xml:"FullProductName"`
|
||||||
|
} `xml:"ProductTree"`
|
||||||
|
Vulnerabilities []struct {
|
||||||
|
Ordinal string `xml:"Ordinal,attr"`
|
||||||
|
Title string `xml:"Title"`
|
||||||
|
Notes struct {
|
||||||
|
Note []struct {
|
||||||
|
Type string `xml:"Type,attr"`
|
||||||
|
Note string `xml:",chardata"`
|
||||||
|
} `xml:"Note"`
|
||||||
|
} `xml:"Notes"`
|
||||||
|
CVE string `xml:"CVE"`
|
||||||
|
ProductStatuses struct {
|
||||||
|
Status []struct {
|
||||||
|
Type string `xml:"Type,attr"`
|
||||||
|
ProductID string `xml:"ProductID"`
|
||||||
|
} `xml:"Status"`
|
||||||
|
} `xml:"ProductStatuses"`
|
||||||
|
CVSSScoreSets struct {
|
||||||
|
ScoreSet []struct {
|
||||||
|
BaseScore float32 `xml:"BaseScore"`
|
||||||
|
TemporalScore float32 `xml:"TemporalScore"`
|
||||||
|
Vector string `xml:"Vector"`
|
||||||
|
ProductID string `xml:"ProductID"`
|
||||||
|
} `xml:"ScoreSet"`
|
||||||
|
} `xml:"CVSSScoreSets"`
|
||||||
|
Remediations struct {
|
||||||
|
Remediation []struct {
|
||||||
|
Type string `xml:"Type,attr"`
|
||||||
|
Description string `xml:"Description"`
|
||||||
|
URL string `xml:"URL"`
|
||||||
|
ProductID string `xml:"ProductID"`
|
||||||
|
//NOTE not yet used
|
||||||
|
AffectedFields interface{} `xml:"AffectedFields"`
|
||||||
|
} `xml:"Remediation"`
|
||||||
|
} `xml:"Remediations"`
|
||||||
|
Acknowledgments struct {
|
||||||
|
Acknowledgment []struct {
|
||||||
|
Name string `xml:"Name"`
|
||||||
|
URL string `xml:"URL"`
|
||||||
|
} `json:Acknowledgment"`
|
||||||
|
} `xml:"Acknowledgments"`
|
||||||
|
} `xml:"Vulnerability"`
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue