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 的字符串進行硬編碼。 這些字符串被放置在 ARARTIFS 數組中。

Continuing:

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

eicar.py 將默認斷言持續時間窗口設置為 5 個區塊。 牆上時間內的區塊時間由社區所在者決定。 在 Swarm Technologies 託管的社區中,大約每秒向鏈中添加 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. 隨機選擇 eicarnot_eicar 字符串作為工件
  4. 告訴 polyswarmd 在 IPFS 上託管工件
  5. 指示 polyswarmd 發布懸賞,指定初始懸賞額(允許的最小值)、工件的 URI、斷言窗口持續時間和 chain*

*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))
...

然後,自定義 generate_bounty 方法,以提交 EICAR 和非 EICAR:

    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 →