chore(i18n,learn): processed translations (#44851)

This commit is contained in:
camperbot
2022-01-21 01:00:18 +05:30
committed by GitHub
parent f866718a3d
commit 5c868af2b8
1696 changed files with 159426 additions and 69 deletions

View File

@@ -0,0 +1,393 @@
---
id: 587d824a367417b2b2512c45
title: 匿名メッセージボード
challengeType: 4
forumTopicId: 301568
dashedName: anonymous-message-board
---
# --description--
<https://anonymous-message-board.freecodecamp.rocks/> と同様の機能を持つフルスタック JavaScript アプリを構築してください。
プロジェクトに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
- [GitHub リポジトリ](https://github.com/freeCodeCamp/boilerplate-project-messageboard/)をクローンし、ローカル環境でプロジェクトを完了させる。
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-messageboard)を使用して、プロジェクトを完了させる。
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、`GitHub Link` フィールドでプロジェクトのソースコードへのリンクを送信してください。
# --instructions--
1. テストおよび DB をデータベース接続文字列 (`.env` 内) に書き込む準備ができたときに、引用符なしでテストするように `NODE_ENV` を設定します。
2. `routes/api.js` でコントローラー/ハンドラーを作成し、ルーティングを処理することを推奨します。
3. `server.js` にセキュリティ機能を追加します。
`tests/2_functional-tests.js` に次のテストを記述してください。
- 新しいスレッドを作成する: `/api/threads/{board}` への POST リクエスト
- 3 つの返信を持つ最新のスレッドを 10 個表示する: `/api/threads/{board}` への GET リクエスト
- 間違ったパスワードでスレッドを削除する: 無効な `delete_password` による `/api/threads/{board}` への DELETE リクエスト
- 正しいパスワードでスレッドを削除する: 有効な `delete_password` による `/api/threads/{board}` への DELETE リクエスト
- スレッドを報告する: `/api/threads/{board}` への PUT リクエスト
- 新しい返信を作成する: `/api/replies/{board}` への POST リクエスト
- すべての返信を持つ単一のスレッドを表示する: `/api/replies/{board}` への GET リクエスト
- 間違ったパスワードで返信を削除する: 無効な `delete_password` による `/api/replies/{board}` への DELETE リクエスト
- 正しいパスワードで返信を削除する: 有効な `delete_password` による `/api/replies/{board}` への DELETE リクエスト
- 返信を報告する: `/api/replies/{board}` への PUT リクエスト
# --hints--
サンプルの URL ではなく、自分で作成したプロジェクトを提供することができます。
```js
(getUserInput) => {
assert(
!/.*\/anonymous-message-board\.freecodecamp\.rocks/.test(
getUserInput('url')
)
);
};
```
自分のサイトだけを自分のページの iFrame に読み込めるようにします。
```js
async (getUserInput) => {
const data = await fetch(getUserInput('url') + '/_api/app-info');
const parsed = await data.json();
assert.isTrue(parsed.headers['x-frame-options']?.includes('SAMEORIGIN'));
};
```
DNS プリフェッチを許可しないでください。
```js
async (getUserInput) => {
const data = await fetch(getUserInput('url') + '/_api/app-info');
const parsed = await data.json();
assert.isTrue(parsed.headers['x-dns-prefetch-control']?.includes('off'));
};
```
自分のサイトだけが、自分のページに対するリファラーを送信できるようにします。
```js
async (getUserInput) => {
const data = await fetch(getUserInput('url') + '/_api/app-info');
const parsed = await data.json();
assert.isTrue(parsed.headers['referrer-policy']?.includes('same-origin'));
};
```
`text``delete_password` を含むフォームデータを使用して、`/api/threads/{board}` への POST リクエストを送信できます。 保存されるデータベースレコードは、少なくとも `_id``text``created_on` (日付と時刻)、`bumped_on`(日付と時刻、`created_on` と同じ時刻に開始)、`reported` (ブール値)、`delete_password`、および `replies` (配列) のフィールドを持ちます。
```js
async (getUserInput) => {
const date = new Date();
const text = `fcc_test_${date}`;
const deletePassword = 'delete_me';
const data = { text, delete_password: deletePassword };
const url = getUserInput('url');
const res = await fetch(url + '/api/threads/fcc_test', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (res.ok) {
const checkData = await fetch(url + '/api/threads/fcc_test');
const parsed = await checkData.json();
try {
assert.equal(parsed[0].text, text);
assert.isNotNull(parsed[0]._id);
assert.equal(new Date(parsed[0].created_on).toDateString(), date.toDateString());
assert.equal(parsed[0].bumped_on, parsed[0].created_on);
assert.isArray(parsed[0].replies);
} catch (err) {
throw new Error(err.responseText || err.message);
}
} else {
throw new Error(`${res.status} ${res.statusText}`);
}
};
```
`text``delete_password`、および `thread_id` を含むフォームデータを使用して、`/api/replies/{board}` への POST リクエストを送信できます。 これにより、`bumped_on` の日付がコメントの日付に更新されます。 スレッドの `replies` 配列には、少なくとも `_id``text``created_on``delete_password`、および `reported` のプロパティを持つオブジェクトが保存されます。
```js
async (getUserInput) => {
const url = getUserInput('url');
const body = await fetch(url + '/api/threads/fcc_test');
const thread = await body.json();
const date = new Date();
const text = `fcc_test_reply_${date}`;
const delete_password = 'delete_me';
const thread_id = thread[0]._id;
const replyCount = thread[0].replies.length;
const data = { text, delete_password, thread_id };
const res = await fetch(url + '/api/replies/fcc_test', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (res.ok) {
const checkData = await fetch(`${url}/api/replies/fcc_test?thread_id=${thread_id}`);
const parsed = await checkData.json();
try {
assert.equal(parsed.replies.length, replyCount + 1);
assert.equal(parsed.replies[0].text, text);
assert.equal(parsed._id, thread_id);
assert.equal(parsed.bumped_on, parsed.replies[0].created_on);
} catch (err) {
throw new Error(err.responseText || err.message);
}
} else {
throw new Error(`${res.status} ${res.statusText}`);
}
};
```
`/api/threads/{board}` への GET リクエストを送信できます。 掲示板で上に上げられた最新の 10 個のスレッドと、それぞれに対する最新の 3 個の返信のみの配列が返されます。 `reported` フィールドと `delete_password` フィールドはクライアントに送信されません。
```js
async (getUserInput) => {
const url = getUserInput('url');
const res = await fetch(url + '/api/threads/fcc_test');
if (res.ok) {
const threads = await res.json();
try {
assert.equal(res.status, 200);
assert.isAtMost(threads.length, 10);
for (let i = 0; i < threads.length; i++) {
assert.containsAllKeys(threads[i], ["_id", "text", "created_on", "bumped_on", "replies"]);
assert.isAtMost(threads[i].replies.length, 3);
assert.notExists(threads[i].delete_password);
assert.notExists(threads[i].reported);
for (let j = 0; j < threads[i].replies.length; j++) {
assert.notExists(threads[i].replies[j].delete_password);
assert.notExists(threads[i].replies[j].reported);
}
}
} catch (err) {
throw new Error(err.responseText || err.message);
}
} else {
throw new Error(`${res.status} ${res.statusText}`);
}
};
```
`/api/replies/{board}?thread_id={thread_id}` への GET リクエストを送信できます。 すべての返信を含むスレッド全体のうち、前のテストと同じフィールドをクライアントから除外したものが返されます。
```js
async (getUserInput) => {
const url = getUserInput('url');
let res = await fetch(url + '/api/threads/fcc_test');
const threads = await res.json();
const thread_id = threads[0]._id;
res = await fetch(`${url}/api/replies/fcc_test?thread_id=${thread_id}`);
if (res.ok) {
const thread = await res.json();
try {
assert.equal(res.status, 200);
assert.isObject(thread);
assert.containsAllKeys(thread, ["_id", "text", "created_on", "bumped_on", "replies"]);
assert.isArray(thread.replies);
assert.notExists(thread.delete_password);
assert.notExists(thread.reported);
for (let i = 0; i < thread.replies.length; i++) {
assert.notExists(thread.replies[i].delete_password);
assert.notExists(thread.replies[i].reported);
}
} catch (err) {
throw new Error(err.responseText || err.message);
}
} else {
throw new Error(`${res.status} ${res.statusText}`);
}
};
```
`/api/threads/{board}` への DELETE リクエストを送信し、スレッドを削除するために `thread_id``delete_password` を渡すことができます。 文字列 `incorrect password` または `success` が返されます。
```js
async (getUserInput) => {
const url = getUserInput('url');
let res = await fetch(url + '/api/threads/fcc_test');
const threads = await res.json();
const thread_id = threads[0]._id;
let data = { thread_id, delete_password: "wrong_password" };
const res_invalid = await fetch(url + '/api/threads/fcc_test', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
data = { thread_id, delete_password: "delete_me" };
res = await fetch(url + '/api/threads/fcc_test', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (res.ok) {
const deleted = await res.text();
const not_deleted = await res_invalid.text();
try {
assert.equal(res.status, 200);
assert.equal(deleted, "success");
assert.equal(not_deleted, "incorrect password");
} catch (err) {
throw new Error(err.responseText || err.message);
}
} else {
throw new Error(`${res.status} ${res.statusText}`);
}
};
```
`/api/replies/{board}` への DELETE リクエストを送信し、`thread_id``reply_id`、および `delete_password` を渡すことができます。 文字列 `incorrect password` または `success` が返されます。 実行に成功すると、`reply_id` のテキストが `[deleted]` に変更されます。
```js
async (getUserInput) => {
const url = getUserInput('url');
const thread_data = {
text: "fcc_test_thread",
delete_password: "delete_me",
};
await fetch(`${url}/api/threads/fcc_test`, {
method: "POST",
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(thread_data)
});
let res = await fetch(`${url}/api/threads/fcc_test`);
let threads = await res.json();
const thread_id = threads[0]._id;
const reply_data = { thread_id, text: "fcc_test_reply", delete_password: "delete_me" };
await fetch(`${url}/api/replies/fcc_test`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(reply_data)
});
res = await fetch(`${url}/api/threads/fcc_test`);
threads = await res.json();
const reply_id = threads[0].replies[0]._id;
const data = { thread_id, reply_id, delete_password: "delete_me" };
res = await fetch(url + '/api/replies/fcc_test', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (res.ok) {
const deleted = await res.text();
try {
assert.equal(res.status, 200);
assert.equal(deleted, "success");
res = await fetch(`${url}/api/replies/fcc_test?thread_id=${thread_id}`);
const thread = await res.json();
assert.equal(thread._id, thread_id);
assert.equal(thread.replies[0]._id, reply_id);
assert.equal(thread.replies[0].text, "[deleted]");
} catch (err) {
throw new Error(err.responseText || err.message);
}
} else {
throw new Error(`${res.status} ${res.statusText}`);
}
};
```
`/api/threads/{board}` への PUT リクエストを送信し、`thread_id` を渡すことができます。 文字列 `reported` が返されます。 `thread_id``reported` の値が `true` に変更されます。
```js
async (getUserInput) => {
const url = getUserInput('url');
let res = await fetch(`${url}/api/threads/fcc_test`);
const threads = await res.json();
const report_id = threads[0]._id;
const data = { report_id };
res = await fetch(`${url}/api/threads/fcc_test`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (res.ok) {
const reported = await res.text();
try {
assert.equal(res.status, 200);
assert.equal(reported, "reported");
} catch (err) {
throw new Error(err.responseText || err.message);
}
} else {
throw new Error(`${res.status} ${res.statusText}`);
}
};
```
`/api/replies/{board}` への PUT リクエストを送信し、`thread_id``reply_id` を渡すことができます。 文字列 `reported` が返されます。 `reply_id``reported` の値が `true` に変更されます。
```js
async (getUserInput) => {
const url = getUserInput('url');
let res = await fetch(`${url}/api/threads/fcc_test`);
const threads = await res.json();
const thread_id = threads[0]._id;
const reply_id = threads[0].replies[0]._id;
const data = { thread_id, reply_id };
res = await fetch(`${url}/api/replies/fcc_test`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (res.ok) {
const reported = await res.text();
try {
assert.equal(res.status, 200);
assert.equal(reported, "reported");
} catch (err) {
throw new Error(err.responseText || err.message);
}
} else {
throw new Error(`${res.status} ${res.statusText}`);
}
};
```
10 種類の機能テストがすべて完了して、合格です。
```js
async (getUserInput) => {
const tests = await fetch(getUserInput('url') + '/_api/get-tests');
const parsed = await tests.json();
assert.isTrue(parsed.length >= 10);
parsed.forEach((test) => {
assert.equal(test.state, 'passed');
assert.isAtLeast(test.assertions.length, 1);
});
};
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,95 @@
---
id: 5e46f979ac417301a38fb932
title: ポートスキャナー
challengeType: 10
forumTopicId: 462372
helpCategory: Python
dashedName: port-scanner
---
# --description--
このプロジェクトは [Replit スターターコード](https://replit.com/github/freeCodeCamp/boilerplate-port-scanner)を使用して作業を行います。
Python カリキュラムの対話式教育コンテンツを引き続き開発中です。 現在、下記の freeCodeCamp.org YouTube チャンネルで、このプロジェクトの完了に必要なすべての知識について説明する動画をいくつか公開しています。
- [「みんなで Python」ビデオコース](https://www.freecodecamp.org/news/python-for-everybody/) (14 時間)
- [「Python を学ぶ」ビデオコース](https://www.freecodecamp.org/news/learn-python-video-course/) (10 時間)
# --instructions--
Python を使用してポートスキャンプログラムを作成してください。
`port_scanner.py` ファイルで、`target``port_range` を引数に取る `get_open_ports` という関数を作成してください。 `target` には URL または IP アドレスを指定できます。 `port_range` は、チェック対象のポート範囲の最初と最後を示す 2 つの数値のリストです。
関数呼び出しの例を次に示します。
```py
get_open_ports("209.216.230.240", [440, 445])
get_open_ports("www.stackoverflow.com", [79, 82])
```
この関数は、指定した範囲の中で開いているポートのリストを返す必要があります。
`get_open_ports` 関数はまた、オプションの 3 つ目の引数として、「詳細」モードであることを示す `True` を受け取る必要があります。 これが true に設定されている場合、関数はポートのリストの代わりに説明的な文字列を返す必要があります。
詳細モードで返される文字列の形式は次のとおりです (`{}` で囲まれたテキストは表示すべき情報を示しています)。
```bash
Open ports for {URL} ({IP address})
PORT SERVICE
{port} {service name}
{port} {service name}
```
`common_ports.py` の辞書を使用して、各ポートの正しいサービス名を取得できます。
たとえば、関数を
```py
port_scanner.get_open_ports("scanme.nmap.org", [20, 80], True)
```
ように呼び出した場合は、次を返す必要があります。
```bash
Open ports for scanme.nmap.org (45.33.32.156)
PORT SERVICE
22 ssh
80 http
```
必ず適切なスペースと改行文字を含めてください。
`get_open_ports` 関数に渡された URL が無効な場合、関数は文字列 "Error: Invalid hostname" を返す必要があります。
`get_open_ports` 関数に渡された IP アドレスが無効な場合、関数は文字列 "Error: Invalid IP address" を返す必要があります。
## 開発
`port_scanner.py` にコードを記述してください。 開発には `main.py` を使用してコードをテストすることができます。 「実行」ボタンをクリックすると `main.py` が実行されます。
## テスト
このプロジェクトの単体テストは `test_module.py` にあります。 すでに `test_module.py` から `main.py` にテストをインポートしてあります。 「実行」ボタンを押すと自動的にテストが実行されます。
## 提出
プロジェクトの URL をコピーし、freeCodeCamp に提出してください。
# --hints--
すべての Python テストに合格する必要があります。
```js
```
# --solutions--
```py
# Python challenges don't need solutions,
# because they would need to be tested against a full working project.
# Please check our contributing guidelines to learn more.
```

View File

@@ -0,0 +1,175 @@
---
id: 5e601c775ac9d0ecd8b94aff
title: セキュアなリアルタイムマルチプレイヤーゲーム
challengeType: 4
forumTopicId: 462375
dashedName: secure-real-time-multiplayer-game
---
# --description--
HTML Canvas API と [Socket.io](https://socket.io/) を使用して、<https://secure-real-time-multiplayer-game.freecodecamp.rocks/> と同様の機能を持つ 2D リアルタイムマルチプレイヤーゲームを開発します。 プロジェクトに取り組むにあたり、以下の方法のうち1つを用いてコードを記述します。
- [GitHub リポジトリ](https://github.com/freeCodeCamp/boilerplate-project-secure-real-time-multiplayer-game/)をクローンし、ローカル環境でプロジェクトを完了させる。
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-secure-real-time-multiplayer-game)を使用して、プロジェクトを完了させる。
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、`GitHub Link` フィールドでプロジェクトのソースコードへのリンクを送信してください。
# --instructions--
**メモ**: ユーザーストーリーの実行には `helmet@^3.21.3` が必要です。 このため、ユーザーストーリーを実現するための情報として、Helmet の旧バージョンのドキュメントを参照する必要があります。
# --hints--
サンプルの URL ではなく、自分で作成したプロジェクトを提供することができます。
```js
(getUserInput) => {
assert(
!/.*\/secure-real-time-multiplayer-game\.freecodecamp\.rocks/.test(
getUserInput('url')
)
);
};
```
複数のプレイヤーがサーバーに接続してプレイすることができます。
```js
```
プレイヤーはそれぞれアバターを持ちます。
```js
```
各プレイヤーは、`Player.mjs``Player` クラスによって作成されるオブジェクトにより表現されます。
```js
```
各プレーヤーオブジェクトには、最低限として、固有の `id``score`、プレーヤーの現在の位置を示す `x` および `y` 座標を含める必要があります。
```js
```
ゲームには少なくとも 1 種類のコレクションアイテムがあります。 それを実装するために、`Collectible.mjs``Collectible` クラスを作成してください。
```js
```
`Collectible` クラスにより作成された各コレクションアイテムオブジェクトには、最低限として、固有の `id``value`、アイテムの現在の位置を示す `x` および `y` 座標を含める必要があります。
```js
```
プレイヤーは、WASD キーや矢印キーを使用してアバターを動かすことができます。 それを実装するために、`Player.mjs``movePlayer` メソッドを作成してください。
```js
```
`movePlayer` メソッドは、"up"、"down"、"left"、"right " のいずれかの文字列と、プレーヤーの位置を変更するピクセル量を表す数値の、2 つの引数を受け取る必要があります。 `movePlayer` は、呼び出し元のプレイヤーオブジェクトの `x` 座標と `y` 座標を調整する必要があります。
```js
```
プレーヤーのスコアを使用して、他のプレーヤーとの相対順位を計算する必要があります。 それを実装するために、`Player``calculateRank` メソッドを作成してください。
```js
```
`calculateRank` メソッドは、すべての接続中のプレイヤーを表すオブジェクトの配列を受け取り、文字列 `Rank: currentRanking/totalPlayers` を返す必要があります。 たとえば、プレイヤーが 2 人のゲームで、プレイヤー A のスコアが 3、プレイヤー B のスコアが 5 の場合、プレイヤー A の `calculateRank``Rank: 2/2` を返す必要があります。
```js
```
プレイヤーはコレクションアイテムにぶつかって接触することができます。 それを実装するために、`Player.mjs``collision` メソッドを作成してください。
```js
```
`collision` メソッドは、コレクションアイテムのオブジェクトを引数として受け取る必要があります。 プレイヤーのアバターがアイテムに接触した場合、`collision` メソッドは `true` を返す必要があります。
```js
```
すべてのプレイヤーは同期が保たれます。
```js
```
プレイヤーはいつでもゲームから離れることができます。
```js
```
クライアントによる MIME タイプの推測や参照の試行を防いでください。
```js
async (getUserInput) => {
const data = await fetch(getUserInput('url') + '/_api/app-info');
const parsed = await data.json();
assert.equal(parsed.headers['x-content-type-options'], 'nosniff');
};
```
クロスサイトスクリプティング (XSS) 攻撃を防いでください。
```js
async (getUserInput) => {
const data = await fetch(getUserInput('url') + '/_api/app-info');
const parsed = await data.json();
assert.equal(parsed.headers['x-xss-protection'], '1; mode=block');
};
```
ウェブサイトからクライアントにキャッシュされるものは何もありません。
```js
async (getUserInput) => {
const data = await fetch(getUserInput('url') + '/_api/app-info');
const parsed = await data.json();
assert.equal(parsed.headers['surrogate-control'], 'no-store');
assert.equal(
parsed.headers['cache-control'],
'no-store, no-cache, must-revalidate, proxy-revalidate'
);
assert.equal(parsed.headers['pragma'], 'no-cache');
assert.equal(parsed.headers['expires'], '0');
};
```
ヘッダーには、サイトで "PHP 7.4.3" が使用されていることを記述します。ただし実際には使用されていません (セキュリティ対策が目的です)。
```js
async (getUserInput) => {
const data = await fetch(getUserInput('url') + '/_api/app-info');
const parsed = await data.json();
assert.equal(parsed.headers['x-powered-by'], 'PHP 7.4.3');
};
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@@ -0,0 +1,72 @@
---
id: 5e46f983ac417301a38fb933
title: SHA-1 パスワードクラッカー
challengeType: 10
forumTopicId: 462374
helpCategory: Python
dashedName: sha-1-password-cracker
---
# --description--
このプロジェクトは [Replit スターターコード](https://replit.com/github/freeCodeCamp/boilerplate-SHA-1-password-cracker)を使用して作業を行います。
Python カリキュラムの対話式教育コンテンツを引き続き開発中です。 現在、下記の freeCodeCamp.org YouTube チャンネルで、このプロジェクトの完了に必要なすべての知識について説明する動画をいくつか公開しています。
- [「みんなで Python」ビデオコース](https://www.freecodecamp.org/news/python-for-everybody/) (14 時間)
- [「Python を学ぶ」ビデオコース](https://www.freecodecamp.org/news/learn-python-video-course/) (10 時間)
# --instructions--
パスワードはプレーンテキストで保存すべきではありません。 パスワードリストが暴露された場合に備えて、ハッシュとして保存する必要があります。 ただし、すべてのハッシュが同じように作成されるとは限りません。
このプロジェクトでは、SHA-1 を使用してハッシュ化されたパスワードの解読を試みるパスワードクラックプログラムを作成し、その作業を通じて適切なセキュリティを実現することの重要性を学びます。
パスワードの SHA-1 ハッシュを受け取り、パスワードがすでに使用されている上位 10,000 個のうちの 1 つである場合に、そのパスワードを返す関数を作成してください。 SHA-1 ハッシュがデータベースにあるパスワード「ではない」場合は、"PASSWORD NOT IN DATABASE" を返してください。
関数では、`top-10000-passwords.txt` から得られるそれぞれのパスワードをハッシュし、それらを関数に渡されたハッシュと比較する必要があります。
関数は、`use_salts` というオプションの 2 つ目の引数を受け取る必要があります。 この引数が true に設定されている場合は、`top-10000-passwords.txt` から得られる各パスワードの「前と後ろの両方」に、ファイル `known-salts.txt` から得られる各ソルト文字列を追加したうえで、ハッシュ化を行い、関数に渡されたハッシュと比較する必要があります。
関数のテストに使用できるハッシュ化されたパスワードの例を次に示します。
- `b305921a3723cd5d70a375cd21a61e60aabb84ec` は "sammy123" を返す
- `c7ab388a5ebefbf4d550652f1eb4d833e5316e3e` は "abacab" を返す
- `5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8` は "password" を返す
`use_salts``True` に設定した場合に関数のテストに使用できるハッシュ化されたパスワードの例を次に示します。
- `53d8b3dc9d39f0184144674e310185e41a87ffd5` は "superman" を返す
- `da5a4e8cf89539e66097acd2f8af128acae2f8ae` は "q1w2e3r4t5" を返す
- `ea3f62d498e3b98557f9f9cd0d905028b3b019e1` は "bubbles1" を返す
`hashlib` ライブラリをあらかじめインポートしてあります。 コードでこのライブラリを使用してみてください。 hashlib の詳細については[こちら](https://docs.python.org/3/library/hashlib.html)を参照してください。
## 開発
`password_cracker.py` にコードを記述してください。 開発には `main.py` を使用してコードをテストすることができます。 「実行」ボタンをクリックすると `main.py` が実行されます。
## テスト
このプロジェクトの単体テストは `test_module.py` にあります。 すでに `test_module.py` から `main.py` にテストをインポートしてあります。 「実行」ボタンを押すと自動的にテストが実行されます。
## 提出
プロジェクトの URL をコピーし、freeCodeCamp に提出してください。
# --hints--
すべての Python テストに合格する必要があります。
```js
```
# --solutions--
```py
# Python challenges don't need solutions,
# because they would need to be tested against a full working project.
# Please check our contributing guidelines to learn more.
```

View File

@@ -0,0 +1,136 @@
---
id: 587d824a367417b2b2512c44
title: 株価チェッカー
challengeType: 4
forumTopicId: 301572
dashedName: stock-price-checker
---
# --description--
<https://stock-price-checker.freecodecamp.rocks/> と同様の機能を持つフルスタック JavaScript アプリを構築してください。
信頼できる株価 API の利用にはすべて API キーが必要になるため、ここでは回避策を用意しました。 <https://stock-price-checker-proxy.freecodecamp.rocks/> を使用すれば、登録して自分のキーを取得しなくても最新の株価情報を得ることができます。
プロジェクトに取り組むにあたり、以下の方法のうち 1 つを用いてコードを記述します。
- [ GitHub リポジトリ](https://github.com/freeCodeCamp/boilerplate-project-stockchecker/)をクローンし、ローカル環境でプロジェクトを完了させる。
- [Replit 始動プロジェクト](https://replit.com/github/freeCodeCamp/boilerplate-project-stockchecker)を使用して、プロジェクトを完了させる。
- 使い慣れたサイトビルダーを使用してプロジェクトを完了させる。 必ず GitHub リポジトリのすべてのファイルを取り込む。
完了したら、プロジェクトの動作デモをどこか公開の場にホストしてください。 そして、`Solution Link` フィールドでデモへの URL を送信してください。 必要に応じて、`GitHub Link` フィールドでプロジェクトのソースコードへのリンクを送信してください。
# --instructions--
1. `NODE_ENV` を引用符なしの `test` に設定し、`DB` を MongoDB 接続文字列に設定します。
2. `routes/api.js` でプロジェクトを完成させるか、ハンドラー/コントローラーを作成します。
3. `server.js` にセキュリティ機能を追加します。
4. `tests/2_functional-tests.js` にすべての機能テストを作成します。
**注**: プライバシーに対する注意事項: 1 つの IP に対して 1 つの「いいね」しか受け付けないという条件があるため、IP アドレスを保存する必要があります。 一般データ保護規則などのデータプライバシー関連の法令を遵守することが重要です。 ユーザーのデータを保存するための権限を取得するという方法もありますが、データを匿名化した方がはるかに簡単です。 このチャレンジでは、データベースに保存する前に必ず IP アドレスを匿名化してください。 その方法として、データをハッシュ化する、切り詰める、IP アドレスの一部を 0 にする、などが考えられます。
次のテストを `tests/2_functional-tests.js` に記述してください。
- 1 つの株式を表示: `/api/stock-prices/` への GET リクエスト
- 1 つの株式を表示して「いいね!」をクリック: `/api/stock-prices/` への GET リクエスト
- もう一度同じ株式を表示して「いいね!」をクリック: `/api/stock-prices/` への GET リクエスト
- 2 つの株式を表示: `/api/stock-prices/` への GET リクエスト
- 2 つの株式を表示して「いいね!」をクリック: `/api/stock-prices/` への GET リクエスト
# --hints--
サンプルの URL ではなく、自分で作成したプロジェクトを提供することができます。
```js
(getUserInput) => {
assert(
!/.*\/stock-price-checker\.freecodecamp\.rocks/.test(getUserInput('url'))
);
};
```
コンテンツセキュリティポリシーを設定して、自分のサーバーからのスクリプトや CSS の読み込みのみを許可するようにしてください。
```js
async (getUserInput) => {
const data = await fetch(getUserInput('url') + '/_api/app-info');
const parsed = await data.json();
assert.isTrue(
parsed.headers['content-security-policy'].includes("script-src 'self'")
);
assert.isTrue(
parsed.headers['content-security-policy'].includes("style-src 'self'")
);
};
```
`GET` リクエストを `/api/stock-prices` に送信し、NASDAQ 株式表示記号を `stock` クエリパラメーターに渡すことができます。 返されるオブジェクトには、`stockData` というプロパティが含まれます。
```js
async (getUserInput) => {
const data = await fetch(
getUserInput('url') + '/api/stock-prices?stock=GOOG'
);
const parsed = await data.json();
assert.property(parsed, 'stockData');
};
```
`stockData` プロパティには、文字列としての `stock` 記号、数値としての `price`、数値としての `likes` が含まれています。
```js
async (getUserInput) => {
const data = await fetch(
getUserInput('url') + '/api/stock-prices?stock=GOOG'
);
const parsed = await data.json();
const ticker = parsed.stockData;
assert.typeOf(ticker.price, 'number');
assert.typeOf(ticker.likes, 'number');
assert.typeOf(ticker.stock, 'string');
};
```
また、`like` フィールドに `true` (ブール値) を渡すと、その株式の「いいね!」が増えます。 「いいね」は、1 つの IP につき 1 回のみ受け付ける必要があります。
```js
```
2 つの株式を渡した場合、返される値は 2 つの株式に関する情報を持つ配列となります。 `likes` の代わりに、両方の `stockData` オブジェクトの `rel_likes` (両株式の「いいね!」の差) を表示します。
```js
async (getUserInput) => {
const data = await fetch(
getUserInput('url') + '/api/stock-prices?stock=GOOG&stock=MSFT'
);
const parsed = await data.json();
const ticker = parsed.stockData;
assert.typeOf(ticker, 'array');
assert.property(ticker[0], 'rel_likes');
assert.property(ticker[1], 'rel_likes');
};
```
5 種類の機能テストがすべて完了して、合格です。
```js
async (getUserInput) => {
const tests = await fetch(getUserInput('url') + '/_api/get-tests');
const parsed = await tests.json();
assert.isTrue(parsed.length >= 5);
parsed.forEach((test) => {
assert.equal(test.state, 'passed');
});
};
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```