Skip to content

Commit ac78065

Browse files
authored
Merge pull request #6 from crowdsecurity/more-tests
test more cases
2 parents 2b9f1cd + 4428c28 commit ac78065

File tree

6 files changed

+173
-46
lines changed

6 files changed

+173
-46
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ vendor/
77
#Tools
88
node_modules/
99
.test-results*
10+
tests/functional/screenshots
1011

1112
# App
1213
.bouncer-key
1314
.cache/
14-
*.log
15+
*.log

inc/admin/advanced-settings.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ function adminAdvancedSettings()
2727
<p>With the stream mode, every decision is retrieved in an asynchronous way. 3 advantages: <br>&nbsp;1) Inivisible latency when loading pages<br>&nbsp;2) The IP verifications works even if your CrowdSec is not reachable.<br>&nbsp;3) The API can never be overloaded by the WordPress traffic</p>
2828
<p>Note: This method has one limit: all the decisions updates since the previous resync will not be taken in account until the next resync.</p>'.
2929
(get_option('crowdsec_stream_mode') ?
30-
'<p><input style="margin-right:10px" type="button" value="Refresh the cache now" class="button button-secondary button-small" onclick="document.getElementById(\'crowdsec_ation_refresh_cache\').submit();"></p>' :
31-
'<p><input style="margin-right:10px" type="button" disabled="disabled" value="Refresh the cache now" class="button button-secondary button-small"></p>'));
30+
'<p><input id="crowdsec_refresh_cache" style="margin-right:10px" type="button" value="Refresh the cache now" class="button button-secondary button-small" onclick="document.getElementById(\'crowdsec_ation_refresh_cache\').submit();"></p>' :
31+
'<p><input id="crowdsec_refresh_cache" style="margin-right:10px" type="button" disabled="disabled" value="Refresh the cache now" class="button button-secondary button-small"></p>'));
3232

