Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

カートページのペネトレーションテスト追加 #523

Merged
merged 6 commits into from
Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/penetration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ jobs:
matrix:
group:
- 'test/front_login/contact.test.ts'
- 'test/front_login/cart/cart.test.ts'
- 'test/front_login/cart/cart_delete.test.ts'

steps:
- name: Checkout
Expand All @@ -27,6 +29,9 @@ jobs:
git config --global user.name "$(git --no-pager log --format=format:'%an' -n 1)"
git config --global user.email "$(git --no-pager log --format=format:'%ae' -n 1)"
git am zap/patches/0001-CSRF-OWASP-ZAP.patch
- name: Apply patch to cart_delete
if: matrix.group == 'test/front_login/cart/cart_delete.test.ts'
run: git am zap/patches/0009-cart_delete.patch

- name: Setup environment
run: echo "COMPOSE_FILE=docker-compose.yml:docker-compose.pgsql.yml:docker-compose.dev.yml:docker-compose.owaspzap.yml:docker-compose.owaspzap.daemon.yml" >> $GITHUB_ENV
Expand Down
1 change: 1 addition & 0 deletions docker-compose.owaspzap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ services:
# Use Zap Proxy
HTTP_URL: https://ec-cube/
HTTPS_URL: https://ec-cube/
USE_FILENAME_DIR_INDEX: 1
zap:
build:
context: ./zap
Expand Down
155 changes: 155 additions & 0 deletions e2e-tests/test/front_login/cart/cart.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { test, expect, chromium, Page } from '@playwright/test';
import PlaywrightConfig from '../../../../playwright.config';
import { ZapClient, Mode, ContextType, Risk, HttpMessage } from '../../../utils/ZapClient';
import { intervalRepeater } from '../../../utils/Progress';
const zapClient = new ZapClient();

const url = `${PlaywrightConfig.use.baseURL}/cart/index.php`;

