diff --git a/.golangci.yml b/.golangci.yml index e1f2fc09a84e..11f8b20f84cc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,6 +4,8 @@ run: skip-dirs: - pkg/time/rate skip-files: + - pkg/cti/client.go + - pkg/cti/types.go - pkg/database/ent/generate.go - pkg/yamlpatch/merge.go - pkg/yamlpatch/merge_test.go diff --git a/Makefile b/Makefile index 5d656165fa8c..3ed8113ec198 100644 --- a/Makefile +++ b/Makefile @@ -155,6 +155,20 @@ endif # intentional, empty line $(info ) + +# To update cti_openapi.yaml: +# curl https://crowdsecurity.github.io/cti-api/v2/swagger.yaml | npx swagger2openapi -o cti_openapi.yaml /dev/stdin + +.PHONY: gen-cti +gen-cti: ## Generate CTI client code from the specs + @which oapi-codegen > /dev/null 2>&1 || (echo "oapi-codegen is not installed. You can install it with 'go install github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen@latest'" && exit 1) + @echo "Generating Go client from Swagger spec..." + oapi-codegen -package cti -generate client -o ./pkg/cti/client.go ./pkg/cti/cti_openapi.yaml + oapi-codegen -package cti -generate types -o ./pkg/cti/types.go ./pkg/cti/cti_openapi.yaml + @echo "Client generation complete." + + + .PHONY: all all: clean test build ## Clean, test and build (requires localstack) diff --git a/go.mod b/go.mod index d61c191c14f1..0c6efe82644a 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( github.com/gofrs/uuid v4.0.0+incompatible github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-querystring v1.0.0 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.5.0 github.com/google/winops v0.0.0-20230712152054-af9b550d0601 github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e github.com/gorilla/websocket v1.5.0 @@ -60,10 +60,11 @@ require ( github.com/jarcoal/httpmock v1.1.0 github.com/jszwec/csvutil v1.5.1 github.com/lithammer/dedent v1.1.0 - github.com/mattn/go-isatty v0.0.19 + github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-sqlite3 v1.14.16 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/nxadm/tail v1.4.8 + github.com/oapi-codegen/runtime v1.1.1 github.com/oschwald/geoip2-golang v1.4.0 github.com/oschwald/maxminddb-golang v1.8.0 github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 @@ -101,11 +102,13 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ahmetalpbalkan/dlog v0.0.0-20170105205344-4fb5f8204f26 // indirect + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bytedance/sonic v1.9.1 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/bytedance/sonic v1.10.0-rc3 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/corazawaf/libinjection-go v0.1.2 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect @@ -125,7 +128,7 @@ require ( github.com/go-openapi/spec v0.20.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/go-playground/validator/v10 v10.14.1 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -150,7 +153,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.17.3 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magefile/mage v1.15.0 // indirect @@ -171,7 +174,7 @@ require ( github.com/oklog/run v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pelletier/go-toml/v2 v2.0.9 // indirect github.com/petar-dambovaliev/aho-corasick v0.0.0-20230725210150-fb29fc3c913e // indirect github.com/pierrec/lz4/v4 v4.1.18 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -199,13 +202,13 @@ require ( github.com/yusufpapurcu/wmi v1.2.3 // indirect github.com/zclconf/go-cty v1.8.0 // indirect go.mongodb.org/mongo-driver v1.9.4 // indirect - golang.org/x/arch v0.3.0 // indirect + golang.org/x/arch v0.4.0 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/term v0.15.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.8.1-0.20230428195545-5283a0178901 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index f5f61594ecd6..038a00bac0f6 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,7 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDe github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= @@ -41,6 +42,8 @@ github.com/alexliesenfeld/health v0.8.0/go.mod h1:TfNP0f+9WQVWMQRzvMUjlws4ceXKEL github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antonmedv/expr v1.15.3 h1:q3hOJZNvLvhqE8OHBs1cFRdbXFNKuA+bHmRaI+AmRmI= github.com/antonmedv/expr v1.15.3/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/appleboy/gin-jwt/v2 v2.8.0 h1:Glo7cb9eBR+hj8Y7WzgfkOlqCaNLjP+RV4dNO3fpdps= @@ -70,18 +73,23 @@ github.com/blackfireio/osinfo v1.0.3 h1:Yk2t2GTPjBcESv6nDSWZKO87bGMQgO+Hi9OoXPpx github.com/blackfireio/osinfo v1.0.3/go.mod h1:Pd987poVNmd5Wsx6PRPw4+w7kLlf9iJxoRKPtPAjOrA= github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0= +github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/c-robinson/iplib v1.0.3 h1:NG0UF0GoEsrC1/vyfX1Lx2Ss7CySWl3KqqXh3q4DdPU= github.com/c-robinson/iplib v1.0.3/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/corazawaf/libinjection-go v0.1.2 h1:oeiV9pc5rvJ+2oqOqXEAMJousPpGiup6f7Y3nZj5GoM= @@ -98,14 +106,10 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/crowdsecurity/coraza/v3 v3.0.0-20231213144607-41d5358da94f h1:FkOB9aDw0xzDd14pTarGRLsUNAymONq3dc7zhvsXElg= -github.com/crowdsecurity/coraza/v3 v3.0.0-20231213144607-41d5358da94f/go.mod h1:TrU7Li+z2RHNrPy0TKJ6R65V6Yzpan2sTIRryJJyJso= github.com/crowdsecurity/coraza/v3 v3.0.0-20240108124027-a62b8d8e5607 h1:hyrYw3h8clMcRL2u5ooZ3tmwnmJftmhb9Ws1MKmavvI= github.com/crowdsecurity/coraza/v3 v3.0.0-20240108124027-a62b8d8e5607/go.mod h1:br36fEqurGYZQGit+iDYsIzW0FF6VufMbDzyyLxEuPA= github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26 h1:r97WNVC30Uen+7WnLs4xDScS/Ex988+id2k6mDf8psU= github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26/go.mod h1:zpv7r+7KXwgVUZnUNjyP22zc/D7LKjyoY02weH2RBbk= -github.com/crowdsecurity/go-cs-lib v0.0.5 h1:eVLW+BRj3ZYn0xt5/xmgzfbbB8EBo32gM4+WpQQk2e8= -github.com/crowdsecurity/go-cs-lib v0.0.5/go.mod h1:8FMKNGsh3hMZi2SEv6P15PURhEJnZV431XjzzBSuf0k= github.com/crowdsecurity/go-cs-lib v0.0.6 h1:Ef6MylXe0GaJE9vrfvxEdbHb31+JUP1os+murPz7Pos= github.com/crowdsecurity/go-cs-lib v0.0.6/go.mod h1:8FMKNGsh3hMZi2SEv6P15PURhEJnZV431XjzzBSuf0k= github.com/crowdsecurity/grokky v0.2.1 h1:t4VYnDlAd0RjDM2SlILalbwfCrQxtJSMGdQOR0zwkE4= @@ -257,8 +261,8 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= +github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -331,8 +335,8 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/winops v0.0.0-20230712152054-af9b550d0601 h1:XvlrmqZIuwxuRE88S9mkxX+FkV+YakqbiAC5Z4OzDnM= github.com/google/winops v0.0.0-20230712152054-af9b550d0601/go.mod h1:rT1mcjzuvcDDbRmUTsoH6kV0DG91AkFe9UCjASraK5I= github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40= @@ -428,6 +432,7 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jszwec/csvutil v1.5.1 h1:c3GFBhj6DFMUl4dMK3+B6rz2+LWWS/e9VJiVJ9t9kfQ= github.com/jszwec/csvutil v1.5.1/go.mod h1:Rpu7Uu9giO9subDyMCIQfHVDuLrcaC36UA4YcJjGBkg= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= @@ -442,8 +447,9 @@ github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHU github.com/klauspost/compress v1.17.3 h1:qkRjuerhUU1EmXLYGkSH6EZL+vPSxIrYjLNAK4slzwA= github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -496,8 +502,8 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= @@ -544,6 +550,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -560,8 +568,8 @@ github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhM github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/petar-dambovaliev/aho-corasick v0.0.0-20230725210150-fb29fc3c913e h1:POJco99aNgosh92lGqmx7L1ei+kCymivB/419SD15PQ= github.com/petar-dambovaliev/aho-corasick v0.0.0-20230725210150-fb29fc3c913e/go.mod h1:EHPiTAKtiFmrMldLUNswFwfZ2eJIYBHktdaUTZxYWRw= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= @@ -645,6 +653,7 @@ github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRM github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -743,8 +752,8 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= +golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -809,8 +818,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -841,7 +848,6 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -876,8 +882,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -906,8 +912,9 @@ golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= @@ -962,6 +969,7 @@ k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/pkg/cti/auth.go b/pkg/cti/auth.go new file mode 100644 index 000000000000..4e0b239eadf2 --- /dev/null +++ b/pkg/cti/auth.go @@ -0,0 +1,14 @@ +package cti + +import ( + "context" + "net/http" +) + +func APIKeyInserter(apiKey string) RequestEditorFn { + return func(ctx context.Context, req *http.Request) error { + req.Header.Add("x-api-key", apiKey) + return nil + } +} + diff --git a/pkg/cti/client.go b/pkg/cti/client.go new file mode 100644 index 000000000000..2aa3c95ffe35 --- /dev/null +++ b/pkg/cti/client.go @@ -0,0 +1,623 @@ +// Package cti provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.1.0 DO NOT EDIT. +package cti + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "github.com/oapi-codegen/runtime" +) + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // GetFire request + GetFire(ctx context.Context, params *GetFireParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetSmoke request + GetSmoke(ctx context.Context, params *GetSmokeParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetSmokeIp request + GetSmokeIp(ctx context.Context, ip string, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) GetFire(ctx context.Context, params *GetFireParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetFireRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetSmoke(ctx context.Context, params *GetSmokeParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetSmokeRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetSmokeIp(ctx context.Context, ip string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetSmokeIpRequest(c.Server, ip) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewGetFireRequest generates requests for GetFire +func NewGetFireRequest(server string, params *GetFireParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/fire") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Page != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "page", runtime.ParamLocationQuery, *params.Page); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.Since != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "since", runtime.ParamLocationQuery, *params.Since); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetSmokeRequest generates requests for GetSmoke +func NewGetSmokeRequest(server string, params *GetSmokeParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/smoke") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "ips", runtime.ParamLocationQuery, params.Ips); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetSmokeIpRequest generates requests for GetSmokeIp +func NewGetSmokeIpRequest(server string, ip string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "ip", runtime.ParamLocationPath, ip) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/smoke/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // GetFireWithResponse request + GetFireWithResponse(ctx context.Context, params *GetFireParams, reqEditors ...RequestEditorFn) (*GetFireResponse, error) + + // GetSmokeWithResponse request + GetSmokeWithResponse(ctx context.Context, params *GetSmokeParams, reqEditors ...RequestEditorFn) (*GetSmokeResponse, error) + + // GetSmokeIpWithResponse request + GetSmokeIpWithResponse(ctx context.Context, ip string, reqEditors ...RequestEditorFn) (*GetSmokeIpResponse, error) +} + +type GetFireResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *FireCTIResponse + JSON400 *ErrorResponse + JSON403 *ErrorResponse + JSON404 *ErrorResponse + JSON429 *ErrorResponse + JSON500 *ErrorResponse +} + +// Status returns HTTPResponse.Status +func (r GetFireResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetFireResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetSmokeResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *SearchCTIResponse + JSON400 *ErrorResponse + JSON403 *ErrorResponse + JSON404 *ErrorResponse + JSON429 *ErrorResponse + JSON500 *ErrorResponse +} + +// Status returns HTTPResponse.Status +func (r GetSmokeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetSmokeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetSmokeIpResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *QueryCTIResponse + JSON400 *ErrorResponse + JSON403 *ErrorResponse + JSON404 *ErrorResponse + JSON429 *ErrorResponse + JSON500 *ErrorResponse +} + +// Status returns HTTPResponse.Status +func (r GetSmokeIpResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetSmokeIpResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// GetFireWithResponse request returning *GetFireResponse +func (c *ClientWithResponses) GetFireWithResponse(ctx context.Context, params *GetFireParams, reqEditors ...RequestEditorFn) (*GetFireResponse, error) { + rsp, err := c.GetFire(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetFireResponse(rsp) +} + +// GetSmokeWithResponse request returning *GetSmokeResponse +func (c *ClientWithResponses) GetSmokeWithResponse(ctx context.Context, params *GetSmokeParams, reqEditors ...RequestEditorFn) (*GetSmokeResponse, error) { + rsp, err := c.GetSmoke(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetSmokeResponse(rsp) +} + +// GetSmokeIpWithResponse request returning *GetSmokeIpResponse +func (c *ClientWithResponses) GetSmokeIpWithResponse(ctx context.Context, ip string, reqEditors ...RequestEditorFn) (*GetSmokeIpResponse, error) { + rsp, err := c.GetSmokeIp(ctx, ip, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetSmokeIpResponse(rsp) +} + +// ParseGetFireResponse parses an HTTP response from a GetFireWithResponse call +func ParseGetFireResponse(rsp *http.Response) (*GetFireResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetFireResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest FireCTIResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetSmokeResponse parses an HTTP response from a GetSmokeWithResponse call +func ParseGetSmokeResponse(rsp *http.Response) (*GetSmokeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetSmokeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest SearchCTIResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetSmokeIpResponse parses an HTTP response from a GetSmokeIpWithResponse call +func ParseGetSmokeIpResponse(rsp *http.Response) (*GetSmokeIpResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetSmokeIpResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest QueryCTIResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 429: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON429 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest ErrorResponse + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} diff --git a/pkg/cti/cti_openapi.yaml b/pkg/cti/cti_openapi.yaml new file mode 100644 index 000000000000..4a33027f287c --- /dev/null +++ b/pkg/cti/cti_openapi.yaml @@ -0,0 +1,600 @@ +openapi: 3.0.1 +info: + description: CTI by Crowdsec + version: 2022-02-16T14:00:00 + title: CTI v2 + contact: + name: Crowdsec team + url: https://github.com/crowdsecurity/crowdsec + email: support@crowdsec.net +externalDocs: + description: CTI Documentation + url: https://docs.crowdsec.net/docs/next/cti_api/intro/ +servers: + - url: https://cti.api.crowdsec.net/v2 +paths: + /smoke: + get: + description: Search for CTI informations + summary: Search for CTI informations + security: + - api_key: [] + parameters: + - name: ips + in: query + required: true + description: List of IPs to query, separated by comma + example: 0.0.0.0,1.1.1.1 + schema: + type: string + responses: + "200": + description: 200 response + content: + application/json: + schema: + $ref: "#/components/schemas/SearchCTIResponse" + "400": + description: 400 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "403": + description: 403 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "404": + description: 404 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "429": + description: 429 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: 500 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "/smoke/{ip}": + get: + description: Get CTI informations about the given IP + summary: CTI information on a given IP + parameters: + - name: ip + in: path + required: true + schema: + type: string + responses: + "200": + description: 200 response + content: + application/json: + schema: + $ref: "#/components/schemas/QueryCTIResponse" + "400": + description: 400 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "403": + description: 403 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "404": + description: 404 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "429": + description: 429 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: 500 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + security: + - api_key: [] + /fire: + get: + description: Get fire CTI informations (IPs belonging to the community-blocklist) + summary: Get fire CTI informations (IPs belonging to the community-blocklist) + security: + - api_key: [] + parameters: + - name: page + in: query + required: false + description: Number of the page to fetch + example: 1 + schema: + type: number + - name: since + in: query + required: false + description: Filter records updated since - duration in h (hours), d(days), + m(minutes) ) + example: 3d + schema: + type: string + responses: + "200": + description: 200 response + content: + application/json: + schema: + $ref: "#/components/schemas/FireCTIResponse" + "400": + description: 400 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "403": + description: 403 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "404": + description: 404 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "429": + description: 429 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" + "500": + description: 500 response + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" +components: + securitySchemes: + api_key: + type: apiKey + name: x-api-key + in: header + schemas: + CTIObject: + title: IP CTI Object + type: object + required: + - as_name + - as_num + - behaviors + - location + - history + - ip + - references + - ip_range + - ip_range_score + - classifications + - reverse_dns + - scores + - target_countries + properties: + ip: + type: string + description: Requested IP + example: 1.2.3.4 + ip_range: + type: string + description: The range to which the IP belongs + example: 1.2.3.0/24 + nullable: true + ip_range_score: + type: number + description: The score of the range (ip_range) the IP belongs to. 0 is + good/unknown, 5 is worse + example: 2 + as_name: + type: string + description: The autonomous system name to which the IP belongs + example: ACME + nullable: true + as_num: + type: number + description: The autonomous system number to which the IP belongs + example: 99999 + nullable: true + background_noise_score: + type: number + description: The background noise score of the IP ranging from 0 to 10 (highly + noisy) + example: 8 + nullable: true + location: + type: object + description: Location information about the IP address + required: + - country + - city + - latitude + - longitude + properties: + country: + type: string + description: The two letters country code of the IP + example: US + nullable: true + city: + type: string + description: The associated City of the IP + example: New York + nullable: true + latitude: + type: number + description: Coordinates of the IP + example: 40.7597 + nullable: true + longitude: + type: number + description: Coordinates of the IP + example: 40.7597 + nullable: true + reverse_dns: + type: string + description: Reverse dns lookup of the IP + example: acbd.my_domain.net + nullable: true + behaviors: + type: array + description: A list of the attack categories for which the IP was reported + items: + type: object + properties: + name: + type: string + description: The category of the attack, often in the form + "protocol-or-scope:attack_type" + example: http:scan + label: + type: string + description: Human-friendly description of the category + example: HTTP Scan + description: + type: string + description: Human-friendly description of the category + example: IP has been reported for performing actions related to HTTP + vulnerability scanning and discovery + references: + type: array + description: A list of the references for which the IP was see + items: + type: object + properties: + name: + type: string + description: The reference, often in the form "list:list_name" + example: list:my_list + label: + type: string + description: Human-friendly description of the reference + example: My List + description: + type: string + description: Human-friendly description of the reference + example: IP was referenced in My List + history: + type: object + properties: + first_seen: + type: string + description: Date of the first time this IP was reported. Due to "progressive + data degradation", this date might be later than the first time + the IP was actually seen + example: 2021-03-03T23:00:00 + last_seen: + type: string + description: Date of the last time this IP was reported + example: 2021-03-03T23:30:00 + full_age: + type: number + description: Delta in days between first seen and today + example: 220 + days_age: + type: number + description: Delta in days between first and last seen timestamps + example: 189 + classifications: + type: object + properties: + false_positives: + type: array + description: A list of false positives tags associated with the IP. Any IP with + `false_positives` tags shouldn't be considered as malicious + items: + type: object + properties: + name: + type: string + description: The name of the false positive, often in the form + "protocol-or-scope:attack_type" + example: seo:crawler + label: + type: string + description: Human-friendly name of the category + example: SEO crawler + description: + type: string + description: Human-friendly description of the category + example: IP belongs to a known SEO crawler and should not be flagged as a + threat. + classifications: + type: array + description: A list of categories associated with the IP. Those data can be + sourced from 3rd parties (i.e. tor exit nodes list) + items: + type: object + properties: + name: + type: string + description: The name of the category, often in the form + "protocol-or-scope:attack_type" + example: community-blocklist + label: + type: string + description: Human-friendly name of the category + example: CrowdSec Community Blocklist + description: + type: string + description: Human-friendly description of the category + example: IP belong to the CrowdSec Community Blocklist + mitre_techniques: + type: array + description: A list of Mitre Enterprise Techniques associated with the IP. + items: + type: object + properties: + name: + type: string + description: The ID of the Mitre technique" + example: T1190 + label: + type: string + description: The name of the Mitre technique + example: Exploit Public-Facing Application + description: + type: string + description: Description of the Mitre technique + example: Adversaries may attempt to exploit a weakness in an Internet-facing + host or system to initially access a network. + cves: + type: array + description: A list of CVEs reported for this IP. + items: + type: string + attack_details: + type: array + description: A more exhaustive list of the scenarios for which a given IP was + reported + items: + type: object + properties: + name: + type: string + description: Name of the scenario (see hub.crowdsec.net) + example: crowdsecurity/http-bad-user-agent + label: + type: string + description: Human-friendly descriptions of scenarios + example: Known Bad User-Agent + description: + type: string + description: Human-friendly descriptions of scenarios + example: Detect bad user-agents + references: + type: array + items: + type: string + target_countries: + type: object + description: The top 10 reports repartition by country about the IP, as a + percentage + scores: + type: object + properties: + overall: + type: object + properties: + aggressiveness: + type: number + description: Overall aggressiveness score + threat: + type: number + description: Overall threat score + trust: + type: number + description: Overall trust score + anomaly: + type: number + description: Overall anomaly score + total: + type: number + description: Overall score + last_day: + type: object + properties: + aggressiveness: + type: number + description: Last day aggressiveness score + threat: + type: number + description: Last day threat score + trust: + type: number + description: Last day trust score + anomaly: + type: number + description: Last day anomaly score + total: + type: number + description: Last day score + last_week: + type: object + properties: + aggressiveness: + type: number + description: Last week aggressiveness score + threat: + type: number + description: Last week threat score + trust: + type: number + description: Last week trust score + anomaly: + type: number + description: Last week anomaly score + total: + type: number + description: Last week score + last_month: + type: object + properties: + aggressiveness: + type: number + description: Last month aggressiveness score + threat: + type: number + description: Last month threat score + trust: + type: number + description: Last month trust score + anomaly: + type: number + description: Last month anomaly score + total: + type: number + description: Last month score + FireIPCTIResponse: + title: Fire IP CTI Response + allOf: + - $ref: "#/components/schemas/CTIObject" + - type: object + properties: + state: + type: string + description: "state of the IP in the community blocklist: validated means IP is + currently part of community blocklist, refused means it was part + of the community blocklist, but was manually purged (ie. false + positive)" + enum: + - validated + - refused + example: validated + expiration: + type: string + description: Date at which the IP address expire from the community blocklist + example: 2022-03-04T10:00:00 + SearchCTIResponse: + title: Search CTI Response + type: object + required: + - items + - total + - not_found + properties: + total: + type: number + description: IP of the request + not_found: + type: number + items: + type: array + items: + $ref: "#/components/schemas/CTIObject" + QueryCTIResponse: + title: Query IP CTI Response + $ref: "#/components/schemas/CTIObject" + FireCTIResponse: + title: Fire CTI response + type: object + required: + - _links + - items + properties: + _links: + type: object + required: + - self + - next + - first + properties: + self: + type: object + required: + - href + properties: + href: + type: string + description: Url of the current result set + example: https://cti.api.dev.crowdsec.net/v1/fire?page=3&since=4h + prev: + type: object + required: + - href + properties: + href: + type: string + description: Url of the previous page of result set + example: https://cti.api.dev.crowdsec.net/v1/fire?page=2&since=4h + next: + type: object + required: + - href + properties: + href: + type: string + description: Url of the next page of result set + example: https://cti.api.dev.crowdsec.net/v1/fire?page=4&since=4h + first: + type: object + required: + - href + properties: + href: + type: string + nullable: true + description: Url of the first page of result set + example: https://cti.api.dev.crowdsec.net/v1/fire?since=4 + items: + type: array + items: + $ref: "#/components/schemas/FireIPCTIResponse" + ErrorResponse: + type: object + required: + - message + properties: + message: + type: string + description: Error message + errors: + type: string + description: More details on individual errors + title: Error response + description: Error response return by the API diff --git a/pkg/cti/cti_swagger.yaml b/pkg/cti/cti_swagger.yaml new file mode 100644 index 000000000000..d77a987a465b --- /dev/null +++ b/pkg/cti/cti_swagger.yaml @@ -0,0 +1,820 @@ +--- +swagger: "2.0" +info: + description: "API to manage machines using [crowdsec](https://github.com/crowdsecurity/crowdsec)\ + \ and bouncers.\n" + version: "2023-01-04T11:16:39Z" + title: "prod-capi-v2" + contact: + name: "Crowdsec team" + url: "https://github.com/crowdsecurity/crowdsec" + email: "support@crowdsec.net" +host: "api.crowdsec.net" +basePath: "/v2" +tags: +- name: "watchers" + description: "Operations about watchers: crowdsec & cscli" +- name: "bouncers" + description: "Operations about decisions : bans, captcha, rate-limit etc." +schemes: +- "https" +paths: + /decisions/delete: + post: + tags: + - "watchers" + summary: "delete decisions" + description: "delete provided decisions" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - in: "body" + name: "DecisionsDeleteRequest" + required: true + schema: + $ref: "#/definitions/DecisionsDeleteRequest" + responses: + "200": + description: "200 response" + schema: + $ref: "#/definitions/SuccessResponse" + "500": + description: "500 response" + schema: + $ref: "#/definitions/ErrorResponse" + security: + - UserPoolAuthorizer: [] + /decisions/stream: + get: + tags: + - "bouncers" + - "watchers" + summary: "returns list of top decisions" + description: "returns list of top decisions to add or delete" + produces: + - "application/json" + responses: + "200": + description: "200 response" + schema: + $ref: "#/definitions/GetDecisionsStreamResponse" + "400": + description: "400 response" + schema: + $ref: "#/definitions/ErrorResponse" + "500": + description: "500 response" + schema: + $ref: "#/definitions/ErrorResponse" + "404": + description: "404 response" + schema: + $ref: "#/definitions/ErrorResponse" + security: + - UserPoolAuthorizer: [] + options: + consumes: + - "application/json" + produces: + - "application/json" + responses: + "200": + description: "200 response" + headers: + Access-Control-Allow-Origin: + type: "string" + Access-Control-Allow-Methods: + type: "string" + Access-Control-Allow-Headers: + type: "string" + /decisions/sync: + post: + tags: + - "watchers" + summary: "sync decisions" + description: "sync provided decisions" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - in: "body" + name: "DecisionsSyncRequest" + required: true + schema: + $ref: "#/definitions/DecisionsSyncRequest" + responses: + "200": + description: "200 response" + schema: + $ref: "#/definitions/SuccessResponse" + "500": + description: "500 response" + schema: + $ref: "#/definitions/ErrorResponse" + security: + - UserPoolAuthorizer: [] + /metrics: + post: + tags: + - "watchers" + summary: "receive metrics about enrolled machines and bouncers in APIL" + description: "receive metrics about enrolled machines and bouncers in APIL" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - in: "body" + name: "MetricsRequest" + required: true + schema: + $ref: "#/definitions/MetricsRequest" + responses: + "200": + description: "200 response" + schema: + $ref: "#/definitions/SuccessResponse" + "400": + description: "400 response" + schema: + $ref: "#/definitions/ErrorResponse" + "500": + description: "500 response" + schema: + $ref: "#/definitions/ErrorResponse" + security: + - UserPoolAuthorizer: [] + /signals: + post: + tags: + - "watchers" + summary: "Push signals" + description: "to push signals" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - in: "body" + name: "AddSignalsRequest" + required: true + schema: + $ref: "#/definitions/AddSignalsRequest" + responses: + "200": + description: "200 response" + schema: + $ref: "#/definitions/SuccessResponse" + "400": + description: "400 response" + schema: + $ref: "#/definitions/ErrorResponse" + "500": + description: "500 response" + schema: + $ref: "#/definitions/ErrorResponse" + security: + - UserPoolAuthorizer: [] + /watchers: + post: + tags: + - "watchers" + summary: "Register watcher" + description: "Register a watcher" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - in: "body" + name: "RegisterRequest" + required: true + schema: + $ref: "#/definitions/RegisterRequest" + responses: + "200": + description: "200 response" + schema: + $ref: "#/definitions/SuccessResponse" + "400": + description: "400 response" + schema: + $ref: "#/definitions/ErrorResponse" + "500": + description: "500 response" + schema: + $ref: "#/definitions/ErrorResponse" + /watchers/enroll: + post: + tags: + - "watchers" + summary: "watcher enrollment" + description: "watcher enrollment : enroll watcher to crowdsec backoffice account" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - in: "body" + name: "EnrollRequest" + required: true + schema: + $ref: "#/definitions/EnrollRequest" + responses: + "200": + description: "200 response" + schema: + $ref: "#/definitions/SuccessResponse" + "400": + description: "400 response" + schema: + $ref: "#/definitions/ErrorResponse" + "500": + description: "500 response" + schema: + $ref: "#/definitions/ErrorResponse" + "403": + description: "403 response" + schema: + $ref: "#/definitions/ErrorResponse" + security: + - UserPoolAuthorizer: [] + /watchers/login: + post: + tags: + - "watchers" + summary: "watcher login" + description: "Sign-in to get a valid token" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - in: "body" + name: "LoginRequest" + required: true + schema: + $ref: "#/definitions/LoginRequest" + responses: + "200": + description: "200 response" + schema: + $ref: "#/definitions/LoginResponse" + "400": + description: "400 response" + schema: + $ref: "#/definitions/ErrorResponse" + "500": + description: "500 response" + schema: + $ref: "#/definitions/ErrorResponse" + "403": + description: "403 response" + schema: + $ref: "#/definitions/ErrorResponse" + /watchers/reset: + post: + tags: + - "watchers" + summary: "Reset Password" + description: "to reset a watcher password" + consumes: + - "application/json" + produces: + - "application/json" + parameters: + - in: "body" + name: "ResetPasswordRequest" + required: true + schema: + $ref: "#/definitions/ResetPasswordRequest" + responses: + "200": + description: "200 response" + schema: + $ref: "#/definitions/SuccessResponse" + headers: + Content-type: + type: "string" + Access-Control-Allow-Origin: + type: "string" + "400": + description: "400 response" + schema: + $ref: "#/definitions/ErrorResponse" + "500": + description: "500 response" + schema: + $ref: "#/definitions/ErrorResponse" + headers: + Content-type: + type: "string" + Access-Control-Allow-Origin: + type: "string" + "403": + description: "403 response" + schema: + $ref: "#/definitions/ErrorResponse" + "404": + description: "404 response" + headers: + Content-type: + type: "string" + Access-Control-Allow-Origin: + type: "string" + options: + consumes: + - "application/json" + produces: + - "application/json" + responses: + "200": + description: "200 response" + headers: + Access-Control-Allow-Origin: + type: "string" + Access-Control-Allow-Methods: + type: "string" + Access-Control-Allow-Headers: + type: "string" +securityDefinitions: + UserPoolAuthorizer: + type: "apiKey" + name: "Authorization" + in: "header" + x-amazon-apigateway-authtype: "cognito_user_pools" +definitions: + DecisionsDeleteRequest: + title: "delete decisions" + type: "array" + description: "delete decision model" + items: + $ref: "#/definitions/DecisionsDeleteRequestItem" + DecisionsSyncRequestItem: + type: "object" + required: + - "message" + - "scenario" + - "scenario_hash" + - "scenario_version" + - "source" + - "start_at" + - "stop_at" + properties: + scenario_trust: + type: "string" + scenario_hash: + type: "string" + scenario: + type: "string" + alert_id: + type: "integer" + created_at: + type: "string" + machine_id: + type: "string" + decisions: + $ref: "#/definitions/DecisionsSyncRequestItemDecisions" + source: + $ref: "#/definitions/DecisionsSyncRequestItemSource" + scenario_version: + type: "string" + message: + type: "string" + description: "a human readable message" + start_at: + type: "string" + stop_at: + type: "string" + title: "Signal" + AddSignalsRequestItem: + type: "object" + required: + - "message" + - "scenario" + - "scenario_hash" + - "scenario_version" + - "source" + - "start_at" + - "stop_at" + properties: + created_at: + type: "string" + machine_id: + type: "string" + source: + $ref: "#/definitions/AddSignalsRequestItemSource" + scenario_version: + type: "string" + message: + type: "string" + description: "a human readable message" + start_at: + type: "string" + scenario_trust: + type: "string" + scenario_hash: + type: "string" + scenario: + type: "string" + alert_id: + type: "integer" + context: + type: "array" + nullable: true + items: + type: "object" + properties: + value: + type: "string" + key: + type: "string" + decisions: + $ref: "#/definitions/AddSignalsRequestItemDecisions" + stop_at: + type: "string" + title: "Signal" + DecisionsSyncRequest: + title: "sync decisions request" + type: "array" + description: "sync decision model" + items: + $ref: "#/definitions/DecisionsSyncRequestItem" + LoginRequest: + type: "object" + required: + - "machine_id" + - "password" + properties: + password: + type: "string" + description: "Password, should respect the password policy (link to add)" + machine_id: + type: "string" + description: "machine_id is a (username) generated by crowdsec" + minLength: 48 + maxLength: 48 + pattern: "^[a-zA-Z0-9]+$" + scenarios: + type: "array" + description: "all scenarios installed" + items: + type: "string" + title: "login request" + description: "Login request model" + GetDecisionsStreamResponseNewItem: + type: "object" + required: + - "duration" + - "origin" + - "scenario" + - "scope" + - "type" + - "value" + properties: + duration: + type: "string" + scenario: + type: "string" + origin: + type: "string" + description: "the origin of the decision : cscli, crowdsec" + scope: + type: "string" + description: "the scope of decision : does it apply to an IP, a range, a username,\ + \ etc" + start_ip: + type: "integer" + description: "(only relevant for GET ops) when the value is an IP or range,\ + \ its numeric representation" + id: + type: "integer" + description: "(only relevant for GET ops) the unique id" + type: + type: "string" + description: "the type of decision, might be 'ban', 'captcha' or something\ + \ custom. Ignored when watcher (cscli/crowdsec) is pushing to APIL." + value: + type: "string" + description: "the value of the decision scope : an IP, a range, a username,\ + \ etc" + end_ip: + type: "integer" + description: "(only relevant for GET ops) when the value is an IP or range,\ + \ its numeric representation" + title: "Decision" + AddSignalsRequestItemDecisionsItem: + type: "object" + required: + - "duration" + - "id" + - "origin" + - "scenario" + - "scope" + - "type" + - "value" + properties: + duration: + type: "string" + scenario: + type: "string" + origin: + type: "string" + description: "the origin of the decision : cscli, crowdsec" + scope: + type: "string" + description: "the scope of decision : does it apply to an IP, a range, a username,\ + \ etc" + simulated: + type: "boolean" + until: + type: "string" + id: + type: "integer" + description: "(only relevant for GET ops) the unique id" + type: + type: "string" + description: "the type of decision, might be 'ban', 'captcha' or something\ + \ custom. Ignored when watcher (cscli/crowdsec) is pushing to APIL." + value: + type: "string" + description: "the value of the decision scope : an IP, a range, a username,\ + \ etc" + title: "Decision" + EnrollRequest: + type: "object" + required: + - "attachment_key" + properties: + name: + type: "string" + description: "The name that will be display in the console for the instance" + overwrite: + type: "boolean" + description: "To force enroll the instance" + attachment_key: + type: "string" + description: "attachment_key is generated in your crowdsec backoffice account\ + \ and allows you to enroll your machines to your BO account" + pattern: "^[a-zA-Z0-9]+$" + tags: + type: "array" + description: "Tags to apply on the console for the instance" + items: + type: "string" + title: "enroll request" + description: "enroll request model" + ResetPasswordRequest: + type: "object" + required: + - "machine_id" + - "password" + properties: + password: + type: "string" + description: "Password, should respect the password policy (link to add)" + machine_id: + type: "string" + description: "machine_id is a (username) generated by crowdsec" + minLength: 48 + maxLength: 48 + pattern: "^[a-zA-Z0-9]+$" + title: "resetPassword" + description: "ResetPassword request model" + MetricsRequestBouncersItem: + type: "object" + properties: + last_pull: + type: "string" + description: "last bouncer pull date" + custom_name: + type: "string" + description: "bouncer name" + name: + type: "string" + description: "bouncer type (firewall, php...)" + version: + type: "string" + description: "bouncer version" + title: "MetricsBouncerInfo" + AddSignalsRequestItemSource: + type: "object" + required: + - "scope" + - "value" + properties: + scope: + type: "string" + description: "the scope of a source : ip,range,username,etc" + ip: + type: "string" + description: "provided as a convenience when the source is an IP" + latitude: + type: "number" + format: "float" + as_number: + type: "string" + description: "provided as a convenience when the source is an IP" + range: + type: "string" + description: "provided as a convenience when the source is an IP" + cn: + type: "string" + value: + type: "string" + description: "the value of a source : the ip, the range, the username,etc" + as_name: + type: "string" + description: "provided as a convenience when the source is an IP" + longitude: + type: "number" + format: "float" + title: "Source" + DecisionsSyncRequestItemDecisions: + title: "Decisions list" + type: "array" + items: + $ref: "#/definitions/DecisionsSyncRequestItemDecisionsItem" + RegisterRequest: + type: "object" + required: + - "machine_id" + - "password" + properties: + password: + type: "string" + description: "Password, should respect the password policy (link to add)" + machine_id: + type: "string" + description: "machine_id is a (username) generated by crowdsec" + pattern: "^[a-zA-Z0-9]+$" + title: "register request" + description: "Register request model" + SuccessResponse: + type: "object" + required: + - "message" + properties: + message: + type: "string" + description: "message" + title: "success response" + description: "success response return by the API" + LoginResponse: + type: "object" + properties: + code: + type: "integer" + expire: + type: "string" + token: + type: "string" + title: "login response" + description: "Login request model" + DecisionsSyncRequestItemDecisionsItem: + type: "object" + required: + - "duration" + - "id" + - "origin" + - "scenario" + - "scope" + - "type" + - "value" + properties: + duration: + type: "string" + scenario: + type: "string" + origin: + type: "string" + description: "the origin of the decision : cscli, crowdsec" + scope: + type: "string" + description: "the scope of decision : does it apply to an IP, a range, a username,\ + \ etc" + simulated: + type: "boolean" + until: + type: "string" + id: + type: "integer" + description: "(only relevant for GET ops) the unique id" + type: + type: "string" + description: "the type of decision, might be 'ban', 'captcha' or something\ + \ custom. Ignored when watcher (cscli/crowdsec) is pushing to APIL." + value: + type: "string" + description: "the value of the decision scope : an IP, a range, a username,\ + \ etc" + title: "Decision" + GetDecisionsStreamResponse: + type: "object" + properties: + new: + $ref: "#/definitions/GetDecisionsStreamResponseNew" + deleted: + $ref: "#/definitions/GetDecisionsStreamResponseNew" + title: "get decisions stream response" + description: "get decision response model" + DecisionsSyncRequestItemSource: + type: "object" + required: + - "scope" + - "value" + properties: + scope: + type: "string" + description: "the scope of a source : ip,range,username,etc" + ip: + type: "string" + description: "provided as a convenience when the source is an IP" + latitude: + type: "number" + format: "float" + as_number: + type: "string" + description: "provided as a convenience when the source is an IP" + range: + type: "string" + description: "provided as a convenience when the source is an IP" + cn: + type: "string" + value: + type: "string" + description: "the value of a source : the ip, the range, the username,etc" + as_name: + type: "string" + description: "provided as a convenience when the source is an IP" + longitude: + type: "number" + format: "float" + title: "Source" + AddSignalsRequestItemDecisions: + title: "Decisions list" + type: "array" + items: + $ref: "#/definitions/AddSignalsRequestItemDecisionsItem" + MetricsRequestMachinesItem: + type: "object" + properties: + last_update: + type: "string" + description: "last agent update date" + name: + type: "string" + description: "agent name" + last_push: + type: "string" + description: "last agent push date" + version: + type: "string" + description: "agent version" + title: "MetricsAgentInfo" + MetricsRequest: + type: "object" + required: + - "bouncers" + - "machines" + properties: + bouncers: + type: "array" + items: + $ref: "#/definitions/MetricsRequestBouncersItem" + machines: + type: "array" + items: + $ref: "#/definitions/MetricsRequestMachinesItem" + title: "metrics" + description: "push metrics model" + ErrorResponse: + type: "object" + required: + - "message" + properties: + message: + type: "string" + description: "Error message" + errors: + type: "string" + description: "more detail on individual errors" + title: "error response" + description: "error response return by the API" + AddSignalsRequest: + title: "add signals request" + type: "array" + description: "All signals request model" + items: + $ref: "#/definitions/AddSignalsRequestItem" + DecisionsDeleteRequestItem: + type: "string" + title: "decisionsIDs" + GetDecisionsStreamResponseNew: + title: "Decisions list" + type: "array" + items: + $ref: "#/definitions/GetDecisionsStreamResponseNewItem" diff --git a/pkg/cti/errors.go b/pkg/cti/errors.go new file mode 100644 index 000000000000..30667914b635 --- /dev/null +++ b/pkg/cti/errors.go @@ -0,0 +1,12 @@ +package cti + +import ( + "errors" +) + +var ( + ErrDisabled = errors.New("CTI is disabled") + ErrLimit = errors.New("request quota exceeeded, please reduce your request rate") + ErrUnauthorized = errors.New("unauthorized") + ErrUnknown = errors.New("unknown error") +) diff --git a/pkg/cti/types.go b/pkg/cti/types.go new file mode 100644 index 000000000000..407c1c0a92b7 --- /dev/null +++ b/pkg/cti/types.go @@ -0,0 +1,481 @@ +// Package cti provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.1.0 DO NOT EDIT. +package cti + +const ( + Api_keyScopes = "api_key.Scopes" +) + +// Defines values for FireIPCTIResponseState. +const ( + Refused FireIPCTIResponseState = "refused" + Validated FireIPCTIResponseState = "validated" +) + +// CTIObject defines model for CTIObject. +type CTIObject struct { + // AsName The autonomous system name to which the IP belongs + AsName *string `json:"as_name"` + + // AsNum The autonomous system number to which the IP belongs + AsNum *float32 `json:"as_num"` + + // AttackDetails A more exhaustive list of the scenarios for which a given IP was reported + AttackDetails *[]struct { + // Description Human-friendly descriptions of scenarios + Description *string `json:"description,omitempty"` + + // Label Human-friendly descriptions of scenarios + Label *string `json:"label,omitempty"` + + // Name Name of the scenario (see hub.crowdsec.net) + Name *string `json:"name,omitempty"` + References *[]string `json:"references,omitempty"` + } `json:"attack_details,omitempty"` + + // BackgroundNoiseScore The background noise score of the IP ranging from 0 to 10 (highly noisy) + BackgroundNoiseScore *float32 `json:"background_noise_score"` + + // Behaviors A list of the attack categories for which the IP was reported + Behaviors []struct { + // Description Human-friendly description of the category + Description *string `json:"description,omitempty"` + + // Label Human-friendly description of the category + Label *string `json:"label,omitempty"` + + // Name The category of the attack, often in the form "protocol-or-scope:attack_type" + Name *string `json:"name,omitempty"` + } `json:"behaviors"` + Classifications struct { + // Classifications A list of categories associated with the IP. Those data can be sourced from 3rd parties (i.e. tor exit nodes list) + Classifications *[]struct { + // Description Human-friendly description of the category + Description *string `json:"description,omitempty"` + + // Label Human-friendly name of the category + Label *string `json:"label,omitempty"` + + // Name The name of the category, often in the form "protocol-or-scope:attack_type" + Name *string `json:"name,omitempty"` + } `json:"classifications,omitempty"` + + // FalsePositives A list of false positives tags associated with the IP. Any IP with `false_positives` tags shouldn't be considered as malicious + FalsePositives *[]struct { + // Description Human-friendly description of the category + Description *string `json:"description,omitempty"` + + // Label Human-friendly name of the category + Label *string `json:"label,omitempty"` + + // Name The name of the false positive, often in the form "protocol-or-scope:attack_type" + Name *string `json:"name,omitempty"` + } `json:"false_positives,omitempty"` + } `json:"classifications"` + + // Cves A list of CVEs reported for this IP. + Cves *[]string `json:"cves,omitempty"` + History struct { + // DaysAge Delta in days between first and last seen timestamps + DaysAge *float32 `json:"days_age,omitempty"` + + // FirstSeen Date of the first time this IP was reported. Due to "progressive data degradation", this date might be later than the first time the IP was actually seen + FirstSeen *string `json:"first_seen,omitempty"` + + // FullAge Delta in days between first seen and today + FullAge *float32 `json:"full_age,omitempty"` + + // LastSeen Date of the last time this IP was reported + LastSeen *string `json:"last_seen,omitempty"` + } `json:"history"` + + // Ip Requested IP + Ip string `json:"ip"` + + // IpRange The range to which the IP belongs + IpRange *string `json:"ip_range"` + + // IpRangeScore The score of the range (ip_range) the IP belongs to. 0 is good/unknown, 5 is worse + IpRangeScore float32 `json:"ip_range_score"` + + // Location Location information about the IP address + Location struct { + // City The associated City of the IP + City *string `json:"city"` + + // Country The two letters country code of the IP + Country *string `json:"country"` + + // Latitude Coordinates of the IP + Latitude *float32 `json:"latitude"` + + // Longitude Coordinates of the IP + Longitude *float32 `json:"longitude"` + } `json:"location"` + + // MitreTechniques A list of Mitre Enterprise Techniques associated with the IP. + MitreTechniques *[]struct { + // Description Description of the Mitre technique + Description *string `json:"description,omitempty"` + + // Label The name of the Mitre technique + Label *string `json:"label,omitempty"` + + // Name The ID of the Mitre technique" + Name *string `json:"name,omitempty"` + } `json:"mitre_techniques,omitempty"` + + // References A list of the references for which the IP was see + References []struct { + // Description Human-friendly description of the reference + Description *string `json:"description,omitempty"` + + // Label Human-friendly description of the reference + Label *string `json:"label,omitempty"` + + // Name The reference, often in the form "list:list_name" + Name *string `json:"name,omitempty"` + } `json:"references"` + + // ReverseDns Reverse dns lookup of the IP + ReverseDns *string `json:"reverse_dns"` + Scores struct { + LastDay *struct { + // Aggressiveness Last day aggressiveness score + Aggressiveness *float32 `json:"aggressiveness,omitempty"` + + // Anomaly Last day anomaly score + Anomaly *float32 `json:"anomaly,omitempty"` + + // Threat Last day threat score + Threat *float32 `json:"threat,omitempty"` + + // Total Last day score + Total *float32 `json:"total,omitempty"` + + // Trust Last day trust score + Trust *float32 `json:"trust,omitempty"` + } `json:"last_day,omitempty"` + LastMonth *struct { + // Aggressiveness Last month aggressiveness score + Aggressiveness *float32 `json:"aggressiveness,omitempty"` + + // Anomaly Last month anomaly score + Anomaly *float32 `json:"anomaly,omitempty"` + + // Threat Last month threat score + Threat *float32 `json:"threat,omitempty"` + + // Total Last month score + Total *float32 `json:"total,omitempty"` + + // Trust Last month trust score + Trust *float32 `json:"trust,omitempty"` + } `json:"last_month,omitempty"` + LastWeek *struct { + // Aggressiveness Last week aggressiveness score + Aggressiveness *float32 `json:"aggressiveness,omitempty"` + + // Anomaly Last week anomaly score + Anomaly *float32 `json:"anomaly,omitempty"` + + // Threat Last week threat score + Threat *float32 `json:"threat,omitempty"` + + // Total Last week score + Total *float32 `json:"total,omitempty"` + + // Trust Last week trust score + Trust *float32 `json:"trust,omitempty"` + } `json:"last_week,omitempty"` + Overall *struct { + // Aggressiveness Overall aggressiveness score + Aggressiveness *float32 `json:"aggressiveness,omitempty"` + + // Anomaly Overall anomaly score + Anomaly *float32 `json:"anomaly,omitempty"` + + // Threat Overall threat score + Threat *float32 `json:"threat,omitempty"` + + // Total Overall score + Total *float32 `json:"total,omitempty"` + + // Trust Overall trust score + Trust *float32 `json:"trust,omitempty"` + } `json:"overall,omitempty"` + } `json:"scores"` + + // TargetCountries The top 10 reports repartition by country about the IP, as a percentage + TargetCountries map[string]interface{} `json:"target_countries"` +} + +// ErrorResponse Error response return by the API +type ErrorResponse struct { + // Errors More details on individual errors + Errors *string `json:"errors,omitempty"` + + // Message Error message + Message string `json:"message"` +} + +// FireCTIResponse defines model for FireCTIResponse. +type FireCTIResponse struct { + Links struct { + // First Url of the first page of result set + First struct { + Href *string `json:"href"` + } `json:"first"` + + // Next Url of the next page of result set + Next struct { + Href string `json:"href"` + } `json:"next"` + + // Prev Url of the previous page of result set + Prev *struct { + Href string `json:"href"` + } `json:"prev,omitempty"` + + // Self Url of the current result set + Self struct { + Href string `json:"href"` + } `json:"self"` + } `json:"_links"` + Items []FireIPCTIResponse `json:"items"` +} + +// FireIPCTIResponse defines model for FireIPCTIResponse. +type FireIPCTIResponse struct { + // AsName The autonomous system name to which the IP belongs + AsName *string `json:"as_name"` + + // AsNum The autonomous system number to which the IP belongs + AsNum *float32 `json:"as_num"` + + // AttackDetails A more exhaustive list of the scenarios for which a given IP was reported + AttackDetails *[]struct { + // Description Human-friendly descriptions of scenarios + Description *string `json:"description,omitempty"` + + // Label Human-friendly descriptions of scenarios + Label *string `json:"label,omitempty"` + + // Name Name of the scenario (see hub.crowdsec.net) + Name *string `json:"name,omitempty"` + References *[]string `json:"references,omitempty"` + } `json:"attack_details,omitempty"` + + // BackgroundNoiseScore The background noise score of the IP ranging from 0 to 10 (highly noisy) + BackgroundNoiseScore *float32 `json:"background_noise_score"` + + // Behaviors A list of the attack categories for which the IP was reported + Behaviors []struct { + // Description Human-friendly description of the category + Description *string `json:"description,omitempty"` + + // Label Human-friendly description of the category + Label *string `json:"label,omitempty"` + + // Name The category of the attack, often in the form "protocol-or-scope:attack_type" + Name *string `json:"name,omitempty"` + } `json:"behaviors"` + Classifications struct { + // Classifications A list of categories associated with the IP. Those data can be sourced from 3rd parties (i.e. tor exit nodes list) + Classifications *[]struct { + // Description Human-friendly description of the category + Description *string `json:"description,omitempty"` + + // Label Human-friendly name of the category + Label *string `json:"label,omitempty"` + + // Name The name of the category, often in the form "protocol-or-scope:attack_type" + Name *string `json:"name,omitempty"` + } `json:"classifications,omitempty"` + + // FalsePositives A list of false positives tags associated with the IP. Any IP with `false_positives` tags shouldn't be considered as malicious + FalsePositives *[]struct { + // Description Human-friendly description of the category + Description *string `json:"description,omitempty"` + + // Label Human-friendly name of the category + Label *string `json:"label,omitempty"` + + // Name The name of the false positive, often in the form "protocol-or-scope:attack_type" + Name *string `json:"name,omitempty"` + } `json:"false_positives,omitempty"` + } `json:"classifications"` + + // Cves A list of CVEs reported for this IP. + Cves *[]string `json:"cves,omitempty"` + + // Expiration Date at which the IP address expire from the community blocklist + Expiration *string `json:"expiration,omitempty"` + History struct { + // DaysAge Delta in days between first and last seen timestamps + DaysAge *float32 `json:"days_age,omitempty"` + + // FirstSeen Date of the first time this IP was reported. Due to "progressive data degradation", this date might be later than the first time the IP was actually seen + FirstSeen *string `json:"first_seen,omitempty"` + + // FullAge Delta in days between first seen and today + FullAge *float32 `json:"full_age,omitempty"` + + // LastSeen Date of the last time this IP was reported + LastSeen *string `json:"last_seen,omitempty"` + } `json:"history"` + + // Ip Requested IP + Ip string `json:"ip"` + + // IpRange The range to which the IP belongs + IpRange *string `json:"ip_range"` + + // IpRangeScore The score of the range (ip_range) the IP belongs to. 0 is good/unknown, 5 is worse + IpRangeScore float32 `json:"ip_range_score"` + + // Location Location information about the IP address + Location struct { + // City The associated City of the IP + City *string `json:"city"` + + // Country The two letters country code of the IP + Country *string `json:"country"` + + // Latitude Coordinates of the IP + Latitude *float32 `json:"latitude"` + + // Longitude Coordinates of the IP + Longitude *float32 `json:"longitude"` + } `json:"location"` + + // MitreTechniques A list of Mitre Enterprise Techniques associated with the IP. + MitreTechniques *[]struct { + // Description Description of the Mitre technique + Description *string `json:"description,omitempty"` + + // Label The name of the Mitre technique + Label *string `json:"label,omitempty"` + + // Name The ID of the Mitre technique" + Name *string `json:"name,omitempty"` + } `json:"mitre_techniques,omitempty"` + + // References A list of the references for which the IP was see + References []struct { + // Description Human-friendly description of the reference + Description *string `json:"description,omitempty"` + + // Label Human-friendly description of the reference + Label *string `json:"label,omitempty"` + + // Name The reference, often in the form "list:list_name" + Name *string `json:"name,omitempty"` + } `json:"references"` + + // ReverseDns Reverse dns lookup of the IP + ReverseDns *string `json:"reverse_dns"` + Scores struct { + LastDay *struct { + // Aggressiveness Last day aggressiveness score + Aggressiveness *float32 `json:"aggressiveness,omitempty"` + + // Anomaly Last day anomaly score + Anomaly *float32 `json:"anomaly,omitempty"` + + // Threat Last day threat score + Threat *float32 `json:"threat,omitempty"` + + // Total Last day score + Total *float32 `json:"total,omitempty"` + + // Trust Last day trust score + Trust *float32 `json:"trust,omitempty"` + } `json:"last_day,omitempty"` + LastMonth *struct { + // Aggressiveness Last month aggressiveness score + Aggressiveness *float32 `json:"aggressiveness,omitempty"` + + // Anomaly Last month anomaly score + Anomaly *float32 `json:"anomaly,omitempty"` + + // Threat Last month threat score + Threat *float32 `json:"threat,omitempty"` + + // Total Last month score + Total *float32 `json:"total,omitempty"` + + // Trust Last month trust score + Trust *float32 `json:"trust,omitempty"` + } `json:"last_month,omitempty"` + LastWeek *struct { + // Aggressiveness Last week aggressiveness score + Aggressiveness *float32 `json:"aggressiveness,omitempty"` + + // Anomaly Last week anomaly score + Anomaly *float32 `json:"anomaly,omitempty"` + + // Threat Last week threat score + Threat *float32 `json:"threat,omitempty"` + + // Total Last week score + Total *float32 `json:"total,omitempty"` + + // Trust Last week trust score + Trust *float32 `json:"trust,omitempty"` + } `json:"last_week,omitempty"` + Overall *struct { + // Aggressiveness Overall aggressiveness score + Aggressiveness *float32 `json:"aggressiveness,omitempty"` + + // Anomaly Overall anomaly score + Anomaly *float32 `json:"anomaly,omitempty"` + + // Threat Overall threat score + Threat *float32 `json:"threat,omitempty"` + + // Total Overall score + Total *float32 `json:"total,omitempty"` + + // Trust Overall trust score + Trust *float32 `json:"trust,omitempty"` + } `json:"overall,omitempty"` + } `json:"scores"` + + // State state of the IP in the community blocklist: validated means IP is currently part of community blocklist, refused means it was part of the community blocklist, but was manually purged (ie. false positive) + State *FireIPCTIResponseState `json:"state,omitempty"` + + // TargetCountries The top 10 reports repartition by country about the IP, as a percentage + TargetCountries map[string]interface{} `json:"target_countries"` +} + +// FireIPCTIResponseState state of the IP in the community blocklist: validated means IP is currently part of community blocklist, refused means it was part of the community blocklist, but was manually purged (ie. false positive) +type FireIPCTIResponseState string + +// QueryCTIResponse defines model for QueryCTIResponse. +type QueryCTIResponse = CTIObject + +// SearchCTIResponse defines model for SearchCTIResponse. +type SearchCTIResponse struct { + Items []CTIObject `json:"items"` + NotFound float32 `json:"not_found"` + + // Total IP of the request + Total float32 `json:"total"` +} + +// GetFireParams defines parameters for GetFire. +type GetFireParams struct { + // Page Number of the page to fetch + Page *float32 `form:"page,omitempty" json:"page,omitempty"` + + // Since Filter records updated since - duration in h (hours), d(days), m(minutes) ) + Since *string `form:"since,omitempty" json:"since,omitempty"` +} + +// GetSmokeParams defines parameters for GetSmoke. +type GetSmokeParams struct { + // Ips List of IPs to query, separated by comma + Ips string `form:"ips" json:"ips"` +} diff --git a/pkg/exprhelpers/crowdsec_cti.go b/pkg/exprhelpers/crowdsec_cti.go index 59a239722e39..0b71f682c4db 100644 --- a/pkg/exprhelpers/crowdsec_cti.go +++ b/pkg/exprhelpers/crowdsec_cti.go @@ -1,13 +1,14 @@ package exprhelpers import ( + "context" + "encoding/json" "fmt" "time" "github.com/bluele/gcache" - "github.com/crowdsecurity/crowdsec/pkg/cticlient" + "github.com/crowdsecurity/crowdsec/pkg/cti" "github.com/crowdsecurity/crowdsec/pkg/types" - "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -22,12 +23,14 @@ var CTIApiEnabled = false var CTIBackOffUntil time.Time var CTIBackOffDuration time.Duration = 5 * time.Minute -var ctiClient *cticlient.CrowdsecCTIClient +var ctiClient *cti.ClientWithResponses +var ctiLogger *log.Entry func InitCrowdsecCTI(Key *string, TTL *time.Duration, Size *int, LogLevel *log.Level) error { + var err error if Key == nil || *Key == "" { log.Warningf("CTI API key not set or empty, CTI will not be available") - return cticlient.ErrDisabled + return cti.ErrDisabled } CTIApiKey = *Key if Size == nil { @@ -40,7 +43,7 @@ func InitCrowdsecCTI(Key *string, TTL *time.Duration, Size *int, LogLevel *log.L } clog := log.New() if err := types.ConfigureLogger(clog); err != nil { - return errors.Wrap(err, "while configuring datasource logger") + return fmt.Errorf("while configuring datasource logger: %w", err) } if LogLevel != nil { clog.SetLevel(*LogLevel) @@ -49,8 +52,12 @@ func InitCrowdsecCTI(Key *string, TTL *time.Duration, Size *int, LogLevel *log.L "type": "crowdsec-cti", } subLogger := clog.WithFields(customLog) + ctiLogger = subLogger CrowdsecCTIInitCache(*Size, *TTL) - ctiClient = cticlient.NewCrowdsecCTIClient(cticlient.WithAPIKey(CTIApiKey), cticlient.WithLogger(subLogger)) + ctiClient, err = cti.NewClientWithResponses("https://cti.api.crowdsec.net/v2/", cti.WithRequestEditorFn(cti.APIKeyInserter(CTIApiKey))) + if err != nil { + return fmt.Errorf("while creating CTI client: %w", err) + } CTIApiEnabled = true return nil } @@ -72,22 +79,22 @@ func CrowdsecCTIInitCache(size int, ttl time.Duration) { CacheExpiration = ttl } -// func CrowdsecCTI(ip string) (*cticlient.SmokeItem, error) { +// func CrowdsecCTI(ip string) (*cti.CTIObject, error) { func CrowdsecCTI(params ...any) (any, error) { var ip string if !CTIApiEnabled { - return &cticlient.SmokeItem{}, cticlient.ErrDisabled + return &cti.CTIObject{}, cti.ErrDisabled } var ok bool if ip, ok = params[0].(string); !ok { - return &cticlient.SmokeItem{}, fmt.Errorf("invalid type for ip : %T", params[0]) + return &cti.CTIObject{}, fmt.Errorf("invalid type for ip : %T", params[0]) } if val, err := CTICache.Get(ip); err == nil && val != nil { - ctiClient.Logger.Debugf("cti cache fetch for %s", ip) - ret, ok := val.(*cticlient.SmokeItem) + ctiLogger.Debugf("cti cache fetch for %s", ip) + ret, ok := val.(*cti.CTIObject) if !ok { - ctiClient.Logger.Warningf("CrowdsecCTI: invalid type in cache, removing") + ctiLogger.Warningf("CrowdsecCTI: invalid type in cache, removing") CTICache.Remove(ip) } else { return ret, nil @@ -96,35 +103,41 @@ func CrowdsecCTI(params ...any) (any, error) { if !CTIBackOffUntil.IsZero() && time.Now().Before(CTIBackOffUntil) { //ctiClient.Logger.Warningf("Crowdsec CTI client is in backoff mode, ending in %s", time.Until(CTIBackOffUntil)) - return &cticlient.SmokeItem{}, cticlient.ErrLimit + return &cti.CTIObject{}, cti.ErrLimit } - ctiClient.Logger.Infof("cti call for %s", ip) + ctiLogger.Infof("cti call for %s", ip) before := time.Now() - ctiResp, err := ctiClient.GetIPInfo(ip) - ctiClient.Logger.Debugf("request for %s took %v", ip, time.Since(before)) + ctx := context.Background() + ctiResp, err := ctiClient.GetSmokeIpWithResponse(ctx, ip) + ctiLogger.Debugf("request for %s took %v", ip, time.Since(before)) if err != nil { switch { - case errors.Is(err, cticlient.ErrUnauthorized): + case ctiResp.HTTPResponse != nil && ctiResp.HTTPResponse.StatusCode == 403: CTIApiEnabled = false - ctiClient.Logger.Errorf("Invalid API key provided, disabling CTI API") - return &cticlient.SmokeItem{}, cticlient.ErrUnauthorized - case errors.Is(err, cticlient.ErrLimit): + ctiLogger.Errorf("Invalid API key provided, disabling CTI API") + return &cti.CTIObject{}, cti.ErrUnauthorized + case ctiResp.HTTPResponse != nil && ctiResp.HTTPResponse.StatusCode == 429: CTIBackOffUntil = time.Now().Add(CTIBackOffDuration) - ctiClient.Logger.Errorf("CTI API is throttled, will try again in %s", CTIBackOffDuration) - return &cticlient.SmokeItem{}, cticlient.ErrLimit + ctiLogger.Errorf("CTI API is throttled, will try again in %s", CTIBackOffDuration) + return &cti.CTIObject{}, cti.ErrLimit default: - ctiClient.Logger.Warnf("CTI API error : %s", err) - return &cticlient.SmokeItem{}, fmt.Errorf("unexpected error : %v", err) + ctiLogger.Warnf("CTI API error : %s", err) + return &cti.CTIObject{}, fmt.Errorf("unexpected error : %v", err) } } if err := CTICache.SetWithExpire(ip, ctiResp, CacheExpiration); err != nil { - ctiClient.Logger.Warningf("IpCTI : error while caching CTI : %s", err) - return &cticlient.SmokeItem{}, cticlient.ErrUnknown + ctiLogger.Warningf("IpCTI : error while caching CTI : %s", err) + return &cti.CTIObject{}, cti.ErrUnknown } - ctiClient.Logger.Tracef("CTI response : %v", *ctiResp) + ctiLogger.Tracef("CTI response: %v", *ctiResp) + + var ctiObject cti.CTIObject + if err := json.Unmarshal(ctiResp.Body, &ctiObject); err != nil { + return &cti.CTIObject{}, fmt.Errorf("while unmarshaling CTI response: %w", err) + } - return ctiResp, nil + return &ctiObject, nil } diff --git a/pkg/exprhelpers/crowdsec_cti_test.go b/pkg/exprhelpers/crowdsec_cti_test.go index fc3a236c5616..37e118da33ba 100644 --- a/pkg/exprhelpers/crowdsec_cti_test.go +++ b/pkg/exprhelpers/crowdsec_cti_test.go @@ -1,5 +1,7 @@ package exprhelpers +/* + import ( "bytes" "encoding/json" @@ -15,7 +17,7 @@ import ( "github.com/crowdsecurity/go-cs-lib/ptr" - "github.com/crowdsecurity/crowdsec/pkg/cticlient" + "github.com/crowdsecurity/crowdsec/pkg/cti" ) var sampledata = map[string]cticlient.SmokeItem{ @@ -203,3 +205,5 @@ func TestCache(t *testing.T) { assert.Equal(t, 1, CTICache.Len(true)) require.NoError(t, err) } + +*/ diff --git a/pkg/exprhelpers/expr_lib.go b/pkg/exprhelpers/expr_lib.go index db191b84a8d8..b1e51a2d1f94 100644 --- a/pkg/exprhelpers/expr_lib.go +++ b/pkg/exprhelpers/expr_lib.go @@ -3,7 +3,7 @@ package exprhelpers import ( "time" - "github.com/crowdsecurity/crowdsec/pkg/cticlient" + "github.com/crowdsecurity/crowdsec/pkg/cti" ) type exprCustomFunc struct { @@ -17,7 +17,7 @@ var exprFuncs = []exprCustomFunc{ name: "CrowdsecCTI", function: CrowdsecCTI, signature: []interface{}{ - new(func(string) (*cticlient.SmokeItem, error)), + new(func(string) (*cti.CTIObject, error)), }, }, {