3333
// Field "crowdsec_stream_mode_refresh_frequency"
3434
addFieldString('crowdsec_stream_mode_refresh_frequency', 'Resync decisions each<br>(stream mode only)', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_stream_mode', function ($input) {
@@ -61,7 +61,7 @@ function adminAdvancedSettings()
6161
** Section "Cache" **
6262
********************/
6363

64-
add_settings_section('crowdsec_admin_advanced_cache', 'Caching configuration <input style="margin-left: 7px;margin-top: -3px;" type="button" value="Clear now" class="button button-secondary button-small" onclick="if (confirm(\'Are you sure you want to completely clear the cache?\')) document.getElementById(\'crowdsec_ation_clear_cache\').submit();">', function () {
64+
add_settings_section('crowdsec_admin_advanced_cache', 'Caching configuration <input id="crowdsec_clear_cache" style="margin-left: 7px;margin-top: -3px;" type="button" value="Clear now" class="button button-secondary button-small" onclick="if (confirm(\'Are you sure you want to completely clear the cache?\')) document.getElementById(\'crowdsec_ation_clear_cache\').submit();">', function () {
6565
?>
6666
<p>Polish the decisions cache settings by selecting the best technology or the cache durations best suited to your use.</p>
6767
<?php
@@ -141,7 +141,7 @@ function adminAdvancedSettings()
141141

142142
return $input;
143143
}, ((CROWDSEC_CACHE_SYSTEM_PHPFS === get_option('crowdsec_cache_system')) ?
144-
'<input style="margin-right:10px" type="button" value="Prune now" class="button button-secondary" onclick="document.getElementById(\'crowdsec_ation_prune_cache\').submit();">' : '').
144+
'<input style="margin-right:10px" type="button" id="crowdsec_prune_cache" value="Prune now" class="button button-secondary" onclick="document.getElementById(\'crowdsec_ation_prune_cache\').submit();">' : '').
145145
'<p>The File system cache is faster than calling LAPI. Redis or Memcached is faster than the File System cache.</p>', [
146146
CROWDSEC_CACHE_SYSTEM_PHPFS => 'File system',
147147
CROWDSEC_CACHE_SYSTEM_REDIS => 'Redis',
@@ -150,7 +150,7 @@ function adminAdvancedSettings()
150150

151151
// Field "crowdsec_clean_ip_cache_duration"
152152
addFieldString('crowdsec_clean_ip_cache_duration', 'Recheck clean IPs each<br>(live mode only)', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_cache', function ($input) {
153-
if ((int) $input <= 0) {
153+
if (!get_option('crowdsec_stream_mode') && (int) $input <= 0) {
154154
add_settings_error('Recheck clean IPs each', 'crowdsec_error', 'Recheck clean IPs each: Minimum is 1 second.');
155155

156156
return '1';
@@ -161,7 +161,7 @@ function adminAdvancedSettings()
161161

162162
// Field "crowdsec_bad_ip_cache_duration"
163163
addFieldString('crowdsec_bad_ip_cache_duration', 'Recheck bad IPs each<br>(live mode only)', 'crowdsec_plugin_advanced_settings', 'crowdsec_advanced_settings', 'crowdsec_admin_advanced_cache', function ($input) {
164-
if ((int) $input <= 0) {
164+
if (!get_option('crowdsec_stream_mode') && (int) $input <= 0) {
165165
add_settings_error('Recheck bad IPs each', 'crowdsec_error', 'Recheck bad IPs each: Minimum is 1 second.');
166166

167167
return '1';

tests/functional/CustomEnvironment.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const PlaywrightEnvironment = require("jest-playwright-preset/lib/PlaywrightEnvironment")
2+
.default;
3+
4+
class CustomEnvironment extends PlaywrightEnvironment {
5+
async setup() {
6+
await super.setup();
7+
}
8+
9+
async teardown() {
10+
await super.teardown();
11+
}
12+
13+
async handleTestEvent(event) {
14+
if (event.name === "test_done" && event.test.errors.length > 0) {
15+
const parentName = event.test.parent.name.replace(/\W/g, "-");
16+
const specName = event.test.name.replace(/\W/g, "-");
17+
18+
await this.global.page.screenshot({
19+
path: `screenshots/${parentName}_${specName}.png`,
20+
});
21+
}
22+
}
23+
}
24+
25+
module.exports = CustomEnvironment;

tests/functional/__tests__/functional.js

Lines changed: 130 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,23 @@ describe(`Run in Live mode`, () => {
353353
await publicHomepageShouldBeAccessible();
354354
});
355355

356+
it("Should fallback to the selected remedition for unknown remediation", async () => {
357+
await removeAllDecisions();
358+
await addDecision(CLIENT_IP, "mfa", (15 * 60)+"s");
359+
await wait(1000);
360+
await publicHomepageShouldBeCaptchaWall();
361+
await goToAdmin();
362+
await onAdminGoToAdvancedPage();
363+
await page.selectOption(
364+
"[name=crowdsec_fallback_remediation]",
365+
"bypass"
366+
);
367+
await onAdminSaveSettings();
368+
await publicHomepageShouldBeAccessible();
369+
});
370+
356371
it('Should handle X-Forwarded-For header for whitelisted IPs only"', async () => {
372+
await removeAllDecisions();
357373
await banIpForSeconds(OTHER_IP, 15 * 60);
358374

359375
// Should be banned as current IP is not trust by CDN
@@ -372,6 +388,46 @@ describe(`Run in Live mode`, () => {
372388
// // Remove the XFF header for next requests
373389
page.setExtraHTTPHeaders({});
374390
});
391+
392+
it("Should prune the File system cache", async () => {
393+
await goToAdmin();
394+
await onAdminGoToAdvancedPage();
395+
await page.click("#crowdsec_prune_cache");
396+
await waitForNavigation;
397+
398+
await expect(page).toHaveText(
399+
"#wpbody-content > div.wrap > div.notice.notice-success",
400+
"CrowdSec cache has just been pruned."
401+
);
402+
});
403+
404+
it("Should clear the cache on demand", async () => {
405+
await onAdminGoToAdvancedPage();
406+
await onAdminAdvancedSettingsPageSetCleanIpCacheDurationTo(60);
407+
await onAdminAdvancedSettingsPageSetBadIpCacheDurationTo(60);
408+
await onAdminSaveSettings();
409+
await banOwnIpForSeconds(15 * 60);
410+
await publicHomepageShouldBeBanWall();
411+
wait(2000);
412+
await publicHomepageShouldBeBanWall();
413+
await removeAllDecisions();
414+
wait(2000);
415+
await publicHomepageShouldBeBanWall();
416+
417+
await goToAdmin();
418+
await onAdminGoToAdvancedPage();
419+
await page.on('dialog', async (dialog) => {
420+
await dialog.accept();
421+
});
422+
await page.click("#crowdsec_clear_cache");
423+
await waitForNavigation;
424+
425+
await expect(page).toHaveText(
426+
"#wpbody-content > div.wrap > div.notice.notice-success",
427+
"CrowdSec cache has just been cleared."
428+
);
429+
await publicHomepageShouldBeAccessible();
430+
});
375431
});
376432

377433
describe(`Run in Stream mode`, () => {
@@ -398,58 +454,97 @@ describe(`Run in Stream mode`, () => {
398454
await forceCronRun();
399455
await publicHomepageShouldBeAccessible();
400456
});
401-
});
402457

403-
/*
404-
# Public website only
405-
406-
In live mode (1s + 1s), disable "Public website only"
407-
Ban current IP during 5 sec
408-
Try to access admin each 2 sec
409-
The third time admin should be back
410-
Re-enable "Public website only"
411-
412-
# Manually clear the cache
458+
it("Should refresh the cache", async () => {
459+
await goToAdmin();
460+
await onAdminGoToAdvancedPage();
461+
await page.click("#crowdsec_refresh_cache");
462+
await waitForNavigation;
413463

414-
Set cache duration to 1min (clean + bad IP)
415-
Remove all decisions + Ban current IP during 15min
416-
The public page should be forbidden
417-
Remove all decisions
418-
The public page should still be forbidden
419-
Click the "Clear now" button
420-
The public page should be accessible
464+
await expect(page).toHaveText(
465+
"#wpbody-content > div.wrap > div.notice.notice-success",
466+
"The cache has just been refreshed (0 new decision, 0 deleted)."
467+
);
468+
});
469+
});
421470

422-
# Refresh cache button
471+
describe(`Use Redis technology`, () => {
472+
it('Should be able to use Redis cache"', async () => {
473+
notify("Use Redis technology");
423474

424-
(to write)
475+
// TODO (+ bad DSN format, + DSN down)
425476

426-
# Stream mode: Resync decisions each
477+
await goToAdmin();
478+
await onAdminGoToAdvancedPage();
479+
await page.selectOption("[name=crowdsec_cache_system]", "redis");
480+
await wait(200);
481+
await fillInput("crowdsec_redis_dsn", "redis://redis:6379"); // TODO test bad DSN format and test DSN down
482+
await onAdminSaveSettings();
427483

428-
Remove all decisions + Ban current IP during 15min
429-
Set stream mode with 15 seconds resync
430-
Refresh cache
484+
await expect(page).toHaveText(
485+
"#wpbody-content > div.wrap > div.notice.notice-success",
486+
"As the stream mode is enabled, the cache has just been warmed up, there is now 0 decision in cache."
487+
);
431488

432-
(to finish, I'm gonna sleep)
489+
await publicHomepageShouldBeAccessible();
490+
await banOwnIpForSeconds(15 * 60);
491+
await forceCronRun();
492+
await publicHomepageShouldBeBanWall();
493+
await removeAllDecisions();
494+
await forceCronRun();
495+
await publicHomepageShouldBeAccessible();
496+
});
497+
});
433498

434-
# Loop avec Redis / Memcached (+ mauvais format DSN, DSN down)
499+
describe(`Use Memcached technology`, () => {
500+
it('Should be able to use Memcached cache"', async () => {
501+
notify("Use Memcached technology");
435502

436-
(to write)
503+
await goToAdmin();
504+
await onAdminGoToAdvancedPage();
505+
await page.selectOption("[name=crowdsec_cache_system]", "memcached");
506+
await wait(200);
507+
await fillInput(
508+
"crowdsec_memcached_dsn",
509+
"memcached://memcached:11211"
510+
);
437511

438-
# Fallback
512+
// TODO test bad DSN format and test DSN down
439513

440-
(to write)
514+
await onAdminSaveSettings();
515+
await expect(page).toHaveText(
516+
"#wpbody-content > div.wrap > div.notice.notice-success",
517+
"As the stream mode is enabled, the cache has just been warmed up, there is now 0 decision in cache."
518+
);
441519

520+
await publicHomepageShouldBeAccessible();
521+
await banOwnIpForSeconds(15 * 60);
522+
await forceCronRun();
523+
await publicHomepageShouldBeBanWall();
524+
await removeAllDecisions();
525+
await forceCronRun();
526+
await publicHomepageShouldBeAccessible();
527+
});
528+
});
442529

443-
# Recheck clean IP
530+
/*
531+
# Public website only
444532
445-
(to write)
533+
In live mode (1s + 1s), disable "Public website only"
534+
Ban current IP during 5 sec
535+
Try to access admin each 2 sec
536+
The third time admin should be back
537+
Re-enable "Public website only"
446538
447-
# Recheck Bad IP
539+
# Stream mode: Resync decisions each
448540
449-
(to write)
541+
Remove all decisions + Ban current IP during 15min
542+
Set stream mode with 15 seconds resync
543+
Refresh cache
544+
(to finish writing)
450545
451-
# Prune FS Cache
546+
# Recheck clean IP (to write)
452547
453-
(to write)
548+
# Recheck Bad IP (to write)
454549
455550
*/

tests/functional/jest.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
module.exports = {
2-
preset: "jest-playwright-preset"
2+
preset: "jest-playwright-preset",
3+
testEnvironment: "./CustomEnvironment.js"
34
}

tests/functional/utils/watcherClient.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ module.exports.addDecision = async (
9494
const startLongIp = ip2long(startIp);
9595
const endLongIp = ip2long(endIp);
9696
const isRange = startLongIp !== endLongIp;
97-
const scenario = `ban ${
97+
const scenario = `add ${remediation} to ${
9898
isRange ? `range ${startIp} to ${endIp}` : `ip ${startIp}`
9999
} for ${duration} for functional tests`;
100100
const scope = isRange ? "Range" : "Ip";
@@ -133,8 +133,13 @@ module.exports.addDecision = async (
133133
start_at: startAt.toISOString(),
134134
stop_at: stopAt.toISOString(),
135135
},
136-
];
137-
const response = await httpClient.post("/v1/alerts", body);
136+
];
137+
try {
138+
const response = await httpClient.post("/v1/alerts", body);
139+
} catch (error) {
140+
console.log(error.response);
141+
throw new Error(error);
142+
}
138143
}
139144

140145
module.exports.deleteAllDecisions = async () => {

0 commit comments

Comments
 (0)