diff mbox series

[v2] scripts/checklayer: check for SECURITY.md

Message ID 20241113172324.3514858-1-ross.burton@arm.com
State New
Headers show
Series [v2] scripts/checklayer: check for SECURITY.md | expand

Commit Message

Ross Burton Nov. 13, 2024, 5:23 p.m. UTC
Add a check for a SECURITY.md file (or similar) to yocto-check-layer, as
knowing where to report security issues is important.

Signed-off-by: Ross Burton <ross.burton@arm.com>
---
 scripts/lib/checklayer/__init__.py     | 12 +++++++++
 scripts/lib/checklayer/cases/common.py | 34 +++++++++++++++++++++++++-
 2 files changed, 45 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/scripts/lib/checklayer/__init__.py b/scripts/lib/checklayer/__init__.py
index 62ecdfe3906..86aadf39a6b 100644
--- a/scripts/lib/checklayer/__init__.py
+++ b/scripts/lib/checklayer/__init__.py
@@ -452,3 +452,15 @@  def compare_signatures(old_sigs, curr_sigs):
             msg.extend(['      ' + line for line in output.splitlines()])
             msg.append('')
     return '\n'.join(msg)
+
+
+def get_git_toplevel(directory):
+    """
+    Try and find the top of the git repository that directory might be in.
+    Returns the top-level directory, or None.
+    """
+    cmd = ["git", "-C", directory, "rev-parse", "--show-toplevel"]
+    try:
+        return subprocess.check_output(cmd, text=True).strip()
+    except:
+        return None
diff --git a/scripts/lib/checklayer/cases/common.py b/scripts/lib/checklayer/cases/common.py
index 97b16f78c8e..51233de767e 100644
--- a/scripts/lib/checklayer/cases/common.py
+++ b/scripts/lib/checklayer/cases/common.py
@@ -7,7 +7,7 @@  import glob
 import os
 import unittest
 import re
-from checklayer import get_signatures, LayerType, check_command, get_depgraph, compare_signatures
+from checklayer import get_signatures, LayerType, check_command, compare_signatures, get_git_toplevel
 from checklayer.case import OECheckLayerTestCase
 
 class CommonCheckLayer(OECheckLayerTestCase):
@@ -40,6 +40,38 @@  class CommonCheckLayer(OECheckLayerTestCase):
         email_regex = re.compile(r"[^@]+@[^@]+")
         self.assertTrue(email_regex.match(data))
 
+    def find_file_by_name(self, globs):
+        """
+        Utility function to find a file that matches the specified list of
+        globs, in either the layer directory itself or the repository top-level
+        directory.
+        """
+        directories = [self.tc.layer["path"]]
+        toplevel = get_git_toplevel(directories[0])
+        if toplevel:
+            directories.append(toplevel)
+
+        for path in directories:
+            for name in globs:
+                files = glob.glob(os.path.join(path, name))
+                if files:
+                    return sorted(files)[0]
+        return None
+
+    def test_security(self):
+        """
+        Test that the layer has a SECURITY.md (or similar) file, either in the
+        layer itself or at the top of the containing git repository.
+        """
+        if self.tc.layer["type"] == LayerType.CORE:
+            raise unittest.SkipTest("Core layer's SECURITY is top level")
+
+        filename = self.find_file_by_name(("SECURITY", "SECURITY.*"))
+        self.assertTrue(filename, msg="Layer doesn't contain a SECURITY.md file.")
+
+        size = os.path.getsize(filename)
+        self.assertGreater(size, 0, msg=f"{filename} has no content.")
+
     def test_parse(self):
         check_command('Layer %s failed to parse.' % self.tc.layer['name'],
                       'bitbake -p')