PolySwarmPolySwarmPolySwarmPolySwarm
Help

Building an EICAR-Submitting Ambassador

EICAR Test File

The EICAR test file is used by the Antivirus industry to test engines' ability to detect malware. The file is not malicious, but is flagged as such by most vendors.

Here we discuss implementing an Ambassador that submits the EICAR test file. Elsewhere, we discuss implementing a Microengine that detects the EICAR test file.


Submitting EICAR

polyswarm-client には、EICAR を送信するアンバサダー (eicar.py) が付属しています。このアンバサダーは、公開分散ファイル共有ネットワークである IPFS でアーティファクトをホストします。

eicar.py begins:

import base64
import logging
import random
import os

from concurrent.futures import CancelledError

from polyswarmartifact import ArtifactType

from polyswarmclient.abstractambassador import AbstractAmbassador

logger = logging.getLogger(__name__)

EICAR = base64.b64decode(
    b'WDVPIVAlQEFQWzRcUFpYNTQoUF4pN0NDKTd9JEVJQ0FSLVNUQU5EQVJELUFOVElWSVJVUy1URVNULUZJTEUhJEgrSCo=')
NOT_EICAR = 'this is not malicious'
ARTIFACTS = [('eicar', EICAR), ('not_eicar', NOT_EICAR)]

インポートとロギングの構成後に、EICAR の文字列と、明確に EICAR ではない文字列がハードコーディングされています。 これらの文字列は、ARTIFACTS 配列に入れられています。

Continuing:

BOUNTY_TEST_DURATION_BLOCKS = int(os.getenv('BOUNTY_TEST_DURATION_BLOCKS', 5))

eicar.py では、デフォルトのアサーション期間を 5 ブロックに設定しています。 実時間のブロック期間は、コミュニティーのホストによって決定されます。 Swarm Technologies がホストしているコミュニティーでは、1 ブロックは約 1 秒ごとにチェーンに追加されます。そのため、5 ブロックの期間は約 5 秒です。 このデフォルト値は、環境変数を使用してオーバーライドできます。

class Ambassador(AbstractAmbassador):
    """Ambassador which submits the EICAR test file"""

    def __init__(self, client, testing=0, chains=None, watchdog=0, submission_rate=30):
        """
        Initialize {{ cookiecutter.participant_name }}
        Args:
            client (`Client`): Client to use
            testing (int): How many test bounties to respond to
            chains (set[str]): Chain(s) to operate on
            watchdog: interval over which a watchdog thread should verify bounty placement on-chain (in number of blocks)
            submission_rate: if nonzero, produce a sleep in the main event loop to prevent the ambassador from overloading `polyswarmd` during testing
        """
        super().__init__(client, testing, chains, watchdog, submission_rate)

eicar.py's Ambassader is built on polyswarm-client's AbstractAmbassador. 特に、AbstractAmbassador は、ホストされている polyswarmd への接続を確立し、client というインスタンス変数でその接続を管理しています。

AbstractAmbassador declares a single method, generate_bounties, as abstract. AbstractAmbassador のサブクラスはすべて、このメソッドを定義する必要があります。

お分かりと思いますが、eicar.py におけるこのメソッドの実装は以下のように非常に単純です。

    async def generate_bounties(self, chain):
        """Submit either the EICAR test string or a benign sample
        Args:
            chain (str): Chain sample is being requested from
        """
        amount = await self.client.bounties.parameters[chain].get('bounty_amount_minimum')

        while True:
            try:
                filename, content = random.choice(ARTIFACTS)

                logger.info('Submitting %s', filename)
                ipfs_uri = await self.client.post_artifacts([(filename, content)])
                if not ipfs_uri:
                    logger.error('Error uploading artifact to IPFS, continuing')
                    continue

                await self.push_bounty(ArtifactType.FILE, amount, ipfs_uri, BOUNTY_TEST_DURATION_BLOCKS, chain)
            except CancelledError:
                logger.info('Cancel requested')
                break
            except Exception:
                logger.exception('Exception in bounty generation task, continuing')
                continue

