12 I don't want to discourage use, but I'm not sure how stable
13 the API is. Have fun, but be prepared to track changes! :)
15 For _now_, binlore is basically a thin wrapper around
16 `<invoke yara> | <postprocess with yallback>` with support
17 for running it on a derivation, saving the result in the
18 store, and aggregating results from a set of packages.
20 In the longer term, I suspect there are more uses for this
21 general pattern (i.e., run some analysis tool that produces
22 a deterministic output and cache the result per package...).
24 I'm not sure how that'll look and if it'll be the case that
25 binlore automatically collects all of them, or if you'll be
26 configuring which "kind(s)" of lore it generates. Nailing
27 that down will almost certainly mean reworking the API.
31 src = fetchFromGitHub {
35 hash = "sha256-4Fs6HThfDhKRskuDJx2+hucl8crMRm10K6949JdIwPY=";
38 binlore has one one more yallbacks responsible for
39 routing the appropriate lore to a named file in the
40 appropriate format. At some point I might try to do
41 something fancy with this, but for now the answer to
42 *all* questions about the lore are: the bare minimum
43 to get resholve over the next feature hump in time to
44 hopefully slip this feature in before the branch-off.
46 # TODO: feeling really uninspired on the API
49 rules = (src + "/execers.yar");
50 # output filenames; "types" of lore
55 # shell rule callbacks; see github.com/abathur/yallback
56 yallback = (src + "/execers.yall");
58 # - echo for debug, can be removed at some point
59 # - I really just wanted to put the bit after the pipe
60 # in here, but I'm erring on the side of flexibility
61 # since this form will make it easier to pilot other
63 callback = lore: drv: ''
64 if [[ -d "${drv}/bin" ]] || [[ -d "${drv}/lib" ]] || [[ -d "${drv}/libexec" ]]; then
65 echo generating binlore for $drv by running:
66 echo "${yara}/bin/yara --scan-list --recursive ${lore.rules} <(printf '%s\n' ${drv}/{bin,lib,libexec}) | ${yallback}/bin/yallback ${lore.yallback}"
68 echo "failed to generate binlore for $drv (none of ${drv}/{bin,lib,libexec} exist)"
71 if [[ -d "${drv}/bin" ]] || [[ -d "${drv}/lib" ]] || [[ -d "${drv}/libexec" ]]; then
72 ${yara}/bin/yara --scan-list --recursive ${lore.rules} <(printf '%s\n' ${drv}/{bin,lib,libexec}) | ${yallback}/bin/yallback ${lore.yallback}
80 Output a directory containing lore for multiple drvs.
82 This will `make` lore for drv in drvs and then combine lore
83 of the same type across all packages into a single file.
85 When drvs are also specified in the strip argument, corresponding
86 lore is made relative by stripping the path of each drv from
87 matching entries. (This is mainly useful in a build process that
88 uses a chain of two or more derivations where the output of one
89 is the source for the next. See resholve for an example.)
97 (runCommand "more-binlore" { } ''
99 for lorefile in ${toString lore.types}; do
101 lib.concatMapStrings (x: x + "/$lorefile ") (
102 map (make lore) (map lib.getBin (builtins.filter lib.isDerivation drvs))
105 substituteInPlace $out/$lorefile ${lib.concatMapStrings (x: "--replace-quiet '${x}/' '' ") strip}
110 Output a directory containing lore for a single drv.
112 This produces lore for the derivation (via lore.callback) and
113 appends any lore that the derivation itself wrote to nix-support
114 or which was overridden in drv.binlore.<outputName> (passthru).
116 > *Note*: Since the passthru is attached to all outputs, binlore
117 > is an attrset namespaced by outputName to support packages with
118 > executables in more than one output.
120 Since the last entry wins, the effective priority is:
121 drv.binlore.<outputName> > $drv/nix-support > lore generated here by callback
125 runCommand "${drv.name}-binlore"
132 touch $out/{${builtins.concatStringsSep "," lore.types}}
134 ${lore.callback lore drv}
137 # append lore from package's $out and drv.binlore.${drv.outputName} (last entry wins)
139 for lore_type in ${builtins.toString lore.types}; do
140 if [[ -f "${drv}/nix-support/$lore_type" ]]; then
141 cat "${drv}/nix-support/$lore_type" >> "$out/$lore_type"
145 lib.optionalString (builtins.hasAttr "binlore" drv && builtins.hasAttr drv.outputName drv.binlore)
147 if [[ -f "${drv.binlore."${drv.outputName}"}/$lore_type" ]]; then
148 cat "${drv.binlore."${drv.outputName}"}/$lore_type" >> "$out/$lore_type"
154 echo binlore for $drv written to $out
159 Utility function for creating override lore for drv.
161 We normally attach this lore to `drv.passthru.binlore.<outputName>`.
164 > - Since the passthru is attached to all outputs, binlore is an
165 > attrset namespaced by outputName to support packages with
166 > executables in more than one output. You'll generally just use
168 > - We can reconsider the passthru attr name if someone adds
169 > a new lore provider. We settled on `.binlore` for now to make it
170 > easier for people to figure out what this is for.
172 The lore argument should be a Shell script (string) that generates
173 the necessary lore. You can use arbitrary Shell, but this function
174 includes a shell DSL you can use to declare/generate lore in most
175 cases. It has the following functions:
177 - `execer <verdict> [<path>...]`
178 - `wrapper <wrapper_path> <original_path>`
180 Writing every override explicitly in a Nix list would be tedious
181 for large packages, but this small shell DSL enables us to express
182 many overrides efficiently via pathname expansion/globbing.
184 Here's a very general example of both functions:
186 passthru.binlore.out = binlore.synthesize finalAttrs.finalPackage ''
187 execer can bin/hello bin/{a,b,c}
188 wrapper bin/hello bin/.hello-wrapped
191 And here's a specific example of how pathname expansion enables us
192 to express lore for the single-binary variant of coreutils while
193 being both explicit and (somewhat) efficient:
195 passthru = {} // optionalAttrs (singleBinary != false) {
196 binlore.out = binlore.synthesize coreutils ''
197 execer can bin/{chroot,env,install,nice,nohup,runcon,sort,split,stdbuf,timeout}
198 execer cannot bin/{[,b2sum,base32,base64,basename,basenc,cat,chcon,chgrp,chmod,chown,cksum,comm,cp,csplit,cut,date,dd,df,dir,dircolors,dirname,du,echo,expand,expr,factor,false,fmt,fold,groups,head,hostid,id,join,kill,link,ln,logname,ls,md5sum,mkdir,mkfifo,mknod,mktemp,mv,nl,nproc,numfmt,od,paste,pathchk,pinky,pr,printenv,printf,ptx,pwd,readlink,realpath,rm,rmdir,seq,sha1sum,sha224sum,sha256sum,sha384sum,sha512sum,shred,shuf,sleep,stat,stty,sum,sync,tac,tail,tee,test,touch,tr,true,truncate,tsort,tty,uname,unexpand,uniq,unlink,uptime,users,vdir,wc,who,whoami,yes}
202 Caution: Be thoughtful about using a bare wildcard (*) glob here.
203 We should generally override lore only when a human understands if
204 the executable will exec arbitrary user-passed executables. A bare
205 glob can match new executables added in future package versions
206 before anyone can audit them.
209 drv: loreSynthesizingScript:
210 runCommand "${drv.name}-lore-override"
222 if [[ -f "$PWD/$path" ]]; then
223 echo "$verdict:$PWD/$path"
225 echo "error: Tried to synthesize execer lore for missing file: $PWD/$path" >&2
235 if [[ ! -f "$wrapper" ]]; then
236 echo "error: Tried to synthesize wrapper lore for missing wrapper: $PWD/$wrapper" >&2
240 if [[ ! -f "$original" ]]; then
241 echo "error: Tried to synthesize wrapper lore for missing original: $PWD/$original" >&2
245 echo "$PWD/$wrapper:$PWD/$original"
251 # lore override commands are relative to the drv root
255 + loreSynthesizingScript