|
1 | 1 | import logging |
2 | 2 | import re |
| 3 | +import urllib.parse |
3 | 4 | from itertools import chain |
4 | 5 | from typing import Any |
5 | 6 |
|
@@ -179,6 +180,28 @@ def is_version_affected(version_statuses: list[str]) -> Version.Status: |
179 | 180 | return result |
180 | 181 |
|
181 | 182 |
|
| 183 | +def get_src_position(derivation: NixDerivation) -> str | None: |
| 184 | + """ |
| 185 | + Get the GitHub URL pointing to the exact source file used for the evaluation of this derivation. |
| 186 | + E.g. https://github.com/NixOS/nixpkgs/blob/0e8be3827d0298743ba71b91eea652d43d7dc03d/pkgs/by-name/he/hello/package.nix#L47 |
| 187 | + """ |
| 188 | + if derivation.metadata and derivation.metadata.position: |
| 189 | + rev = urllib.parse.quote(derivation.parent_evaluation.commit_sha1) |
| 190 | + # position is something like `/tmp/tmpfh7ff2xs/pkgs/development/python-modules/qemu/default.nix:67` |
| 191 | + position_match = re.match( |
| 192 | + # FIXME the location of the eval store is going to be configurable in the future. |
| 193 | + # https://github.com/Nix-Security-WG/nix-security-tracker/pull/451 |
| 194 | + # Ideally the position field is already relative to the location. |
| 195 | + r"/tmp/[^/]+/(.+):(\d+)", |
| 196 | + derivation.metadata.position, |
| 197 | + ) |
| 198 | + if position_match: |
| 199 | + path = urllib.parse.quote(position_match.group(1)) |
| 200 | + linenumber = urllib.parse.quote(position_match.group(2)) |
| 201 | + return f"https://github.com/NixOS/nixpkgs/blob/{rev}/{path}#L{linenumber}" |
| 202 | + return None |
| 203 | + |
| 204 | + |
182 | 205 | def channel_structure( |
183 | 206 | version_constraints: list[Version], derivations: list[NixDerivation] |
184 | 207 | ) -> dict: |
@@ -211,21 +234,26 @@ def channel_structure( |
211 | 234 | "major_version": None, |
212 | 235 | "status": None, |
213 | 236 | "uniform_versions": None, |
| 237 | + "src_position": None, |
214 | 238 | "sub_branches": dict(), |
215 | 239 | } |
216 | | - if not branch_name == major_channel: |
| 240 | + if branch_name == major_channel: |
| 241 | + packages[attribute]["versions"][major_channel]["major_version"] = ( |
| 242 | + version |
| 243 | + ) |
| 244 | + packages[attribute]["versions"][major_channel]["src_position"] = ( |
| 245 | + get_src_position(derivation) |
| 246 | + ) |
| 247 | + else: |
217 | 248 | packages[attribute]["versions"][major_channel]["sub_branches"][ |
218 | 249 | branch_name |
219 | 250 | ] = { |
220 | 251 | "version": version, |
221 | 252 | "status": is_version_affected( |
222 | 253 | [v.is_affected(version) for v in version_constraints] |
223 | 254 | ), |
| 255 | + "src_position": get_src_position(derivation), |
224 | 256 | } |
225 | | - else: |
226 | | - packages[attribute]["versions"][major_channel]["major_version"] = ( |
227 | | - version |
228 | | - ) |
229 | 257 | for package_name in packages: |
230 | 258 | for mc in packages[package_name]["versions"].keys(): |
231 | 259 | uniform_versions = True |
|
0 commit comments