This commit is contained in:
marco 2024-02-01 17:20:50 +01:00
parent 1eab943ec2
commit 4ecefdd849
13 changed files with 2660 additions and 60 deletions

View file

@ -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

View file

@ -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)

23
go.mod
View file

@ -46,7 +46,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
@ -59,10 +59,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
@ -99,11 +100,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.3 // indirect
@ -123,7 +126,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
@ -148,7 +151,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
@ -169,7 +172,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
@ -197,13 +200,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

52
go.sum
View file

@ -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=
@ -251,8 +259,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=
@ -325,8 +333,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=
@ -422,6 +430,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=
@ -436,8 +445,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=
@ -490,8 +500,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=
@ -538,6 +548,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=
@ -554,8 +566,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=
@ -639,6 +651,7 @@ github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyh
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=
@ -737,8 +750,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=
@ -833,7 +846,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=
@ -868,8 +880,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=
@ -898,8 +910,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=
@ -954,6 +967,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=

14
pkg/cti/auth.go Normal file
View file

@ -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
}
}

623
pkg/cti/client.go Normal file
View file

@ -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
}

600
pkg/cti/cti_openapi.yaml Normal file
View file

@ -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

820
pkg/cti/cti_swagger.yaml Normal file
View file

@ -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"

12
pkg/cti/errors.go Normal file
View file

@ -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")
)

481
pkg/cti/types.go Normal file
View file

@ -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"`
}

View file

@ -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)
return ctiResp, nil
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 &ctiObject, nil
}

View file

@ -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)
}
*/

View file

@ -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)),
},
},
{