Skip to content

Commit

Permalink
Merge pull request #75 from zema1/release-new
Browse files Browse the repository at this point in the history
Release new
  • Loading branch information
zema1 committed Mar 28, 2024
2 parents 3f74951 + a052a60 commit 9839f97
Show file tree
Hide file tree
Showing 12 changed files with 86 additions and 66 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## v1.7.0 (2024.03.28)

### 新增

- 新增 `CISA-KEV` 数据源抓取,更新后默认启用,感谢 [@Center-Sun](https://github.com/Center-Sun) 贡献
- 新增 `蓝信``pushplus` 推送支持,感谢 [@fengwenhua](https://github.com/fengwenhua) 贡献

## v1.6.0 (2024.03.08)

### 新增
Expand Down
45 changes: 23 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
| 奇安信威胁情报中心 | https://ti.qianxin.com/ | 等级为高危严重**并且**包含 `奇安信CERT验证` `POC公开` `技术细节公布`标签之一 |
| 微步在线研究响应中心(公众号) | https://x.threatbook.com/v5/vulIntelligence | 等级为高危或严重 |
| 知道创宇Seebug漏洞库 | https://www.seebug.org/ | 等级为高危或严重 |
| CISA KEV | https://www.cisa.gov/known-exploited-vulnerabilities-catalog | 全部推送 |
| Struts2 Security Bulletins | [Struts2 Security Bulletins](https://cwiki.apache.org/confluence/display/WW/Security+Bulletins) | 等级为高危或严重 |

> 所有信息来自网站公开页面, 如果有侵权,请提交 issue, 我会删除相关源。
Expand Down Expand Up @@ -44,29 +45,29 @@

Docker 方式推荐使用环境变量来配置服务参数

| 环境变量名 | 说明 | 默认值 |
| ----------------------- | ------------------------------------------------------------ | --------------------------------------- |
| `DB_CONN` | 数据库链接字符串,详情见 [数据库连接](#数据库连接) | `sqlite3://vuln_v3.sqlite3` |
| `DINGDING_ACCESS_TOKEN` | 钉钉机器人 url 的 `access_token` 部分 | |
| `DINGDING_SECRET` | 钉钉机器人的加签值 (仅支持加签方式) | |
| `LARK_ACCESS_TOKEN` | 飞书机器人 url 的 `/open-apis/bot/v2/hook/` 后的部分, 也支持直接指定完整的 url 来访问私有部署的飞书 | |
| `LARK_SECRET` | 飞书机器人的加签值 (仅支持加签方式) | |
| `WECHATWORK_KEY ` | 微信机器人 url 的 `key` 部分 | |
| `SERVERCHAN_KEY ` | Server酱的 `SCKEY` | |
| `WEBHOOK_URL` | 自定义 webhook 服务的完整 url | |
| `BARK_URL` | Bark 服务的完整 url, 路径需要包含 DeviceKey | |
| `PUSHPLUS_KEY` | PushPlus的token | |
| `LANXIN_DOMAIN` | 蓝信webhook机器人的域名 | |
| `LANXIN_TOKEN` | 蓝信webhook机器人的hook token | |
| `LANXIN_SECRET` | 蓝信webhook机器人的签名 | |
| `TELEGRAM_BOT_TOKEN` | Telegram Bot Token | |
| `TELEGRAM_CHAT_IDS` | Telegram Bot 需要发送给的 chat 列表,使用 `,` 分割 | |
| 环境变量名 | 说明 | 默认值 |
|-------------------------|-------------------------------------------------------------------------|-----------------------------------------|
| `DB_CONN` | 数据库链接字符串,详情见 [数据库连接](#数据库连接) | `sqlite3://vuln_v3.sqlite3` |
| `DINGDING_ACCESS_TOKEN` | 钉钉机器人 url 的 `access_token` 部分 | |
| `DINGDING_SECRET` | 钉钉机器人的加签值 (仅支持加签方式) | |
| `LARK_ACCESS_TOKEN` | 飞书机器人 url 的 `/open-apis/bot/v2/hook/` 后的部分, 也支持直接指定完整的 url 来访问私有部署的飞书 | |
| `LARK_SECRET` | 飞书机器人的加签值 (仅支持加签方式) | |
| `WECHATWORK_KEY ` | 微信机器人 url 的 `key` 部分 | |
| `SERVERCHAN_KEY ` | Server酱的 `SCKEY` | |
| `WEBHOOK_URL` | 自定义 webhook 服务的完整 url | |
| `BARK_URL` | Bark 服务的完整 url, 路径需要包含 DeviceKey | |
| `PUSHPLUS_KEY` | PushPlus的token | |
| `LANXIN_DOMAIN` | 蓝信webhook机器人的域名 | |
| `LANXIN_TOKEN` | 蓝信webhook机器人的hook token | |
| `LANXIN_SECRET` | 蓝信webhook机器人的签名 | |
| `TELEGRAM_BOT_TOKEN` | Telegram Bot Token | |
| `TELEGRAM_CHAT_IDS` | Telegram Bot 需要发送给的 chat 列表,使用 `,` 分割 | |
| `SOURCES` | 启用哪些漏洞信息源,逗号分隔, 可选 `avd`, `ti`, `oscs`, `seebug`,`threatbook`,`struts2` | `avd,ti,oscs,threatbook,seebug,struts2` |
| `INTERVAL` | 检查周期,支持秒 `60s`, 分钟 `10m`, 小时 `1h`, 最低 `1m` | `30m` |
| `ENABLE_CVE_FILTER` | 启用 CVE 过滤,开启后多个数据源的统一 CVE 将只推送一次 | `true` |
| `NO_FILTER` | 禁用上述推送过滤策略,所有新发现的漏洞都会被推送 | `false` |
| `NO_START_MESSAGE` | 禁用服务启动的提示信息 | `false` |
| `HTTPS_PROXY` | 给所有请求配置代理, 支持 `socks5://xxxx` 或者 `http(s)://xxkx` | |
| `INTERVAL` | 检查周期,支持秒 `60s`, 分钟 `10m`, 小时 `1h`, 最低 `1m` | `30m` |
| `ENABLE_CVE_FILTER` | 启用 CVE 过滤,开启后多个数据源的统一 CVE 将只推送一次 | `true` |
| `NO_FILTER` | 禁用上述推送过滤策略,所有新发现的漏洞都会被推送 | `false` |
| `NO_START_MESSAGE` | 禁用服务启动的提示信息 | `false` |
| `HTTPS_PROXY` | 给所有请求配置代理, 支持 `socks5://xxxx` 或者 `http(s)://xxkx` | |

比如使用钉钉机器人

Expand Down
20 changes: 13 additions & 7 deletions ctrl/ctrl.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,14 +225,20 @@ func (w *WatchVulnApp) Run(ctx context.Context) error {
}
}
w.log.Infof("Pushing %s", v)
if err := w.pushVuln(v); err == nil {
// 如果两种推送都成功,才标记为已推送
_, err = dbVuln.Update().SetPushed(true).Save(ctx)
if err != nil {
w.log.Errorf("failed to save pushed %s status, %s", v.UniqueKey, err)
// retry 3 times
for i := 0; i < 3; i++ {
if err := w.pushVuln(v); err == nil {
// 如果两种推送都成功,才标记为已推送
_, err = dbVuln.Update().SetPushed(true).Save(ctx)
if err != nil {
w.log.Errorf("failed to save pushed %s status, %s", v.UniqueKey, err)
}
break
} else {
w.log.Errorf("failed to push %s, %s", v.UniqueKey, err)
}
} else {
w.log.Errorf("failed to push %s, %s", v.UniqueKey, err)
w.log.Infof("retry to push %s after 30s", v.UniqueKey)
time.Sleep(time.Second * 30)
}
} else {
w.log.Infof("skipped %s as not valuable", v)
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ require (
github.com/PuerkitoBio/goquery v1.8.1
github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d
github.com/dop251/goja_nodejs v0.0.0-20231122114759-e84d9a924c5c
github.com/go-resty/resty/v2 v2.12.0
github.com/go-sql-driver/mysql v1.7.1
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/google/go-github/v53 v53.2.0
github.com/hashicorp/go-multierror v1.1.1
github.com/imroc/req/v3 v3.42.2
github.com/jackc/pgx/v5 v5.5.1
github.com/jackc/pgx/v5 v5.5.5
github.com/kataras/golog v0.1.8
github.com/larksuite/oapi-sdk-go/v2 v2.0.11
github.com/mmcdole/gofeed v1.2.1
Expand All @@ -40,7 +41,6 @@ require (
github.com/dlclark/regexp2 v1.10.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/go-resty/resty/v2 v2.12.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/protobuf v1.5.3 // indirect
Expand Down
6 changes: 3 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.1 h1:5I9etrGkLrN+2XPCsi6XLlV5DITbSL/xBZdmAxFcXPI=
github.com/jackc/pgx/v5 v5.5.1/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
Expand Down Expand Up @@ -195,7 +195,6 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
Expand Down Expand Up @@ -238,6 +237,7 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
Expand Down
18 changes: 12 additions & 6 deletions grab/kev.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ package grab

import (
"context"
"sort"
"time"

"github.com/imroc/req/v3"
"github.com/kataras/golog"
"github.com/pkg/errors"
"time"
)

const KEVUrl = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json"

// const CVEUrl = "https://cveawg.mitre.org/api/cve/" 查询原始评级预留url
const PageSize = 5 //KEV每次都是返回全量数据,所以这里自己定义一下pagesize匹配原来的爬取逻辑
const KEYPageSize = 10 // KEV每次都是返回全量数据,所以这里自己定义一下pagesize匹配原来的爬取逻辑

type KEVCrawler struct {
client *req.Client
Expand Down Expand Up @@ -54,23 +56,27 @@ func (c *KEVCrawler) GetUpdate(ctx context.Context, pageLimit int) ([]*VulnInfo,
var vulnInfos []*VulnInfo
var itemLimit = 0
var maxCount = len(result.Vulnerabilities)
if pageLimit*PageSize > maxCount {
if pageLimit*KEYPageSize > maxCount {
itemLimit = maxCount
} else {
itemLimit = pageLimit * PageSize
itemLimit = pageLimit * KEYPageSize
}
sort.Slice(result.Vulnerabilities, func(i, j int) bool {
return result.Vulnerabilities[i].DateAdded > result.Vulnerabilities[j].DateAdded
})
for i := 1; i <= itemLimit; i++ {
var vulnInfo VulnInfo
vuln := result.Vulnerabilities[maxCount-i]
vulnInfo.UniqueKey = vuln.CveID + "_KEV"
vulnInfo.Title = vuln.VulnerabilityName
vulnInfo.Description = vuln.ShortDescription
vulnInfo.Severity = Critical //数据源本身无该字段,因为有在野利用直接提成Critical了,后续考虑要不要去CVE查询原始评级?
vulnInfo.Severity = Critical // 数据源本身无该字段,因为有在野利用直接提成Critical了,后续考虑要不要去CVE查询原始评级?
vulnInfo.CVE = vuln.CveID
vulnInfo.Solutions = vuln.RequiredAction
vulnInfo.Disclosure = vuln.DateAdded
vulnInfo.From = vuln.Notes
vulnInfo.From = "https://www.cisa.gov/known-exploited-vulnerabilities-catalog"
vulnInfo.Tags = []string{vuln.VendorProject, vuln.Product, "在野利用"}
vulnInfo.Creator = c
vulnInfos = append(vulnInfos, &vulnInfo)
}

Expand Down
5 changes: 3 additions & 2 deletions grab/kev_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package grab

import (
"context"
"github.com/stretchr/testify/require"
"testing"
"time"

"github.com/stretchr/testify/require"
)

func TestKEV(t *testing.T) {
Expand All @@ -26,5 +27,5 @@ func TestKEV(t *testing.T) {
assert.NotEmpty(v.Severity)

}
assert.Equal(count, 25)
assert.Equal(count, 50)
}
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
)

var log = golog.Child("[main]")
var Version = "v1.6.0"
var Version = "v1.7.0"

func main() {
golog.Default.SetLevel("info")
Expand Down Expand Up @@ -357,7 +357,7 @@ func initPusher(c *cli.Context) (push.TextPusher, push.RawPusher, error) {
if webhook != "" {
rawPusher = append(rawPusher, push.NewWebhook(webhook))
}
if lanxinDomain != "" && lanxinToken != "" && lanxinSecret != "" {
if lanxinDomain != "" && lanxinToken != "" && lanxinSecret != "" {
textPusher = append(textPusher, push.NewLanxin(lanxinDomain, lanxinToken, lanxinSecret))
}
if bark != "" {
Expand Down
23 changes: 11 additions & 12 deletions push/lanxin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/kataras/golog"
"github.com/pkg/errors"
"io"
"net/http"
"strconv"
"time"

"github.com/kataras/golog"
"github.com/pkg/errors"
)

var _ = TextPusher(&LanXin{})
Expand Down Expand Up @@ -56,12 +57,8 @@ type LanXinResponse struct {

func GenSign(secret string, timestamp int64) string {
stringToSign := fmt.Sprintf("%v", timestamp) + "@" + secret
fmt.Println(stringToSign)

h := hmac.New(sha256.New, []byte(stringToSign))
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))

fmt.Println(signature)
return signature
}

Expand Down Expand Up @@ -129,20 +126,22 @@ func (m *LanXin) Send(content string) (response *LanXinResponse, error error) {
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload))
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, _ := client.Do(req)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrap(err, "read body")
}
defer resp.Body.Close()
err = json.Unmarshal(data, res)
if err != nil {
return res, errors.New("json格式好数据失败")
}
if res.ErrCode == 0 {
return res, nil
if res.ErrCode != 0 {
return res, errors.New(res.ErrMsg)
}
return res, errors.New(res.ErrMsg)
return res, nil
}
5 changes: 3 additions & 2 deletions push/lanxin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ func newLanxin() *LanXin {
return &LanXin{
// 下面要填入能用的domain、token和secret
domain: "",
token: "",
token: "",
secret: "",
log: golog.Child("[pusher-lanxin]"),
log: golog.Child("[pusher-lanxin]"),
}
}

func TestLanxinSendText(t *testing.T) {
t.Skip("local test lanxin")
result, err := lanxin.Send("6666")
fmt.Println(result)
assert.Nil(t, err)
Expand Down
13 changes: 5 additions & 8 deletions push/pushplus.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package push
import (
"encoding/json"
"fmt"

"github.com/go-resty/resty/v2"
"github.com/kataras/golog"
"github.com/pkg/errors"
Expand All @@ -21,8 +22,6 @@ type PushPlusResponse struct {
Data string `json:"data"`
}

const URI = "https://www.pushplus.plus/send"

var _ = TextPusher(&PushPlus{})

type PushPlus struct {
Expand All @@ -45,7 +44,7 @@ func (r *PushPlus) Send(message PushPlusMessage) (response *PushPlusResponse, er
return res, errors.New("invalid token")
}

result, err := resty.New().R().SetBody(message).SetHeader("Content-Type", "application/json").Post(URI)
result, err := resty.New().R().SetBody(message).SetHeader("Content-Type", "application/json").Post("https://www.pushplus.plus/send")

if err != nil {
return res, errors.New(fmt.Sprintf("请求失败:%s", err.Error()))
Expand All @@ -54,12 +53,10 @@ func (r *PushPlus) Send(message PushPlusMessage) (response *PushPlusResponse, er
if err != nil {
return res, errors.New("json 格式化数据失败")
}

if res.Code == 200 {
return res, nil
if res.Code != 200 {
return res, errors.New(res.Msg)
}

return res, errors.New(res.Msg)
return res, nil
}

func (d *PushPlus) PushText(s string) error {
Expand Down
2 changes: 2 additions & 0 deletions push/pushplus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func newPushPlus() *PushPlus {
}

func TestPushPlusSendTxt(t *testing.T) {
t.Skip("local test plusplus")
message := PushPlusMessage{
Title: "test1",
Content: `<h1>纯文本内容</h1>`,
Expand All @@ -29,6 +30,7 @@ func TestPushPlusSendTxt(t *testing.T) {
}

func TestPushPlusSendMarkdown(t *testing.T) {
t.Skip("local test plusplus")
message := PushPlusMessage{
Title: "test",
Content: "# 内容",
Expand Down

0 comments on commit 9839f97

Please sign in to comment.