test.describe.serial('カートページのテストをします', () => {
let page: Page;
test.beforeAll(async () => {
await zapClient.setMode(Mode.Protect);
await zapClient.newSession('/zap/wrk/sessions/front_login_cart', true);
await zapClient.importContext(ContextType.FrontLogin);

if (!await zapClient.isForcedUserModeEnabled()) {
await zapClient.setForcedUserModeEnabled();
expect(await zapClient.isForcedUserModeEnabled()).toBeTruthy();
}
const browser = await chromium.launch();
page = await browser.newPage();
await page.goto(url);
});

const detailURL = `${PlaywrightConfig.use.baseURL}/products/detail.php?product_id=1`;
test('商品詳細ページを表示します', async () => {
await page.goto(detailURL);
await expect(page.locator('#detailrightbloc > h2')).toContainText('アイスクリーム');
});

test('商品をカートに入れます', async () => {
await page.selectOption('select[name=classcategory_id1]', { label: '抹茶' });
await page.selectOption('select[name=classcategory_id2]', { label: 'S' });
await page.fill('input[name=quantity]', '2');
await page.click('[alt=カゴに入れる]');
});

test('カートの内容を確認します', async () => {
await expect(page.locator('h2.title')).toContainText('現在のカゴの中');
await expect(page.locator('table[summary=商品情報] >> tr >> nth=1')).toContainText('アイスクリーム');
});

test.describe('テストを実行します[GET] @attack', () => {
let scanId: number;
test('アクティブスキャンを実行します', async () => {
scanId = await zapClient.activeScanAsUser(url, 2, 110, false, null, 'GET');
await intervalRepeater(async () => await zapClient.getActiveScanStatus(scanId), 5000, page);
});

test('結果を確認します', async () => {
await zapClient.getAlerts(url, 0, 1, Risk.High)
.then(alerts => expect(alerts).toEqual([]));
});
});

test('カートの数量を加算します', async () => {
await page.reload();
const quantity = parseInt(await page.locator('table[summary=商品情報] >> tr >> nth=1 >> td >> nth=4').textContent());
await page.click('table[summary=商品情報] >> tr >> nth=1 >> td >> nth=4 >> [alt="+"]');
await expect(page.locator('table[summary=商品情報] >> tr >> nth=1 >> td >> nth=4')).toContainText(String(quantity + 1));
});

test.describe('数量加算のテストを実行します[POST] @attack', () => {

let message: HttpMessage;
let requestBody: string;
test('履歴を取得します', async () => {
const result = await zapClient.getMessages(url, await zapClient.getNumberOfMessages(url) - 1, 1);
message = result.pop();
expect(message.requestBody).toContain('mode=up');
});
test('transactionid を取得し直します', async () => {
await page.goto(url);
const transactionid = await page.locator('input[name=transactionid]').first().inputValue();
requestBody = message.requestBody.replace(/transactionid=[a-z0-9]+/, `transactionid=${transactionid}`);
});

let scanId: number;
test('アクティブスキャンを実行します', async () => {
expect(requestBody).toContain('mode=up');
scanId = await zapClient.activeScanAsUser(url, 2, 110, false, null, 'POST', requestBody);
await intervalRepeater(async () => await zapClient.getActiveScanStatus(scanId), 5000, page);
});

test('結果を確認します', async () => {
await zapClient.getAlerts(url, 0, 1, Risk.High)
.then(alerts => expect(alerts).toEqual([]));
});
});

test('カートの数量を減算します', async () => {
await page.reload();
const quantity = parseInt(await page.locator('table[summary=商品情報] >> tr >> nth=1 >> td >> nth=4').textContent());
await page.click('table[summary=商品情報] >> tr >> nth=1 >> td >> nth=4 >> [alt="-"]');
await expect(page.locator('table[summary=商品情報] >> tr >> nth=1 >> td >> nth=4')).toContainText(String(quantity - 1));
});

test.describe('数量減算のテストを実行します[POST] @attack', () => {

let message: HttpMessage;
let requestBody: string;
test('履歴を取得します', async () => {
const result = await zapClient.getMessages(url, await zapClient.getNumberOfMessages(url) - 1, 1);
message = result.pop();
});
test('transactionid を取得し直します', async () => {
await page.goto(url);
const transactionid = await page.locator('input[name=transactionid]').first().inputValue();
requestBody = message.requestBody.replace(/transactionid=[a-z0-9]+/, `transactionid=${transactionid}`);
});
let manuallyMessage: HttpMessage;
test('数量減算の requestBody に書き換えて手動送信します', async () => {
requestBody = requestBody.replace(/mode=down/, 'mode=down&mode_down=dummy');
await zapClient.sendRequest(message.requestHeader + requestBody);
manuallyMessage = await zapClient.getLastMessage(url);
});
let scanId: number;
test('アクティブスキャンを実行します', async () => {
expect(manuallyMessage.requestBody).toContain('mode=down');
scanId = await zapClient.activeScanAsUser(url, 2, 110, false, null, 'POST', requestBody);
await intervalRepeater(async () => await zapClient.getActiveScanStatus(scanId), 5000, page);
});

test('結果を確認します', async () => {
await zapClient.getAlerts(url, 0, 1, Risk.High)
.then(alerts => expect(alerts).toEqual([]));
});
});

test('購入手続きへ進みます', async () => {
await page.goto(url);
await page.click('input[name=confirm][alt=購入手続きへ]');
await expect(page.locator('h2.title')).toContainText('お届け先の指定');
});

test.describe('購入手続きへ進むテストを実行します[POST] @attack', () => {
let message: HttpMessage;
test('履歴を取得します', async () => {
message = await zapClient.getLastMessage(url);
expect(message.requestHeader).toContain(`POST ${url}`);
expect(message.responseHeader).toContain('HTTP/1.1 302 Found');
});

let scanId: number;
test('アクティブスキャンを実行します', async () => {
scanId = await zapClient.activeScanAsUser(url, 2, 110, false, null, 'POST', message.requestBody);
await intervalRepeater(async () => await zapClient.getActiveScanStatus(scanId), 5000, page);
});

test('結果を確認します', async () => {
await zapClient.getAlerts(url, 0, 1, Risk.High)
.then(alerts => expect(alerts).toEqual([]));
});
});
});
70 changes: 70 additions & 0 deletions e2e-tests/test/front_login/cart/cart_delete.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { test, expect, chromium, Page } from '@playwright/test';
import PlaywrightConfig from '../../../../playwright.config';
import { ZapClient, Mode, ContextType, Risk, HttpMessage } from '../../../utils/ZapClient';
import { intervalRepeater } from '../../../utils/Progress';
const zapClient = new ZapClient();

const url = `${PlaywrightConfig.use.baseURL}/cart/index.php`;

