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 数组中。

继续:

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 →