lwn-vulns automation

How It Works

In the root of this repository is a file titled db. It is a new-line separated list of vulnerability IDs from the LWN database.

The new tool screen-scrapes the database until it finds two full pages with no new vulnerabilities.

The reformat tool updates an issue in progress to highlight remaining items to do.

When a roundup issue is closed, the contents of it are sent to the updatedb command which outputs a list of the vulnerability IDs marked as done in the roundup. This should then appended to the db file.

The shell script looks for cherry-picked commits from master to stable, to make most of a security announcement.

Tool Interface

These tools are a bit like plumbing right now, and I would like some simpler user interfaces to be developed on top. Right now, I think they work okay.

Lifecycle of an Issue

Here is a typical workflow. I’ll be using pbcopy and pbpaste to copy and paste to/from my system clipboard. On Linux, it may be xclip -sel clip -i and xclip -sel clip -o.

Build the tools

Start with nix-build ./default.nix -A lwnvulns.pkg. This will create the result symlink referenced here. Note, if you’re doing development, you can enter a nix-shell (just run nix-shell) and replace ./result/bin/ with cargo run --bin <command>:

security$ nix-shell

[nix-shell:~/projects/security/lwnvulns]$ pbpaste | cargo run --bin reformat | pbcopy
    Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/reformat`

Making a new issue

$ ./result/bin/new | pbcopy
Page 0 yielded 30 issues, after 0 pages with nothing
Page 1 yielded 0 issues, after 0 pages with nothing
Page 2 yielded 0 issues, after 1 pages with nothing

My clipboard now contains a full report to open as an issue. It has a few sections of things in here for you to do. Starting with:


 - [ ] Title it "Vulnerability Roundup <n>"
 - [ ] Update the last roundup link
 - [ ] CC everyone who participated in the previous roundup
 - [ ] Label with `security`

Where, obviously, <n> is the last one +1. For example, if the last one was Roundup 9, this one would be Roundup 10. A bit later there will be a place to put a link to the previous roundup:

Here are all the vulnerabilities from
since our [last roundup]()

between those two (). Make sure to correctly find the latest roundup and update this link accordingly.


cc: .

Visit the last roundup and review all the contributors in the sidebar. It will say something like “7 participants”. Make sure each one of those people are CC’d in the new issue. This way it is easy for people to join and drop out of roundups. If they participate in one, they’ll be tagged in the next one. If they don’t participate in that one, they won’t be tagged on the one after that.

Updating an issue

  1. Refresh the issue’s page. This is important to make sure we don’t accidentally delete progress not loaded on your page.
  2. Click edit on the issue, and copy the full markdown contents.
  3. Run it through the reformat tool like this: pbpaste | ./result/bin/reformat | pbcopy.
  4. Paste the newly altered contents in to the issue, and click Save.

Closing an issue

After the roundup is done and all the issues are solved, make sure to finish out this list:

## Upon Completion ...
 - [ ] Run the issue through `reformat` one last time
   through `reformat` again to show all the issues again.
 - [ ] Review commits since last roundup for backport candidates
 - [ ] Update with a
 - [ ] Update the database at

Updating the database

  1. Refresh the issue’s page. This is important to make sure we don’t accidentally delete progress not loaded on your page.
  2. Click edit on the issue, and copy the full markdown contents.
  3. pbpaste | ./result/bin/updatedb >> db
  4. Commit these changes, and open a PR with the new changes.

Review and backport commits from master to stable (release-16.09)

Page through commits to master and try and find all commits which contained security fixes. Make sure any security fixes to master are applied to the stable branch as well. If not, cherry-pick them yourself. If you’re not sure, open a PR with the backported commits.

Creating an Announcement

This tool is quite rough. It is ./ and it looks at all the commits on the release which were cherry-picked from master.

  1. cd in to a nixpkgs clone and run It will output a rough template of all the announcements to make, but make sure to audit it and review, by following the remainder of these steps.
  2. Delete any backported commit lines which were not for security.
  3. Page through the commits to the release branch and identify commits which are security related, but are not cherrypicks from master. The ported tool WILL miss these, so it is imperative to check.
  4. Update the link at the end of the output to point to the latest security vulnerability roundup.
  5. Commit and push and open a PR with the updated ported state file.


Run nix-shell and within that, cargo run --bin new|updatedb|reformat etc.


This tooling includes a tokenizer for tokenizing the issues, a parser to build a “syntax tree”, and then various AST transformations to update the document later on. There is a tool for writing out a syntax tree as text. The new tool which generates new reports emits tokens to the parser, and then uses the writer to output the report.

It is important to me that code be well formatted and have tests. One place in particular that I have failed to adhere to these standards in particular is the new code… this isn’t an excuse to get sloppy. Sorry. :(




A document is the entire markdown contents of a Github Issue. A document begins with a preamble, and ends with a report.


The preamble is arbitrary text and has no specific rules about its content. When being parsed and generated, the preamble is left completely alone and is to be preserved bit-for-bit when outputted.

The preamble is begins at the start of the document, and ends at the first occurance of a Header.


The Report is a collection of Headers and Issues, where a Header is typically followed by zero or more Issues.

Any lines of data inside the Report which is not a Header or an Issue is considered garbage, and should be discarded.


A Header is defined by the following regular expression:

^### (.*) \((\d+) issues?\)( .*)?\n$
     (1)    (2)            (3)

     (1) Package Name
     (2) Issue Count
     (3) Additional Notes (optional)

The header is designed to start with arbitrary text describing the affected packages, the number of issues affecting the package, and then optional notes. Note that the number of issues in the header does not necessarily reflect the true number of contained issues, however a well behaved parser will correctly update the counter. Note that the plural s in issues is optional.

Here are some example headers:

### jasper (2 issues)
### jasper (0 issues)
### jasper (1 issue)
### foo bar baz tux!!! (1 issues) extra notes go here


An Issue is defined by the following regular expression:

^ ?- \[(x| )\] \[`#(.*)`\]\((.*)\) (.*)$
       (1)         (2)      (3)    (4)

       (1) Completion Indicator (x == complete)
       (2) Vulnerability ID
       (3) URL for the Vulnerability
       (4) Arbitraty text describing the vulnerability