// zap/patches/0009-cart_delete.patch を適用する必要があります
test.describe.serial('カートページのテストをします', () => {
let page: Page;
test.beforeAll(async () => {
await zapClient.setMode(Mode.Protect);
await zapClient.newSession('/zap/wrk/sessions/front_login_contact', true);
await zapClient.importContext(ContextType.FrontLogin);

if (!await zapClient.isForcedUserModeEnabled()) {
await zapClient.setForcedUserModeEnabled();
expect(await zapClient.isForcedUserModeEnabled()).toBeTruthy();
}
const browser = await chromium.launch();
page = await browser.newPage();
await page.goto(url);
});

const detailURL = `${PlaywrightConfig.use.baseURL}/products/detail.php?product_id=1`;
test('商品詳細ページを表示します', async () => {
await page.goto(detailURL);
await expect(page.locator('#detailrightbloc > h2')).toContainText('アイスクリーム');
});

test('商品をカートに入れます', async () => {
await page.selectOption('select[name=classcategory_id1]', { label: '抹茶' });
await page.selectOption('select[name=classcategory_id2]', { label: 'S' });
await page.fill('input[name=quantity]', '2');
await page.click('[alt=カゴに入れる]');
});

test('カートの内容を確認します', async () => {
await expect(page.locator('h2.title')).toContainText('現在のカゴの中');
await expect(page.locator('table[summary=商品情報] >> tr >> nth=1')).toContainText('アイスクリーム');
});

test('カートを削除します', async () => {
page.on('dialog', dialog => dialog.accept());
await page.reload();
await page.click('table[summary=商品情報] >> tr >> nth=1 >> td >> nth=0 >> text=削除');
});

test.describe('カート削除のテストを実行します[POST] @attack', () => {

let message: HttpMessage;
test('履歴を取得します', async () => {
const result = await zapClient.getMessages(url, await zapClient.getNumberOfMessages(url) - 1, 1);
message = result.pop();
});

let scanId: number;
test('アクティブスキャンを実行します', async () => {
expect(message.requestBody).toContain('mode=delete');
scanId = await zapClient.activeScanAsUser(url, 2, 110, false, null, 'POST', message.requestBody);
await intervalRepeater(async () => await zapClient.getActiveScanStatus(scanId), 5000, page);
});

test('結果を確認します', async () => {
await zapClient.getAlerts(url, 0, 1, Risk.High)
.then(alerts => expect(alerts).toEqual([]));
});
});
});
2 changes: 1 addition & 1 deletion html/define.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* true: 使用する, false: 使用しない, null: 自動(Symfony, IIS は true、それ以外は false)
* ※ IIS は、POST 時にファイル名を使用しないと不具合が発生する。(http://support.microsoft.com/kb/247536/ja)
*/
define('USE_FILENAME_DIR_INDEX', null);
define('USE_FILENAME_DIR_INDEX', getenv('USE_FILENAME_DIR_INDEX') ? (bool) getenv('USE_FILENAME_DIR_INDEX') : null);

$autoload = HTML_REALDIR . HTML2DATA_DIR . 'vendor/autoload.php';
if (!file_exists($autoload) && !is_readable($autoload)) {
Expand Down
30 changes: 30 additions & 0 deletions zap/patches/0009-cart_delete.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
From 274562f19542ac7548c75a65677d0e46af89a655 Mon Sep 17 00:00:00 2001
From: Kentaro Ohkouchi <[email protected]>
Date: Fri, 18 Feb 2022 15:56:54 +0900
Subject: [PATCH] =?UTF-8?q?=E3=82=AB=E3=83=BC=E3=83=88=E5=89=8A=E9=99=A4?=
=?UTF-8?q?=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=E3=81=99?=
=?UTF-8?q?=E3=82=8B=E3=83=91=E3=83=83=E3=83=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
data/class/SC_CartSession.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/data/class/SC_CartSession.php b/data/class/SC_CartSession.php
index add3a0555..b022d932f 100644
--- a/data/class/SC_CartSession.php
+++ b/data/class/SC_CartSession.php
@@ -518,7 +518,7 @@ class SC_CartSession
$max = $this->getMax($productTypeId);
for ($i = 0; $i <= $max; $i++) {
if ($this->cartSession[$productTypeId][$i]['cart_no'] == $cart_no) {
- unset($this->cartSession[$productTypeId][$i]);
+ // unset($this->cartSession[$productTypeId][$i]);
}
}
}
--
2.34.1