[{"id":3669127,"web_url":"http://patchwork.ozlabs.org/comment/3669127/","msgid":"<20260325143448.381493-1-thomas.perale@mind.be>","list_archive_url":null,"date":"2026-03-25T14:34:48","subject":"Re: [Buildroot] [PATCH v3 1/5] support/testing/utils: add basic\n tests for utils/generate-cyclonedx","submitter":{"id":87308,"url":"http://patchwork.ozlabs.org/api/people/87308/","name":"Thomas Perale","email":"thomas.perale@mind.be"},"content":"Hi Martin,\n\nSome last remarks before I approve all those changes. Thanks again for\nre-working your patch series.\n\nIn reply of:\n> Introduce unit-tests for the generate-cyclonedx script, covering basic\n> script invocation, patch CVE extraction and virtual packages.\n> \n> Signed-off-by: Martin Willi <martin@strongswan.org>\n\n> ---\n>  .../tests/utils/test_generate_cyclonedx.py    | 136 ++++++++++++++++++\n>  .../cve_upstream.patch                        |  11 ++\n>  2 files changed, 147 insertions(+)\n>  create mode 100644 support/testing/tests/utils/test_generate_cyclonedx.py\n>  create mode 100644 support/testing/tests/utils/test_generate_cyclonedx/cve_upstream.patch\n> \n> diff --git a/support/testing/tests/utils/test_generate_cyclonedx.py b/support/testing/tests/utils/test_generate_cyclonedx.py\n> new file mode 100644\n> index 000000000000..20f67b48a7a8\n> --- /dev/null\n> +++ b/support/testing/tests/utils/test_generate_cyclonedx.py\n> @@ -0,0 +1,136 @@\n> +\"\"\"Unit tests for utils/generate-cyclonedx.\"\"\"\n> +\n> +import json\n> +import os\n> +import subprocess\n> +import tempfile\n> +import unittest\n> +from pathlib import Path\n> +\n> +import infra\n> +\n> +PATCH = \"support/testing/tests/utils/test_generate_cyclonedx/cve_upstream.patch\"\n> +SCHEMA_LICENSES = [\"MIT\", \"Apache-2.0\", \"GPL-3.0-only\"]\n> +\n> +\n> +class TestGenerateCycloneDX(unittest.TestCase):\n> +    def setUp(self):\n> +        # Provide a fake SPDX schema so the script never hits the network.\n> +        self.schema_dir = tempfile.TemporaryDirectory()\n> +        self.addCleanup(self.schema_dir.cleanup)\n> +\n> +        cyclonedx_dir = Path(self.schema_dir.name) / \"cyclonedx\"\n> +        cyclonedx_dir.mkdir(parents=True)\n> +        schema_path = cyclonedx_dir / \"spdx-1.6.schema.json\"\n> +        schema_path.write_text(json.dumps({\"enum\": SCHEMA_LICENSES}))\n> +\n> +        self.env = os.environ.copy()\n> +        self.env[\"BR2_DL_DIR\"] = self.schema_dir.name\n> +        self.script = infra.basepath(\"utils/generate-cyclonedx\")\n> +        self.cwd = infra.basepath()\n> +\n> +    def _make_show_info(self) -> dict:\n> +        return {\n> +            \"package-foo\": {\n> +                \"name\": \"foo\",\n> +                \"version\": \"1.2\",\n> +                \"type\": \"target\",\n> +                \"virtual\": False,\n> +                \"licenses\": \"MIT\",\n> +                \"cpe-id\": \"cpe:2.3:a:example:foo:1.2:*:*:*:*:*:*:*\",\n> +                \"patches\": [PATCH],\n> +                \"dependencies\": [\"skeleton-baz\", \"package-virtual\", \"package-bar\"],\n> +                \"ignore_cves\": [\"CVE-2025-0001\"],\n> +                \"package_dir\": \"package/package-foo\",\n> +            },\n> +            \"skeleton-baz\": {\n> +                \"name\": \"skeleton-baz\",\n> +                \"version\": \"0.1\",\n> +                \"type\": \"target\",\n> +                \"virtual\": False,\n> +                \"licenses\": \"Apache-2.0\",\n> +                \"dependencies\": [],\n> +                \"package_dir\": \"package/skeleton-baz\",\n> +            },\n> +            \"package-bar\": {\n> +                \"name\": \"bar\",\n> +                \"version\": \"0.2\",\n> +                \"type\": \"target\",\n> +                \"virtual\": False,\n> +                \"licenses\": \"MIT\",\n> +                \"ignore_cves\": [\"CVE-2025-0002\"],\n> +                \"dependencies\": [],\n> +                \"package_dir\": \"package/package-bar\",\n> +            },\n> +            \"host-tool\": {\n> +                \"name\": \"host-tool\",\n> +                \"version\": \"0.3\",\n> +                \"type\": \"host\",\n> +                \"virtual\": False,\n> +                \"licenses\": \"GPL-3.0-only\",\n> +                \"dependencies\": [],\n> +                \"package_dir\": \"package/host-tool\",\n> +            },\n> +            \"package-virtual\": {\n> +                \"name\": \"virtual-provider\",\n> +                \"virtual\": True,\n> +                \"type\": \"target\",\n> +                \"provides\": [\"package-foo\"],\n> +                \"dependencies\": [],\n> +                \"package_dir\": \"package/package-virtual\",\n> +            },\n\nHere I think there is a mistake in the 'virtual' package declaration. The\n\"provides\" property should be present in 'package-foo' instead.\nSee how \"go\" the virtual package for \"go-bin\" is declared when running `make\nshow-info`:\n\n```\n\"host-go\": {\n    \"type\": \"host\",\n    \"name\": \"go\",\n    \"virtual\": true,\n    ...\n    \"dependencies\": [\n        \"host-go-bin\",\n        \"host-skeleton\",\n        \"toolchain\"\n    ],\n    \"reverse_dependencies\": []\n},\n\"host-go-bin\": {\n    \"type\": \"host\",\n    \"name\": \"go-bin\",\n    \"virtual\": false,\n    ...\n    \"provides\": [\n        \"host-go\"\n    ],\n    ...\n    \"dependencies\": [\n        \"host-skeleton\"\n    ],\n    ...\n},\n```\n\nSo in this make show-info example it should be the following if i'm not\nmistaking.\n\n```\n\"package-foo\": {\n    \"name\": \"foo\",\n    ...\n    \"virtual\": False,\n    \"provides\": [\"package-virtual\"],\n    ...\n    \"dependencies\": [\"skeleton-baz\", \"package-bar\"],\n    ...\n},\n\"package-virtual\": {\n    \"name\": \"virtual-provider\",\n    \"virtual\": True,\n    ...\n    \"dependencies\": [\"package-foo\"],\n    ...\n},\n```\n\n> +        }\n> +\n> +    def _run_script(self, extra_args=(), show_info=None):\n> +        data = show_info if show_info is not None else self._make_show_info()\n> +        completed = subprocess.run(\n> +            [self.script, *extra_args],\n> +            cwd=self.cwd,\n> +            env=self.env,\n> +            input=json.dumps(data),\n> +            text=True,\n> +            capture_output=True,\n> +            check=True,\n> +        )\n> +        return json.loads(completed.stdout)\n> +\n> +    def _find_component(self, result: dict, name: str) -> dict:\n> +        for component in result[\"components\"]:\n> +            if component[\"bom-ref\"] == name:\n> +                return component\n> +        self.fail(f\"component {name} missing\")\n> +\n> +    def test_default(self):\n> +        result = self._run_script()\n> +\n> +        self.assertEqual(len(result[\"components\"]), 4)\n> +        self.assertIn(\"vulnerabilities\", result)\n> +        vulnerabilities = {v[\"id\"]: v for v in result[\"vulnerabilities\"]}\n> +        self.assertEqual(len(vulnerabilities), 2)\n> +        self.assertEqual(vulnerabilities[\"CVE-2025-0001\"][\"analysis\"][\"state\"], \"resolved_with_pedigree\")\n> +        self.assertEqual(vulnerabilities[\"CVE-2025-0002\"][\"analysis\"][\"state\"], \"in_triage\")\n> +\n> +        foo = self._find_component(result, \"package-foo\")\n> +        patch = foo[\"pedigree\"][\"patches\"][0]\n> +        self.assertIn(\"text\", patch[\"diff\"])\n> +        self.assertIn(\"content\", patch[\"diff\"][\"text\"])\n> +\n> +        host = self._find_component(result, \"host-tool\")\n> +        self.assertEqual(host[\"properties\"][0][\"value\"], \"host\")\n> +\n> +        names = {c[\"bom-ref\"] for c in result[\"components\"]}\n> +        self.assertIn(\"skeleton-baz\", names)\n> +        self.assertIn(\"package-bar\", names)\n> +        self.assertNotIn(\"package-virtual\", names)\n\nI would add then a test case that depends on the \"package-virtual\" package to\nmake sure the dependency to \"package-foo\" is correctly resolved.\n\n> +\n> +        foo_deps = next(d for d in result[\"dependencies\"] if d[\"ref\"] == \"package-foo\")\n> +        self.assertEqual(foo_deps[\"dependsOn\"], [\"package-bar\", \"skeleton-baz\"])\n> +\n> +    def test_virtual(self):\n> +        result = self._run_script([\"--virtual\"])\n> +\n> +        names = {c[\"bom-ref\"] for c in result[\"components\"]}\n> +        self.assertEqual(names, {\"package-foo\", \"skeleton-baz\", \"host-tool\", \"package-virtual\", \"package-bar\"})\n> +\n> +        foo_deps = next(d for d in result[\"dependencies\"] if d[\"ref\"] == \"package-foo\")\n> +        self.assertEqual(foo_deps[\"dependsOn\"], [\"package-bar\", \"package-virtual\", \"skeleton-baz\"])\n> diff --git a/support/testing/tests/utils/test_generate_cyclonedx/cve_upstream.patch b/support/testing/tests/utils/test_generate_cyclonedx/cve_upstream.patch\n> new file mode 100644\n> index 000000000000..f18e51ebb9ec\n> --- /dev/null\n> +++ b/support/testing/tests/utils/test_generate_cyclonedx/cve_upstream.patch\n> @@ -0,0 +1,11 @@\n> +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001\n> +CVE: CVE-2025-0001\n> +Upstream: https://patches.example/foo.patch\n> +\n> +diff --git a/foo.txt b/foo.txt\n> +index 0000001..0000002 100644\n> +--- a/foo.txt\n> ++++ b/foo.txt\n> +@@\n> +-foo\n> ++bar\n> -- \n> 2.43.0\n> \n> _______________________________________________\n> buildroot mailing list\n> buildroot@buildroot.org\n> https://lists.buildroot.org/mailman/listinfo/buildroot","headers":{"Return-Path":"<buildroot-bounces@buildroot.org>","X-Original-To":["incoming-buildroot@patchwork.ozlabs.org","buildroot@buildroot.org"],"Delivered-To":["patchwork-incoming-buildroot@legolas.ozlabs.org","buildroot@buildroot.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=buildroot.org header.i=@buildroot.org\n header.a=rsa-sha256 header.s=default header.b=b7sumCpn;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=buildroot.org\n (client-ip=2605:bc80:3010::136; helo=smtp3.osuosl.org;\n envelope-from=buildroot-bounces@buildroot.org; receiver=patchwork.ozlabs.org)"],"Received":["from smtp3.osuosl.org (smtp3.osuosl.org [IPv6:2605:bc80:3010::136])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fgqDy3jyCz1xy3\n\tfor <incoming-buildroot@patchwork.ozlabs.org>;\n Thu, 26 Mar 2026 01:34:58 +1100 (AEDT)","from localhost (localhost [127.0.0.1])\n\tby smtp3.osuosl.org (Postfix) with ESMTP id C877861366;\n\tWed, 25 Mar 2026 14:34:56 +0000 (UTC)","from smtp3.osuosl.org ([127.0.0.1])\n by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id yMTFMQyA9k42; Wed, 25 Mar 2026 14:34:55 +0000 (UTC)","from lists1.osuosl.org (lists1.osuosl.org [140.211.166.142])\n\tby smtp3.osuosl.org (Postfix) with ESMTP id 8BBF36136F;\n\tWed, 25 Mar 2026 14:34:55 +0000 (UTC)","from smtp3.osuosl.org (smtp3.osuosl.org [140.211.166.136])\n by lists1.osuosl.org (Postfix) with ESMTP id 2CB081D3\n for <buildroot@buildroot.org>; Wed, 25 Mar 2026 14:34:54 +0000 (UTC)","from localhost (localhost [127.0.0.1])\n by smtp3.osuosl.org (Postfix) with ESMTP id 1D5D06136F\n for <buildroot@buildroot.org>; Wed, 25 Mar 2026 14:34:54 +0000 (UTC)","from smtp3.osuosl.org ([127.0.0.1])\n by localhost (smtp3.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id qe1FWGLogEyI for <buildroot@buildroot.org>;\n Wed, 25 Mar 2026 14:34:52 +0000 (UTC)","from mail-wr1-x430.google.com (mail-wr1-x430.google.com\n [IPv6:2a00:1450:4864:20::430])\n by smtp3.osuosl.org (Postfix) with ESMTPS id 5D4BC61366\n for <buildroot@buildroot.org>; Wed, 25 Mar 2026 14:34:51 +0000 (UTC)","by mail-wr1-x430.google.com with SMTP id\n ffacd0b85a97d-43b467dcf0bso656480f8f.0\n for <buildroot@buildroot.org>; Wed, 25 Mar 2026 07:34:51 -0700 (PDT)","from arch ([79.132.232.220]) by smtp.gmail.com with ESMTPSA id\n ffacd0b85a97d-43b919cefd7sm154618f8f.17.2026.03.25.07.34.48\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Wed, 25 Mar 2026 07:34:49 -0700 (PDT)"],"X-Virus-Scanned":["amavis at osuosl.org","amavis at osuosl.org"],"X-Comment":"SPF check N/A for local connections - client-ip=140.211.166.142;\n helo=lists1.osuosl.org; envelope-from=buildroot-bounces@buildroot.org;\n receiver=<UNKNOWN> ","DKIM-Filter":["OpenDKIM Filter v2.11.0 smtp3.osuosl.org 8BBF36136F","OpenDKIM Filter v2.11.0 smtp3.osuosl.org 5D4BC61366"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=buildroot.org;\n\ts=default; t=1774449295;\n\tbh=6HrQg1MW6Ts1qASJIetyUYlNOs0scYg5oB9nuOl/t3I=;\n\th=To:Cc:Date:In-Reply-To:References:Subject:List-Id:\n\t List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe:\n\t From:Reply-To:From;\n\tb=b7sumCpnon5Lj2N8GiO1d/wtPEd/QVYLA5edNQRYhlBB+GDNn2PkOT/fcX5DcyoTD\n\t ZzAFRcnSvua2X0DMagoKWkOtnZ+vhxGTaqeMtLgNAZr9QqU9DiOVIgE0Cg7AoWZ/KS\n\t Y891q1MMCpvwmDA72iVAZKouCixRZgOldUWfKyLexs48ucSI15KteQLm6VPWnf1sOz\n\t 0+YhUT8/0XerTrjbZzRlbtQfqCdkKqkoryP3Yolx6Kjxvzc/2sOwUo0HaNGH2SrMf/\n\t 1TLdbzFgsb2sJrblKxnptvXy2d/3eYvpIwd5KfNdPKKkHEiCbKvsMo1QaFAJsF3D4M\n\t EWPcSBRZ/a9DQ==","Received-SPF":"Pass (mailfrom) identity=mailfrom;\n client-ip=2a00:1450:4864:20::430; helo=mail-wr1-x430.google.com;\n envelope-from=thomas.perale@essensium.com; receiver=<UNKNOWN>","DMARC-Filter":"OpenDMARC Filter v1.4.2 smtp3.osuosl.org 5D4BC61366","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1774449290; x=1775054090;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=AU+RTuCvezSeSc5cXNw6pdgGpWC2IQQsTb7rCq6Wu3s=;\n b=QIOC+xv0YAKQz5EdO2rbUueXkj8+c+dbOu7k/YlHStvd8gfO4dtN68LNdSk1g1xaYo\n RR29qfdh/4jEjM8WclcgVrmQAEQFvu61I2LvyA7cjtSaBJNgflRG6CFEDjgsL3eIdi9N\n 6bPcPqNztGA6xydlm8Un3VuM0BQks5rd7bv35yWRE48RLnGk0ezDnuVO/ywMZq5jDVGo\n NUr9RHZVTEG6BqotSEXJWK0MK1OXiC9icH+FWEhNs1ufQCcspYa7cv8+Rq19F5JhBFzg\n t5a5mlcNuh1y/VYu8m6dELMdbNmUjchAbqC2ZjQ55Kj0AUUBBe30AFupz+UksrCZx1Dn\n gNJA==","X-Forwarded-Encrypted":"i=1;\n AJvYcCXC5uPsVnsgdEqvVuX+EmSV/Ta3nuDMxbqzEuVFkzCU8pLXRBxd2Mb0YWhwH9vxaeLVGWIqjtJ89y8=@buildroot.org","X-Gm-Message-State":"AOJu0YwzwBjJD0nz3uOGt5BRkGR4HxtBVS6pEJ8dpda0/RGwjEg5K/ct\n IzHc1wt6qs3SVUrK6oxN9E0r7V9VuprEvZzS/FxhMPch2NwyIL4+nziruxxfpON8pcOa1VCfsAC\n B6bQI+1w=","X-Gm-Gg":"ATEYQzw1r3d6sAS8TXmFWcafaLG19ch4A7FSlWRev/RvEr0lHjElnEzRNccaA97XFii\n HvhcF+/d8hTJ6TOQZW9NeLzFkX/dKYONiisayCXKHqxUAm5rUE3xkKoiDUuLfGZDKjzkjQVgTWm\n zBVvUxtflrZVkSJc3cqsmtDotev5CG6bZFa+Lk5JtE9S2BIu9Za8uflTSqEPLrUYPouQDJtQR/x\n QGoyyExQlFATZBNhAyt0C1QOH1rxNinZCEvhME6ofrXRdhy+7p0xwToGZlMnIp44sr5MPIyOTB3\n vn8GuZPxSRKRPVhtTZlv30mnVX156iYp8dnfjYMnbxulF3uFax26yd/0PXi72/VUoapmxLH8Yu2\n Aet9FNpK4pFS+um/t5sQe8TK09uDgI549sfm1DyVopecBybysSWZPAarKGlMHpTpNSU7wN7WHg/\n BX/7ivxnLMAiZ3A15A","X-Received":"by 2002:a05:6000:220b:b0:43b:83a8:25ca with SMTP id\n ffacd0b85a97d-43b83a82639mr9594109f8f.1.1774449289692;\n Wed, 25 Mar 2026 07:34:49 -0700 (PDT)","To":"Martin Willi <martin@strongswan.org>","Cc":"Thomas Perale <thomas.perale@mind.be>,\n\tbuildroot@buildroot.org","Date":"Wed, 25 Mar 2026 15:34:48 +0100","Message-ID":"<20260325143448.381493-1-thomas.perale@mind.be>","X-Mailer":"git-send-email 2.53.0","In-Reply-To":"<20260325133343.1008245-2-martin@strongswan.org>","References":"<20260325133343.1008245-2-martin@strongswan.org>","MIME-Version":"1.0","X-Mailman-Original-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=mind.be; s=google; t=1774449290; x=1775054090; darn=buildroot.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=AU+RTuCvezSeSc5cXNw6pdgGpWC2IQQsTb7rCq6Wu3s=;\n b=if75ucyozJwrNwdmW3Ke4+r+1MoVdWf9iw8aE0EwGhMoF14d3oPvXsfxSNqXQx+sgS\n Vhzq3DwoWHailJI8Oc6sRW0uMKIamfaJCII/FreIKyHlnRbFAzZR0h/ZGnvcVRIRWP7F\n Tjn48P4gm0DOlv74yEQfmm0T9/ayvnMOO6JBYGXc7UbbCkCRkSrZBREcQ1C2H3V8xBLz\n V0dpO79f/yTMBBdLE8NnKe3i/tPMrYcuPasuhUSv1jq1JG8dlRZkwMc8maB6tbHHU8qD\n bGxmnQcrt7mS5qdBZvPSd10jlwBjtPS8/jeEGYX+fuDkD3OsMtZarlP00FHiig5ShJmb\n 8SUw==","X-Mailman-Original-Authentication-Results":["smtp3.osuosl.org;\n dmarc=pass (p=quarantine dis=none)\n header.from=mind.be","smtp3.osuosl.org;\n dkim=pass (2048-bit key) header.d=mind.be header.i=@mind.be\n header.a=rsa-sha256 header.s=google header.b=if75ucyo"],"Subject":"Re: [Buildroot] [PATCH v3 1/5] support/testing/utils: add basic\n tests for utils/generate-cyclonedx","X-BeenThere":"buildroot@buildroot.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"Discussion and development of buildroot <buildroot.buildroot.org>","List-Unsubscribe":"<https://lists.buildroot.org/mailman/options/buildroot>,\n <mailto:buildroot-request@buildroot.org?subject=unsubscribe>","List-Archive":"<http://lists.buildroot.org/pipermail/buildroot/>","List-Post":"<mailto:buildroot@buildroot.org>","List-Help":"<mailto:buildroot-request@buildroot.org?subject=help>","List-Subscribe":"<https://lists.buildroot.org/mailman/listinfo/buildroot>,\n <mailto:buildroot-request@buildroot.org?subject=subscribe>","From":"Thomas Perale via buildroot <buildroot@buildroot.org>","Reply-To":"Thomas Perale <thomas.perale@mind.be>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"buildroot-bounces@buildroot.org","Sender":"\"buildroot\" <buildroot-bounces@buildroot.org>"}}]