The issue is designed to be in a markdown formatted list with a beginning checkbox and a link. This checkbox may either be filled or unfilled, following a link indicating the primary URL for this issue. The link’s text, currently, must start with a #, followed by an identifier to identify this issue. It is assumed this identifier is unique to this issue, and that this issue will never need to be addressed again. Following the link may be arbitrary content. The markdown list line may or may not be prefixed by a blank space.

Here are some example Issues:

 - [x] [`#705362`]( ([search](, [files](  bind: denial of service
 - [ ] [`#705917`]( ([search](, [files](  java-1.8.0-openjdk-aarch32: multiple vulnerabilities
- [x] [`#705362`]( ([search](, [files](  bind: denial of service
- [ ] [`#705917`]( ([search](, [files](  java-1.8.0-openjdk-aarch32: multiple vulnerabilities

Example Document

Here are all the vulnerabilities from
## Notes on the list
1. The reports have been roughly grouped by the package name. This
   isn't perfect, but is intended to help identify if a whole group
### This is valid too, because it doesn't have an issue count!
 - [ ] even this isn't counted!

### Assorted (31 issues)
 - [ ] [`#705568`]( ([search](, [files](  libvirt: privilege escalation
 - [ ] [`#705361`]( ([search](, [files](  java: unspecified vulnerability
 - [ ] [`#705578`]( ([search](, [files](  qemu: multiple vulnerabilities
This stuff is garbage and will be deleted when the parser is run again.
### tiff (2 issues)
 - [x] [`#705364`]( ([search](, [files](  tiff: multiple vulnerabilities
 - [x] [`#635993`]( ([search](, [files](  tiff: multiple vulnerabilities


Why LWN? Why not NVD?

LWN nicely aggregates reports from distributions, who also aggregate CVE IDs they are responding to. This means instead of checking several CVE IDs individually, we know we just need to update a package.

NVD and other CVE databases are frequently dreadfully out of date, and are won’t have data for a CVE data for a long time, where as LWN will already have information about the report.

Has LWN approved this?


new emits Problem with the SSL CA cert (path? access rights?)?

I was missing a /etc/ssl/certs/ca-certificates.crt and copied my /etc/ssl/certs/ca-bundle.crt to be there… shrug.

Why rust?

I originally wrote this tooling in python, but wanted to have strong typing to provide structure to the parser and tokenizer. I don’t any functional languages that are vogue in the Nix ecosystem.

Top Contributors

grahamc phanimahesh