このメソッドでは、以下 (剰余エラー検査) を実行します。

  1. polyswarmd に最小初期報奨金額を照会します。
  2. 無限ループに入ります。
  3. eicar または not_eicar 文字列をアーティファクトとしてランダムに選択します。
  4. アーティファクトを IPFS でホストするように polyswarmd に指示します。
  5. 初期報奨金額 (許可される最小)、アーティファクトの URI、アサーション期間、chain を指定して、報奨金をポストするように polyswarmd に指示します。*

*chain refers to which blockchain to post the bounty on: "homechain" or "sidechain". This argument should always be side; it will be removed in a future polyswarm-client release.

Notes

  1. polyswarm-client ベースのアンバサダーは、デフォルトでマルチスレッド対応であり、イベントを非同期的に処理します。 この無限ループは、報奨金のポストを担当するスレッドから分離されています。これにより、アンバサダーの残りの部分が正常に機能します。
  2. ループ内に明示的なスリープはありません。 This is intentional; the thread responsible for generate_bounties effectively sleeps while blocking on bounty submission each time it calls self.client.post_artifacts (which in turn blocks on IPFS hosting) and self.client.push_bounty (which blocks on the announcement of the bounty in the marketplace by polyswarmd).

eicar.py is a trivial example that does not account for many real-world ambassador operating concerns.

アンバサダーのカスタマイズ

ここでは、シンプルな最小限の実行可能なアンバサダーを実装し、上述の EICAR アンバサダーを再作成します。

最小限の実行可能なアンバサダーの実装はシンプルであり、Ambassadorgenerate_bounties メソッドを実装するだけです。 This method is found in ambassador_<participant_name_slug>/src/<author_org_slug>_<participant_name_slug>/__init__.py.

Customize __init__.py to include the EICAR and not-EICAR definitions we saw in the EICAR ambassador:

...
logger = logging.getLogger(__name__)

EICAR = base64.b64decode(
    b'WDVPIVAlQEFQWzRcUFpYNTQoUF4pN0NDKTd9JEVJQ0FSLVNUQU5EQVJELUFOVElWSVJVUy1URVNULUZJTEUhJEgrSCo=')
NOT_EICAR = 'this is not malicious'
ARTIFACTS = [('eicar', EICAR), ('not_eicar', NOT_EICAR)]

BOUNTY_TEST_DURATION_BLOCKS = int(os.getenv('BOUNTY_TEST_DURATION_BLOCKS', 5))
...

次に、以下のように、EICAR と非 EICAR を送信するように generate_bounty メソッドをカスタマイズします。

    async def generate_bounties(self, chain):
        """Submit either the EICAR test string or a benign sample
        Args:
            chain (str): Chain sample is being requested from
        """
        amount = await self.client.bounties.parameters[chain].get('bounty_amount_minimum')

        while True:
            try:
                filename, content = random.choice(ARTIFACTS)

                logger.info('Submitting %s', filename)
                ipfs_uri = await self.client.post_artifacts([(filename, content)])
                if not ipfs_uri:
                    logger.error('Error uploading artifact to IPFS, continuing')
                    continue

                await self.push_bounty(ArtifactType.FILE, amount, ipfs_uri, BOUNTY_TEST_DURATION_BLOCKS, chain)
            except CancelledError:
                logger.info('Cancel requested')
                break
            except Exception:
                logger.exception('Exception in bounty generation task, continuing')
                continue

変更を完了すると、participant-template を基に作成された EICAR アンバサダーの完成です。

参加者のテスト

Once everything is in place, let's test our participant:

Unit Testing →

Next Steps

An Ambassador that submits a hardcoded string is an unrealistic Ambassador. In the next section, we create a slightly more realistic Ambassador - one that submits files from a directory on disk.

Continue to File-Submitting Ambassador →