Repository: google/osv.dev

Issue:
API: incorrectly returns vulnerability for unaffected version
PYSEC-2021-142 is fixed in `pyyaml==5.4`, but the vulnerability is included when querying for 5.4.1:

```
$ curl -sX POST -d '{"version": "5.4.1", "package": {"name": "pyyaml", "ecosystem": "PyPI"}}' "https://api.osv.dev/v1/query" | jq
{
  "vulns": [
    {
      "id": "PYSEC-2021-142",
      "details": "A vulnerability was discovered in the PyYAML library in versions before 5.4, where it is susceptible to arbitrary code execution when it processes untrusted YAML files through the full_load method or with the FullLoader loader. Applications that use the library to process untrusted input may be vulnerable to this flaw. This flaw allows an attacker to execute arbitrary code on the system by abusing the python/object/new constructor. This flaw is due to an incomplete fix for CVE-2020-1747.",
      "aliases": [
        "CVE-2020-14343"
      ],
      "modified": "2021-08-27T03:22:18.913334Z",
      "published": "2021-02-09T21:15:00Z",
      "references": [
        {
          "type": "REPORT",
          "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1860466"
        }
      ],
      "affected": [
        {
          "package": {
            "name": "pyyaml",
            "ecosystem": "PyPI",
            "purl": "pkg:pypi:pyyaml"
          },
          "ranges": [
            {
              "type": "ECOSYSTEM",
              "events": [
                {
                  "introduced": "0"
                },
                {
                  "fixed": "5.4"
                }
              ]
            }
          ],
          "versions": [
            "3.01",
            "3.02",
            "3.03",
            "3.04",
            "3.05",
            "3.06",
            "3.07",
            "3.08",
            "3.09",
            "3.10",
            "3.11",
            "3.12",
            "3.13",
            "3.13b1",
            "3.13rc1",
            "4.2b1",
            "4.2b2",
            "4.2b4",
            "5.1",
            "5.1.1",
            "5.1.2",
            "5.1b1",
            "5.1b3",
            "5.1b5",
            "5.1b7",
            "5.2",
            "5.2b1",
            "5.3",
            "5.3.1",
            "5.3b1",
            "5.4b1",
            "5.4b2"
          ],
          "database_specific": {
            "source": "https://github.com/pypa/advisory-db/blob/main/vulns/pyyaml/PYSEC-2021-142.yaml"
          }
        }
      ]
    }
  ]
}
```


Discussion:

Resolving patch:
diff --git a/gcp/api/server.py b/gcp/api/server.py
--- a/gcp/api/server.py
+++ b/gcp/api/server.py
@@ -174,9 +174,17 @@ def _query_by_semver(query, package_name, ecosystem, version):
   ]
 
 
-def _query_by_generic_version(query, version):
+def _query_by_generic_version(base_query, version):
   """Query by generic version."""
-  query = query.filter(osv.Bug.affected_fuzzy == osv.normalize_tag(version))
+  # Try without normalizing.
+  query = base_query.filter(osv.Bug.affected_fuzzy == version)
+  results = list(query)
+  if results:
+    return results
+
+  # Try again after normalizing.
+  query = base_query.filter(
+      osv.Bug.affected_fuzzy == osv.normalize_tag(version))
   return list(query)
 
 
diff --git a/lib/osv/models.py b/lib/osv/models.py
--- a/lib/osv/models.py
+++ b/lib/osv/models.py
@@ -338,7 +338,13 @@ def _pre_put_hook(self):
 
     for affected_package in self.affected_packages:
       # Indexes used for querying by exact version.
-      self.affected_fuzzy.extend(bug.normalize_tags(affected_package.versions))
+      if ecosystems.get(affected_package.package.ecosystem):
+        # No need to normalize if the ecosystem is supported.
+        self.affected_fuzzy.extend(affected_package.versions)
+      else:
+        self.affected_fuzzy.extend(
+            bug.normalize_tags(affected_package.versions))
+
       self.has_affected |= bool(affected_package.versions)
 
       for affected_range in affected_package.ranges:


Test patch: