api_test.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. package csconfig
  2. import (
  3. "fmt"
  4. "net"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "testing"
  9. log "github.com/sirupsen/logrus"
  10. "github.com/stretchr/testify/assert"
  11. "gopkg.in/yaml.v2"
  12. "github.com/crowdsecurity/go-cs-lib/cstest"
  13. "github.com/crowdsecurity/go-cs-lib/ptr"
  14. )
  15. func TestLoadLocalApiClientCfg(t *testing.T) {
  16. tests := []struct {
  17. name string
  18. input *LocalApiClientCfg
  19. expected *ApiCredentialsCfg
  20. expectedErr string
  21. }{
  22. {
  23. name: "basic valid configuration",
  24. input: &LocalApiClientCfg{
  25. CredentialsFilePath: "./tests/lapi-secrets.yaml",
  26. },
  27. expected: &ApiCredentialsCfg{
  28. URL: "http://localhost:8080/",
  29. Login: "test",
  30. Password: "testpassword",
  31. },
  32. },
  33. {
  34. name: "invalid configuration",
  35. input: &LocalApiClientCfg{
  36. CredentialsFilePath: "./tests/bad_lapi-secrets.yaml",
  37. },
  38. expected: &ApiCredentialsCfg{},
  39. expectedErr: "field unknown_key not found in type csconfig.ApiCredentialsCfg",
  40. },
  41. {
  42. name: "invalid configuration filepath",
  43. input: &LocalApiClientCfg{
  44. CredentialsFilePath: "./tests/nonexist_lapi-secrets.yaml",
  45. },
  46. expected: nil,
  47. expectedErr: "open ./tests/nonexist_lapi-secrets.yaml: " + cstest.FileNotFoundMessage,
  48. },
  49. {
  50. name: "valid configuration with insecure skip verify",
  51. input: &LocalApiClientCfg{
  52. CredentialsFilePath: "./tests/lapi-secrets.yaml",
  53. InsecureSkipVerify: ptr.Of(false),
  54. },
  55. expected: &ApiCredentialsCfg{
  56. URL: "http://localhost:8080/",
  57. Login: "test",
  58. Password: "testpassword",
  59. },
  60. },
  61. }
  62. for _, tc := range tests {
  63. tc := tc
  64. t.Run(tc.name, func(t *testing.T) {
  65. err := tc.input.Load()
  66. cstest.RequireErrorContains(t, err, tc.expectedErr)
  67. if tc.expectedErr != "" {
  68. return
  69. }
  70. assert.Equal(t, tc.expected, tc.input.Credentials)
  71. })
  72. }
  73. }
  74. func TestLoadOnlineApiClientCfg(t *testing.T) {
  75. tests := []struct {
  76. name string
  77. input *OnlineApiClientCfg
  78. expected *ApiCredentialsCfg
  79. expectedErr string
  80. }{
  81. {
  82. name: "basic valid configuration",
  83. input: &OnlineApiClientCfg{
  84. CredentialsFilePath: "./tests/online-api-secrets.yaml",
  85. },
  86. expected: &ApiCredentialsCfg{
  87. URL: "http://crowdsec.api",
  88. Login: "test",
  89. Password: "testpassword",
  90. },
  91. },
  92. {
  93. name: "invalid configuration",
  94. input: &OnlineApiClientCfg{
  95. CredentialsFilePath: "./tests/bad_lapi-secrets.yaml",
  96. },
  97. expected: &ApiCredentialsCfg{},
  98. expectedErr: "failed unmarshaling api server credentials",
  99. },
  100. {
  101. name: "missing field configuration",
  102. input: &OnlineApiClientCfg{
  103. CredentialsFilePath: "./tests/bad_online-api-secrets.yaml",
  104. },
  105. expected: nil,
  106. },
  107. {
  108. name: "invalid configuration filepath",
  109. input: &OnlineApiClientCfg{
  110. CredentialsFilePath: "./tests/nonexist_online-api-secrets.yaml",
  111. },
  112. expected: &ApiCredentialsCfg{},
  113. expectedErr: "failed to read api server credentials",
  114. },
  115. }
  116. for _, tc := range tests {
  117. tc := tc
  118. t.Run(tc.name, func(t *testing.T) {
  119. err := tc.input.Load()
  120. cstest.RequireErrorContains(t, err, tc.expectedErr)
  121. if tc.expectedErr != "" {
  122. return
  123. }
  124. assert.Equal(t, tc.expected, tc.input.Credentials)
  125. })
  126. }
  127. }
  128. func TestLoadAPIServer(t *testing.T) {
  129. tmpLAPI := &LocalApiServerCfg{
  130. ProfilesPath: "./tests/profiles.yaml",
  131. }
  132. if err := tmpLAPI.LoadProfiles(); err != nil {
  133. t.Fatalf("loading tmp profiles: %+v", err)
  134. }
  135. LogDirFullPath, err := filepath.Abs("./tests")
  136. if err != nil {
  137. t.Fatal(err)
  138. }
  139. logLevel := log.InfoLevel
  140. config := &Config{}
  141. fcontent, err := os.ReadFile("./tests/config.yaml")
  142. if err != nil {
  143. t.Fatal(err)
  144. }
  145. configData := os.ExpandEnv(string(fcontent))
  146. err = yaml.UnmarshalStrict([]byte(configData), &config)
  147. if err != nil {
  148. t.Fatal(err)
  149. }
  150. tests := []struct {
  151. name string
  152. input *Config
  153. expected *LocalApiServerCfg
  154. expectedErr string
  155. }{
  156. {
  157. name: "basic valid configuration",
  158. input: &Config{
  159. Self: []byte(configData),
  160. API: &APICfg{
  161. Server: &LocalApiServerCfg{
  162. ListenURI: "http://crowdsec.api",
  163. OnlineClient: &OnlineApiClientCfg{
  164. CredentialsFilePath: "./tests/online-api-secrets.yaml",
  165. },
  166. ProfilesPath: "./tests/profiles.yaml",
  167. PapiLogLevel: &logLevel,
  168. },
  169. },
  170. DbConfig: &DatabaseCfg{
  171. Type: "sqlite",
  172. DbPath: "./tests/test.db",
  173. },
  174. Common: &CommonCfg{
  175. LogDir: "./tests/",
  176. LogMedia: "stdout",
  177. },
  178. DisableAPI: false,
  179. },
  180. expected: &LocalApiServerCfg{
  181. Enable: ptr.Of(true),
  182. ListenURI: "http://crowdsec.api",
  183. TLS: nil,
  184. DbConfig: &DatabaseCfg{
  185. DbPath: "./tests/test.db",
  186. Type: "sqlite",
  187. MaxOpenConns: ptr.Of(DEFAULT_MAX_OPEN_CONNS),
  188. },
  189. ConsoleConfigPath: DefaultConfigPath("console.yaml"),
  190. ConsoleConfig: &ConsoleConfig{
  191. ShareManualDecisions: ptr.Of(false),
  192. ShareTaintedScenarios: ptr.Of(true),
  193. ShareCustomScenarios: ptr.Of(true),
  194. ShareContext: ptr.Of(false),
  195. ConsoleManagement: ptr.Of(false),
  196. },
  197. LogDir: LogDirFullPath,
  198. LogMedia: "stdout",
  199. OnlineClient: &OnlineApiClientCfg{
  200. CredentialsFilePath: "./tests/online-api-secrets.yaml",
  201. Credentials: &ApiCredentialsCfg{
  202. URL: "http://crowdsec.api",
  203. Login: "test",
  204. Password: "testpassword",
  205. },
  206. },
  207. Profiles: tmpLAPI.Profiles,
  208. ProfilesPath: "./tests/profiles.yaml",
  209. UseForwardedForHeaders: false,
  210. PapiLogLevel: &logLevel,
  211. },
  212. },
  213. {
  214. name: "basic invalid configuration",
  215. input: &Config{
  216. Self: []byte(configData),
  217. API: &APICfg{
  218. Server: &LocalApiServerCfg{},
  219. },
  220. Common: &CommonCfg{
  221. LogDir: "./tests/",
  222. LogMedia: "stdout",
  223. },
  224. DisableAPI: false,
  225. },
  226. expected: &LocalApiServerCfg{
  227. Enable: ptr.Of(true),
  228. PapiLogLevel: &logLevel,
  229. },
  230. expectedErr: "no database configuration provided",
  231. },
  232. }
  233. for idx, test := range tests {
  234. err := test.input.LoadAPIServer()
  235. if err == nil && test.expectedErr != "" {
  236. fmt.Printf("TEST '%s': NOK\n", test.name)
  237. t.Fatalf("Test number %d/%d expected error, didn't get it", idx+1, len(tests))
  238. } else if test.expectedErr != "" {
  239. fmt.Printf("ERR: %+v\n", err)
  240. if !strings.HasPrefix(fmt.Sprintf("%s", err), test.expectedErr) {
  241. fmt.Printf("TEST '%s': NOK\n", test.name)
  242. t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
  243. test.expectedErr,
  244. fmt.Sprintf("%s", err))
  245. }
  246. assert.Equal(t, test.expected, test.input.API.Server)
  247. }
  248. }
  249. }
  250. func mustParseCIDRNet(s string) *net.IPNet {
  251. _, ipNet, err := net.ParseCIDR(s)
  252. if err != nil {
  253. panic(fmt.Sprintf("MustParseCIDR: parsing %q: %v", s, err))
  254. }
  255. return ipNet
  256. }
  257. func TestParseCapiWhitelists(t *testing.T) {
  258. tests := []struct {
  259. name string
  260. input string
  261. expected *CapiWhitelist
  262. expectedErr string
  263. }{
  264. {
  265. name: "empty file",
  266. input: "",
  267. expected: &CapiWhitelist{
  268. Ips: []net.IP{},
  269. Cidrs: []*net.IPNet{},
  270. },
  271. expectedErr: "empty file",
  272. },
  273. {
  274. name: "empty ip and cidr",
  275. input: `{"ips": [], "cidrs": []}`,
  276. expected: &CapiWhitelist{
  277. Ips: []net.IP{},
  278. Cidrs: []*net.IPNet{},
  279. },
  280. },
  281. {
  282. name: "some ip",
  283. input: `{"ips": ["1.2.3.4"]}`,
  284. expected: &CapiWhitelist{
  285. Ips: []net.IP{net.IPv4(1, 2, 3, 4)},
  286. Cidrs: []*net.IPNet{},
  287. },
  288. },
  289. {
  290. name: "some cidr",
  291. input: `{"cidrs": ["1.2.3.0/24"]}`,
  292. expected: &CapiWhitelist{
  293. Ips: []net.IP{},
  294. Cidrs: []*net.IPNet{mustParseCIDRNet("1.2.3.0/24")},
  295. },
  296. },
  297. }
  298. for _, tc := range tests {
  299. tc := tc
  300. t.Run(tc.name, func(t *testing.T) {
  301. wl, err := parseCapiWhitelists(strings.NewReader(tc.input))
  302. cstest.RequireErrorContains(t, err, tc.expectedErr)
  303. if tc.expectedErr != "" {
  304. return
  305. }
  306. assert.Equal(t, tc.expected, wl)
  307. })
  308. }
  309. }