[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#841670: lintian: Check udev rules and AppStream metadata



Here are a new set of patches rebased on the current master branch.  The
tag description in the last patch is slightly modified based on feedback
from the appstream maintainer.

Is there any hope this change can land in unstable early enough for the
package maintainers to react to the checks before Stretch?
-- 
Happy hacking
Petter Reinholdtsen
>From 44d2bcab866db48ace8110046eb23fda3911f8c9 Mon Sep 17 00:00:00 2001
From: Petter Reinholdtsen <pere@hungry.com>
Date: Wed, 26 Oct 2016 12:01:16 +0000
Subject: [PATCH 1/3] Added two new test debs udev-rules and appstream-metadata
 for AppStream and udev checks.

---
 .../appstream-metadata-obsolete.metadata.xml       |  2 ++
 .../debian/debian/appstream-metadata.desktop       |  8 ++++++++
 .../debian/debian/appstream-metadata.dirs          |  2 ++
 .../debian/debian/appstream-metadata.metadata.xml  | 22 ++++++++++++++++++++++
 .../debian/debian/appstream-metadata.udev          | 14 ++++++++++++++
 t/tests/appstream-metadata/debian/debian/rules     | 14 ++++++++++++++
 t/tests/appstream-metadata/desc                    |  8 ++++++++
 t/tests/appstream-metadata/tags                    |  6 ++++++
 t/tests/udev-rules/debian/debian/rules             | 13 +++++++++++++
 t/tests/udev-rules/debian/debian/udev-rules.dirs   |  2 ++
 .../debian/debian/udev-rules.metadata.xml          | 22 ++++++++++++++++++++++
 t/tests/udev-rules/debian/debian/udev-rules.udev   | 17 +++++++++++++++++
 t/tests/udev-rules/desc                            |  7 +++++++
 t/tests/udev-rules/tags                            |  4 ++++
 14 files changed, 141 insertions(+)
 create mode 100644 t/tests/appstream-metadata/debian/debian/appstream-metadata-obsolete.metadata.xml
 create mode 100644 t/tests/appstream-metadata/debian/debian/appstream-metadata.desktop
 create mode 100644 t/tests/appstream-metadata/debian/debian/appstream-metadata.dirs
 create mode 100644 t/tests/appstream-metadata/debian/debian/appstream-metadata.metadata.xml
 create mode 100644 t/tests/appstream-metadata/debian/debian/appstream-metadata.udev
 create mode 100644 t/tests/appstream-metadata/debian/debian/rules
 create mode 100644 t/tests/appstream-metadata/desc
 create mode 100644 t/tests/appstream-metadata/tags
 create mode 100644 t/tests/udev-rules/debian/debian/rules
 create mode 100644 t/tests/udev-rules/debian/debian/udev-rules.dirs
 create mode 100644 t/tests/udev-rules/debian/debian/udev-rules.metadata.xml
 create mode 100644 t/tests/udev-rules/debian/debian/udev-rules.udev
 create mode 100644 t/tests/udev-rules/desc
 create mode 100644 t/tests/udev-rules/tags

diff --git a/t/tests/appstream-metadata/debian/debian/appstream-metadata-obsolete.metadata.xml b/t/tests/appstream-metadata/debian/debian/appstream-metadata-obsolete.metadata.xml
new file mode 100644
index 0000000..31d281e
--- /dev/null
+++ b/t/tests/appstream-metadata/debian/debian/appstream-metadata-obsolete.metadata.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<application/>
diff --git a/t/tests/appstream-metadata/debian/debian/appstream-metadata.desktop b/t/tests/appstream-metadata/debian/debian/appstream-metadata.desktop
new file mode 100644
index 0000000..4c91430
--- /dev/null
+++ b/t/tests/appstream-metadata/debian/debian/appstream-metadata.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Version=1.0
+Type=Application
+Name=appstream-udev-data
+Terminal=true
+Exec=true
+Categories=ConsoleOnly;Game;
+Keywords=Text;
diff --git a/t/tests/appstream-metadata/debian/debian/appstream-metadata.dirs b/t/tests/appstream-metadata/debian/debian/appstream-metadata.dirs
new file mode 100644
index 0000000..402f5d5
--- /dev/null
+++ b/t/tests/appstream-metadata/debian/debian/appstream-metadata.dirs
@@ -0,0 +1,2 @@
+usr/share/metainfo
+usr/share/appdata
diff --git a/t/tests/appstream-metadata/debian/debian/appstream-metadata.metadata.xml b/t/tests/appstream-metadata/debian/debian/appstream-metadata.metadata.xml
new file mode 100644
index 0000000..82294c1
--- /dev/null
+++ b/t/tests/appstream-metadata/debian/debian/appstream-metadata.metadata.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<component>
+  <id>appstream-udev-data</id>
+  <metadata_license>MIT</metadata_license>
+  <name>lintian appstream-udev-data</name>
+  <summary>Test AppStream and udev related checks in lintian</summary>
+  <description>
+    <p>
+      This is a test package designed to exercise some feature or tag
+      of Lintian.  It is part of the Lintian test suite and may do
+      very odd things.  It should not be installed like a regular
+      package.  It may be an empty package.
+    </p>
+  </description>
+  <provides>
+    <modalias>usb:v0000p0001d*</modalias>
+    <modalias>usb:v0000p0002d*</modalias>
+    <modalias>usb:v0000p0003d*</modalias>
+    <modalias>usb:v0000p0004d*</modalias>
+    <modalias>usb:v0000p000ad*</modalias>
+  </provides>
+</component>
diff --git a/t/tests/appstream-metadata/debian/debian/appstream-metadata.udev b/t/tests/appstream-metadata/debian/debian/appstream-metadata.udev
new file mode 100644
index 0000000..e2f2841
--- /dev/null
+++ b/t/tests/appstream-metadata/debian/debian/appstream-metadata.udev
@@ -0,0 +1,14 @@
+SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="0000", ATTR{idProduct}=="0001", \
+    MODE="0666"
+
+SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="0000", ATTR{idProduct}=="0002", \
+    MODE="0660", GROUP="plugdev"
+
+SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="0000", ATTR{idProduct}=="0003", \
+    TAG+="uaccess"
+
+SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="0000", ATTR{idProduct}=="0004", \
+    MODE="0660", GROUP="plugdev", TAG+="uaccess"
+
+SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="0000", ATTR{idProduct}=="000a", \
+    ID_TEST_DEVICE="1"
diff --git a/t/tests/appstream-metadata/debian/debian/rules b/t/tests/appstream-metadata/debian/debian/rules
new file mode 100644
index 0000000..5302e21
--- /dev/null
+++ b/t/tests/appstream-metadata/debian/debian/rules
@@ -0,0 +1,14 @@
+#!/usr/bin/make -f
+
+DESTDIR = debian/$(shell dh_listpackages)
+APPSYNC_DIR=$(DESTDIR)/usr/share/metainfo/
+OBSOLETE_APPSYNC_DIR=$(DESTDIR)/usr/share/appdata/
+UDEV_DIR=$(DESTDIR)/lib/udev/rules.d/
+
+%:
+	dh $@
+
+override_dh_install:
+	dh_install
+	install -m 0644 debian/appstream-metadata.metadata.xml $(APPSYNC_DIR)
+	install -m 0644 debian/appstream-metadata-obsolete.metadata.xml $(OBSOLETE_APPSYNC_DIR)
diff --git a/t/tests/appstream-metadata/desc b/t/tests/appstream-metadata/desc
new file mode 100644
index 0000000..2a424af
--- /dev/null
+++ b/t/tests/appstream-metadata/desc
@@ -0,0 +1,8 @@
+Testname: appstream-metadata
+Version: 1.0
+Description: Test AppStream and udev metadata in binary package
+Test-For:
+ appstream-metadata-in-legacy-location
+ appstream-metadata-legacy-format
+ appstream-metadata-missing-modalias-provide
+ appstream-metadata-malformed-modalias-provide
diff --git a/t/tests/appstream-metadata/tags b/t/tests/appstream-metadata/tags
new file mode 100644
index 0000000..03eeba9
--- /dev/null
+++ b/t/tests/appstream-metadata/tags
@@ -0,0 +1,6 @@
+E: appstream-metadata: appstream-metadata-legacy-format usr/share/appdata/appstream-metadata-obsolete.metadata.xml
+W: appstream-metadata: appstream-metadata-in-legacy-location usr/share/appdata/appstream-metadata-obsolete.metadata.xml
+W: appstream-metadata: appstream-metadata-malformed-modalias-provide usr/share/metainfo/appstream-metadata.metadata.xml include non-valid hex digit in USB matching rule 'usb:v0000p000ad*'
+W: appstream-metadata: appstream-metadata-missing-modalias-provide lib/udev/rules.d/60-appstream-metadata.rules match rule usb:v0000p000Ad*
+W: appstream-metadata: udev-rule-missing-uaccess lib/udev/rules.d/60-appstream-metadata.rules:2 user accessible device missing TAG+="uaccess"
+W: appstream-metadata: udev-rule-missing-uaccess lib/udev/rules.d/60-appstream-metadata.rules:5 user accessible device missing TAG+="uaccess"
diff --git a/t/tests/udev-rules/debian/debian/rules b/t/tests/udev-rules/debian/debian/rules
new file mode 100644
index 0000000..1ecb975
--- /dev/null
+++ b/t/tests/udev-rules/debian/debian/rules
@@ -0,0 +1,13 @@
+#!/usr/bin/make -f
+
+DESTDIR = debian/$(shell dh_listpackages)
+APPSYNC_DIR=$(DESTDIR)/usr/share/metainfo/
+UDEV_DIR=$(DESTDIR)/lib/udev/rules.d/
+
+%:
+	dh $@
+
+override_dh_install:
+	dh_install
+	install -m 0644 debian/udev-rules.metadata.xml $(APPSYNC_DIR)
+	ln -s dangling $(UDEV_DIR)/60-dangling-symlink.rules
diff --git a/t/tests/udev-rules/debian/debian/udev-rules.dirs b/t/tests/udev-rules/debian/debian/udev-rules.dirs
new file mode 100644
index 0000000..d0cd146
--- /dev/null
+++ b/t/tests/udev-rules/debian/debian/udev-rules.dirs
@@ -0,0 +1,2 @@
+lib/udev/rules.d
+usr/share/metainfo
diff --git a/t/tests/udev-rules/debian/debian/udev-rules.metadata.xml b/t/tests/udev-rules/debian/debian/udev-rules.metadata.xml
new file mode 100644
index 0000000..2564e98
--- /dev/null
+++ b/t/tests/udev-rules/debian/debian/udev-rules.metadata.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<component>
+  <id>udev-rules</id>
+  <metadata_license>MIT</metadata_license>
+  <name>lintian udev-rules test</name>
+  <summary>Test udev related checks in lintian</summary>
+  <description>
+    <p>
+      This is a test package designed to exercise some feature or tag
+      of Lintian.  It is part of the Lintian test suite and may do
+      very odd things.  It should not be installed like a regular
+      package.  It may be an empty package.
+    </p>
+  </description>
+  <provides>
+    <modalias>usb:v0000p0001d*</modalias>
+    <modalias>usb:v0000p0002d*</modalias>
+    <modalias>usb:v0000p0003d*</modalias>
+    <modalias>usb:v0000p0004d*</modalias>
+    <modalias>usb:v0000p000Ad*</modalias>
+  </provides>
+</component>
diff --git a/t/tests/udev-rules/debian/debian/udev-rules.udev b/t/tests/udev-rules/debian/debian/udev-rules.udev
new file mode 100644
index 0000000..dd3442b
--- /dev/null
+++ b/t/tests/udev-rules/debian/debian/udev-rules.udev
@@ -0,0 +1,17 @@
+SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="0000", ATTR{idProduct}=="0001", \
+    MODE="0666"
+
+SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="0000", ATTR{idProduct}=="0002", \
+    MODE="0660", GROUP="plugdev"
+
+SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="0000", ATTR{idProduct}=="0003", \
+    TAG+="uaccess"
+
+SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="0000", ATTR{idProduct}=="0004", \
+    MODE="0660", GROUP="plugdev", TAG+="uaccess"
+
+ACTION=="add", ATTR{idVendor}=="0000", ATTR{idProduct}=="0005", \
+    MODE="0660", GROUP="plugdev", TAG+="uaccess"
+
+SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="0000", ATTR{idProduct}=="000a", \
+    ID_TEST_DEVICE="1"
diff --git a/t/tests/udev-rules/desc b/t/tests/udev-rules/desc
new file mode 100644
index 0000000..b633996
--- /dev/null
+++ b/t/tests/udev-rules/desc
@@ -0,0 +1,7 @@
+Testname: udev-rules
+Version: 1.0
+Description: Check udev rules for mistakes
+Test-For:
+ udev-rule-unreadable
+ udev-rule-missing-uaccess
+ udev-rule-missing-subsystem
diff --git a/t/tests/udev-rules/tags b/t/tests/udev-rules/tags
new file mode 100644
index 0000000..5798a00
--- /dev/null
+++ b/t/tests/udev-rules/tags
@@ -0,0 +1,4 @@
+E: udev-rules: udev-rule-unreadable lib/udev/rules.d/60-dangling-symlink.rules
+W: udev-rules: udev-rule-missing-subsystem lib/udev/rules.d/60-udev-rules.rules:14 vendor/product matching missing SUBSYSTEM specifier
+W: udev-rules: udev-rule-missing-uaccess lib/udev/rules.d/60-udev-rules.rules:2 user accessible device missing TAG+="uaccess"
+W: udev-rules: udev-rule-missing-uaccess lib/udev/rules.d/60-udev-rules.rules:5 user accessible device missing TAG+="uaccess"
-- 
2.1.4

>From 4d2f58115e1d8772138bc22aac2b9c4bfd5e0c5c Mon Sep 17 00:00:00 2001
From: Petter Reinholdtsen <pere@hungry.com>
Date: Sun, 23 Oct 2016 23:33:22 +0200
Subject: [PATCH 2/3] Added new check for udev rule files.

Mostly check USB rules.
---
 checks/udev.desc         |  32 +++++++++++++
 checks/udev.pm           | 114 +++++++++++++++++++++++++++++++++++++++++++++++
 t/scripts/pod-spelling.t |   2 +-
 3 files changed, 147 insertions(+), 1 deletion(-)
 create mode 100644 checks/udev.desc
 create mode 100644 checks/udev.pm

diff --git a/checks/udev.desc b/checks/udev.desc
new file mode 100644
index 0000000..7ccfdf8
--- /dev/null
+++ b/checks/udev.desc
@@ -0,0 +1,32 @@
+Check-Script: udev
+Author: Petter Reinholdtsen <pere@hungry.com>
+Type: binary
+Needs-Info: unpacked
+Info: This script checks the udev rules for problems.
+
+Tag: udev-rule-unreadable
+Severity: serious
+Certainty: certain
+Ref: https://wiki.debian.org/USB/GadgetSetup
+Info: The udev rule entry should be a file
+ The package contain a non-file in /lib/udev/rules.d/.  The directory
+ should only contain readable files.
+
+Tag: udev-rule-missing-uaccess
+Severity: normal
+Certainty: possible
+Ref: https://wiki.debian.org/USB/GadgetSetup
+Info: The package set up a device for user access without using the
+ uaccess tag.  Some udev rules get the same effect using other markers
+ enabling console user access using rules in
+ /lib/udev/rules.d/70-uaccess.rules.  Others should specify
+ TAG+="uaccess" in the udev rule.
+
+Tag: udev-rule-missing-subsystem
+Severity: normal
+Certainty: possible
+Ref: https://wiki.debian.org/USB/GadgetSetup
+Info: The package matches vendor/product IDs without specifying
+ subsystem.  The vendor/product IDs are subsystem specific.  Matching
+ rules using those should specify subsystem too, for example by using
+ SUBSYSTEM=="usb" at the start of the matching rule.
diff --git a/checks/udev.pm b/checks/udev.pm
new file mode 100644
index 0000000..3aa5e2a
--- /dev/null
+++ b/checks/udev.pm
@@ -0,0 +1,114 @@
+# udev -- lintian check script -*- perl -*-
+
+# Copyright © 2016 Petter Reinholdtsen
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, you can find it on the World Wide
+# Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::udev;
+
+use strict;
+use warnings;
+
+use Lintian::Tags qw(tag);
+
+# Check /lib/udev/rules.d/, detect use of MODE="0666" and use of
+# GROUP="plugdev" without TAG+="uaccess".
+
+sub run {
+    my ($pkg, $type, $info, $proc, $group) = @_;
+    my $rules_dir = $info->index_resolved_path('lib/udev/rules.d/');
+    return unless $rules_dir;
+    foreach my $file ($rules_dir->children) {
+        if (! $file->is_open_ok()) {
+            tag('udev-rule-unreadable', $file);
+            next;
+        }
+        check_udev_rules($file, \&check_rule);
+    }
+    return;
+}
+
+sub check_rule {
+    my ($file, $linenum, $rule) = @_;
+
+    # for USB, if everyone or the plugdev group members are
+    # allowed access, the uaccess tag should be used too.
+    if ($rule =~ m/SUBSYSTEM=="usb"/
+        && ($rule =~ m/GROUP="plugdev"/
+            || $rule =~ m/MODE="0666"/)
+        && $rule !~ m/ENV\{COLOR_MEASUREMENT_DEVICE\}/
+        && $rule !~ m/ENV\{DDC_DEVICE\}/
+        && $rule !~ m/ENV\{ID_CDROM\}/
+        && $rule !~ m/ENV\{ID_FFADO\}/
+        && $rule !~ m/ENV\{ID_GPHOTO2\}/
+        && $rule !~ m/ENV\{ID_HPLIP\}/
+        && $rule !~ m/ENV\{ID_INPUT_JOYSTICK\}/
+        && $rule !~ m/ENV\{ID_MAKER_TOOL\}/
+        && $rule !~ m/ENV\{ID_MEDIA_PLAYER\}/
+        && $rule !~ m/ENV\{ID_PDA\}/
+        && $rule !~ m/ENV\{ID_REMOTE_CONTROL\}/
+        && $rule !~ m/ENV\{ID_SECURITY_TOKEN\}/
+        && $rule !~ m/ENV\{ID_SMARTCARD_READER\}/
+        && $rule !~ m/ENV\{ID_SOFTWARE_RADIO\}/
+        && $rule !~ m/TAG\+="uaccess"/) {
+        tag('udev-rule-missing-uaccess', "$file:$linenum",
+            'user accessible device missing TAG+="uaccess"');
+    }
+
+    # Matching rules mentioning vendor/product should also specify
+    # subsystem, as vendor/product is subsystem specific.
+    if ($rule =~ m/ATTR\{idVendor\}=="[0-9a-fA-F]+"/
+        && $rule =~ m/ATTR\{idProduct\}=="[0-9a-fA-F]*"/
+        && $rule !~ m/SUBSYSTEM=="[^"]+"/ ) {
+        tag('udev-rule-missing-subsystem', "$file:$linenum",
+            'vendor/product matching missing SUBSYSTEM specifier');
+    }
+    return 0;
+}
+
+sub check_udev_rules {
+    my ($file, $check) = @_;
+
+    my $fd = $file->open();
+    my $linenum = 0;
+    my $cont;
+    my $retval = 0;
+    while (<$fd>) {
+        chomp;
+        $linenum++;
+        if (defined $cont) {
+            $_ = $cont . $_;
+            $cont = undef;
+        }
+        if (/^(.*)\\$/) {
+            $cont = $1;
+            next;
+        }
+        next if /^#.*/; # Skip comments
+        $retval |= $check->($file, $linenum, $_);
+    }
+    close($fd);
+    return $retval;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/t/scripts/pod-spelling.t b/t/scripts/pod-spelling.t
index 75787eb..6973488 100755
--- a/t/scripts/pod-spelling.t
+++ b/t/scripts/pod-spelling.t
@@ -121,7 +121,7 @@ hashrefs namespace subdir SIGPIPE SIG blocknumber blocksub readwindow
 REMOVESLASH STAMPFILE TAGNAME TCODE TESTDATA BLOCKSIZE jN
 POSIX t1c2pfb init runtime txt executability writability
 INHANDLE OUTHANDLES UTC timestamp faux tagname READMEs Testname
-debhelper dh buildpackage
+debhelper dh buildpackage uaccess udev
 
 __END__
 
-- 
2.1.4

>From d4989d8539aac47fd3812924baf21c9c2040d0c2 Mon Sep 17 00:00:00 2001
From: Petter Reinholdtsen <pere@hungry.com>
Date: Sun, 23 Oct 2016 23:41:47 +0200
Subject: [PATCH 3/3] Added checks for AppStream metadata and compare the
 metadata to udev rules.

---
 checks/appstream-metadata.desc |  38 ++++++++
 checks/appstream-metadata.pm   | 215 +++++++++++++++++++++++++++++++++++++++++
 t/scripts/pod-spelling.t       |   2 +-
 3 files changed, 254 insertions(+), 1 deletion(-)
 create mode 100644 checks/appstream-metadata.desc
 create mode 100644 checks/appstream-metadata.pm

diff --git a/checks/appstream-metadata.desc b/checks/appstream-metadata.desc
new file mode 100644
index 0000000..84673cd
--- /dev/null
+++ b/checks/appstream-metadata.desc
@@ -0,0 +1,38 @@
+Check-Script: appstream-metadata
+Author: Petter Reinholdtsen <pere@hungry.com>
+Type: binary
+Needs-Info: unpacked
+Info: This script checks the AppStream metadata files for problems.
+
+Tag: appstream-metadata-in-legacy-location
+Severity: minor
+Certainty: certain
+Ref: https://wiki.debian.org/AppStream/Guidelines
+Info: AppStream metadata file was found in /usr/share/appdata/.  The
+ AppStream XML files should be placed in /usr/share/metainfo/.
+
+Tag: appstream-metadata-legacy-format
+Severity: important
+Certainty: certain
+Ref: https://wiki.debian.org/AppStream/Guidelines
+Info: AppStream metadata with obsolete <application> root node found.
+ This indicate a legacy format.  The metadata should follow the format
+ outlined at https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html#sect-Metadata-GenericComponent .
+ It is possible to validate the format using 'appstreamcli validate'.
+
+Tag: appstream-metadata-missing-modalias-provide
+Severity: normal
+Certainty: certain
+Ref: https://wiki.debian.org/AppStream/Guidelines
+Info: This package contain a udev rule for providing device access to
+ the console user (using the uaccess udev TAG) or to members of the
+ plugdev file group without announcing the hardware support using
+ AppStream.
+
+Tag: appstream-metadata-malformed-modalias-provide
+Severity: normal
+Certainty: certain
+Ref: https://wiki.debian.org/AppStream/Guidelines
+Info: The modalias matching rule in the AppStream metadata file is
+ malformed.  Hexadecimal numbers in vendor and product IDs must be
+ upper case.
diff --git a/checks/appstream-metadata.pm b/checks/appstream-metadata.pm
new file mode 100644
index 0000000..af65354
--- /dev/null
+++ b/checks/appstream-metadata.pm
@@ -0,0 +1,215 @@
+# appstream-metadata -- lintian check script -*- perl -*-
+
+# Copyright © 2016 Petter Reinholdtsen
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, you can find it on the World Wide
+# Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::appstream_metadata;
+
+# For .desktop files, the lintian check would be really easy: Check if
+# .desktop file is there, check if matching file exists in
+# /usr/share/metainfo, if not throw a warning. Maybe while we're at it
+# also check for legacy locations (stuff in /usr/share/appdata) and
+# legacy data (metainfo files starting with `<application>`).
+#
+# For modaliases, maybe udev rules could give some hints.
+# Check modalias values to ensure hex numbers are using capital A-F.
+
+use strict;
+use warnings;
+
+use XML::Simple qw(:strict);
+use File::Basename qw(basename);
+
+use Lintian::Tags qw(tag);
+
+sub run {
+    my ($pkg, $type, $info, $proc, $group) = @_;
+
+    my %desktopfiles;
+    my %metainfo;
+    my @udevrules;
+    my $found_modalias = 0;
+    my $modaliases = [];
+    if (defined(my $dir = $info->index_resolved_path('usr/share/applications/'))) {
+        for my $file ($dir->children('breadth-first')) {
+            $desktopfiles{$file} = 1 if ($file->is_file);
+        }
+    }
+    if (defined(my $dir = $info->index_resolved_path('usr/share/metainfo/'))) {
+        for my $file ($dir->children) {
+            if ($file->is_file) {
+                $metainfo{$file} = 1;
+                $found_modalias |= check_modalias($info, $file, $modaliases);
+            }
+        }
+    }
+    if (defined(my $dir = $info->index_resolved_path('usr/share/appdata/'))) {
+        for my $file ($dir->children('breadth-first')) {
+            if ($file->is_file) {
+                tag('appstream-metadata-in-legacy-location', $file);
+                $found_modalias |= check_modalias($info, $file, $modaliases);
+            }
+        }
+    }
+    if (defined(my $dir = $info->index_resolved_path('lib/udev/rules.d/'))) {
+        for my $file ($dir->children('breadth-first')) {
+            push(@udevrules, $file) if ($file->is_file);
+        }
+    }
+
+    for my $udevrule (@udevrules) {
+        if (check_udev_rules($udevrule, \&provides_user_device, $modaliases)
+            && !$found_modalias) {
+            tag('appstream-metadata-missing-modalias-provide', $udevrule);
+        }
+    }
+    return;
+}
+
+=head1 NAME
+
+check_modalias
+
+=head1 DESCRIPTION
+
+Check if a AppStream XML file contain a provides->modalias block and
+check its content.
+
+=cut
+sub check_modalias {
+    my ($info, $metadatafile, $modaliases) = @_;
+#    my $metadatafile = $info->index_resolved_path($metadatapath);
+    if (! $metadatafile->is_open_ok()) {
+        # FIXME report this as an error
+        return;
+    }
+    my $xml = XMLin($metadatafile->fs_path(),
+                    ForceArray => [ 'provides', 'modalias' ],
+                    KeepRoot => 1,
+                    KeyAttr => [],
+        );
+    if (exists $xml->{'application'}) {
+        tag('appstream-metadata-legacy-format', $metadatafile);
+        return 0;
+    }
+    if (exists $xml->{'component'}
+        && exists $xml->{'component'}->{'provides'}
+        && exists $xml->{'component'}->{'provides'}[0]->{'modalias'}) {
+        for (@{$xml->{'component'}->{'provides'}[0]->{'modalias'}}) {
+            push(@{$modaliases}, $_);
+            if (m/^usb:v[0-9a-f]{4}p[0-9a-f]{4}d/i
+                && ! m/^usb:v[0-9A-F]{4}p[0-9A-F]{4}d/) {
+                tag('appstream-metadata-malformed-modalias-provide',
+                    $metadatafile,
+                    "include non-valid hex digit in USB matching rule '$_'"
+                    );
+            }
+        }
+        return 1;
+    }
+    return 0;
+}
+
+=head1 NAME
+
+provides_user_device
+
+=head1 DESCRIPTION
+
+Check if a udev rule file contain rules for plugdev or uaccess.
+
+=cut
+sub provides_user_device {
+    my ($udevrulefile, $linenum, $rule, $data) = @_;
+    my $retval = 0;
+    if (
+        m/plugdev/
+        || m/uaccess/
+        || m/MODE=\"0666\"/
+        ) {
+        $retval = 1;
+    }
+    if ($rule =~ m/SUBSYSTEM=="usb"/) {
+        my ($vmatch, $pmatch);
+        if ($rule =~ m/ATTR\{idVendor\}=="([0-9a-fA-F]{4})"/) {
+            $vmatch = "v" . uc($1);
+        }
+        if ($rule =~ m/ATTR\{idProduct\}=="([0-9a-fA-F]{4})"/) {
+            $pmatch = "p" . uc($1);
+        }
+        if (defined $vmatch && defined $pmatch) {
+            my $match = "usb:${vmatch}${pmatch}d";
+            my $foundmatch;
+            for my $aliasmatch (@{$data}) {
+                if (0 == index($aliasmatch, $match)) {
+                    $foundmatch = 1;
+                }
+            }
+            if (! $foundmatch) {
+                tag('appstream-metadata-missing-modalias-provide',
+                    $udevrulefile, "match rule $match*");
+            }
+        }
+    }
+    return $retval;
+}
+
+=head1 NAME
+
+check_udev_rules
+
+=head1 DESCRIPTION
+
+Extract all udev rules from a rule file and pass them on one by one to
+the callback function.
+
+This is a copy of a function in udev.pm.
+
+=cut
+sub check_udev_rules {
+    my ($file, $check, $data) = @_;
+
+    my $fd = $file->open();
+    my $linenum = 0;
+    my $cont;
+    my $retval = 0;
+    while (<$fd>) {
+        chomp;
+        $linenum++;
+        if (defined $cont) {
+            $_ = $cont . $_;
+            $cont = undef;
+        }
+        if (/^(.*)\\$/) {
+            $cont = $1;
+            next;
+        }
+        next if /^#.*/; # Skip comments
+        $retval |= $check->($file, $linenum, $_, $data);
+    }
+    close($fd);
+    return $retval;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/t/scripts/pod-spelling.t b/t/scripts/pod-spelling.t
index 6973488..a5b07db 100755
--- a/t/scripts/pod-spelling.t
+++ b/t/scripts/pod-spelling.t
@@ -121,7 +121,7 @@ hashrefs namespace subdir SIGPIPE SIG blocknumber blocksub readwindow
 REMOVESLASH STAMPFILE TAGNAME TCODE TESTDATA BLOCKSIZE jN
 POSIX t1c2pfb init runtime txt executability writability
 INHANDLE OUTHANDLES UTC timestamp faux tagname READMEs Testname
-debhelper dh buildpackage uaccess udev
+debhelper dh buildpackage uaccess udev AppStream plugdev
 
 __END__
 
-- 
2.1.4


Reply to: