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