├── .Rbuildignore ├── .gitignore ├── .travis.yml ├── DESCRIPTION ├── README-NOT.md ├── README.Rmd ├── README.md ├── builddocs.R ├── builddocs.sh ├── docs ├── .nojekyll ├── administrators.html ├── builddocs.Rout ├── developers.html ├── index.html ├── intro.html ├── libs │ ├── bootstrap-3.3.5 │ │ ├── css │ │ │ └── bootstrap.min.css │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ └── glyphicons-halflings-regular.woff │ │ ├── js │ │ │ └── bootstrap.min.js │ │ └── shim │ │ │ ├── html5shiv.min.js │ │ │ └── respond.min.js │ ├── gitbook-2.6.7 │ │ ├── css │ │ │ ├── fontawesome │ │ │ │ └── fontawesome-webfont.ttf │ │ │ ├── plugin-bookdown.css │ │ │ ├── plugin-fontsettings.css │ │ │ ├── plugin-highlight.css │ │ │ ├── plugin-search.css │ │ │ └── style.css │ │ └── js │ │ │ ├── app.min.js │ │ │ ├── jquery.highlight.js │ │ │ ├── lunr.js │ │ │ ├── plugin-bookdown.js │ │ │ ├── plugin-fontsettings.js │ │ │ ├── plugin-search.js │ │ │ └── plugin-sharing.js │ ├── jquery-1.11.3 │ │ └── jquery.min.js │ ├── jquery-2.2.3 │ │ └── jquery.min.js │ └── navigation-1.1 │ │ └── tabsets.js ├── r-security-practices.html ├── r-users.html ├── references.html └── search_index.json ├── handouts ├── 01-intro.Rmd ├── 02-users.Rmd ├── 03-developers.Rmd ├── 04-administrators.Rmd ├── 06-references.Rmd └── index.Rmd ├── roadmap.md └── rsecuritypractices.Rproj /.Rbuildignore: -------------------------------------------------------------------------------- 1 | data-raw 2 | ^.*\.Rproj$ 3 | ^\.Rproj\.user$ 4 | \.Rproj\.user 5 | ^\.travis\.yml$ 6 | ^buildpres\.sh$ 7 | ^buildpres\.R$ 8 | docs 9 | ^README\.Rmd$ 10 | ^README-.*\.png$ 11 | pres 12 | demo 13 | handouts 14 | ^.*\.md$ 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r 2 | language: R 3 | sudo: false 4 | cache: packages 5 | addons: 6 | apt: 7 | sources: 8 | - sourceline: 'ppa:chris-lea/libsodium' 9 | packages: 10 | - libgpgme11-dev 11 | - libsodium-dev 12 | latex: false 13 | pandoc_version: 1.17.2 14 | warnings_are_errors: false 15 | install: 16 | - R -e 'install.packages("devtools")' 17 | - R -e 'devtools::install_deps(dep = T)' 18 | - R CMD build --no-build-vignettes --no-manual . 19 | - R CMD check --no-build-vignettes --no-manual *tar.gz 20 | - Rscript -e 'devtools::install(pkg = ".")' 21 | before_script: 22 | - chmod +x ./builddocs.sh 23 | script: 24 | - ./builddocs.sh 25 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: rsecuritypractices 2 | Title: Materials relating to security best practices for R people 3 | Version: 0.0.0.9001 4 | Authors@R: person("Steph", "Locke", email = "stetphanie.g.locke@gmail.com", role = c("aut", "cre")) 5 | Description: Guidance information intended for R users and developers to help them write safer code. 6 | Depends: R (>= 3.0.0) 7 | License: MIT 8 | Encoding: UTF-8 9 | LazyData: true 10 | Remotes: ropenscilabs/notary 11 | Imports: bookdown, 12 | rmarkdown, 13 | notary, 14 | DBI, 15 | shiny 16 | -------------------------------------------------------------------------------- /README-NOT.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | R security practices 4 | ==================== 5 | 6 | This is a repository of guidance materials to help people improve the security of R usage. 7 | 8 | The core guidance is available in the [book](https://ropenscilabs.github.io/r-security-practices). 9 | 10 | Who this is for 11 | --------------- 12 | 13 | This work is aimed at three (often overlapping) types of people involved with R -- users, developers, and administrators. 14 | 15 | Motivation 16 | ---------- 17 | 18 | The R community is pretty awesome but as a lot of us aren't trained developers, we can inadvertently do things that are security boo-boos or open us to bad people taking advantage of our systems. I'm sure we'd like to avoid that! 19 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, echo = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "README-" 12 | ) 13 | ``` 14 | 15 | # R security practices 16 | 17 | This is a repository of guidance materials to help people improve the security of R usage. 18 | 19 | The core guidance is available in the [book](https://ropenscilabs.github.io/r-security-practices). 20 | 21 | ## Who this is for 22 | This work is aimed at three (often overlapping) types of people involved with R -- users, developers, and administrators. 23 | 24 | ## Motivation 25 | The R community is pretty awesome but as a lot of us aren't trained developers, we can inadvertently do things that are security boo-boos or open us to bad people taking advantage of our systems. I'm sure we'd like to avoid that! 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rsecuritypractices 2 | 3 | [![Project Status: Abandoned](https://www.repostatus.org/badges/latest/abandoned.svg)](https://www.repostatus.org/#abandoned) 4 | 5 | This repository has been archived. The former README is now in [README-NOT.md](README-NOT.md). 6 | -------------------------------------------------------------------------------- /builddocs.R: -------------------------------------------------------------------------------- 1 | library(rmarkdown) 2 | bookdown::render_book("handouts/index.Rmd", "bookdown::gitbook", output_dir="docs") 3 | # slides=list.files("pres","*.Rmd",full.names=TRUE) 4 | # 5 | # for (f in slides) render(f,output_dir = "docs") 6 | -------------------------------------------------------------------------------- /builddocs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | AUTHORNAME="Steph" 3 | AUTHOREMAIL="Steph@itsalocke.com" 4 | GITURL="https://$GITHUB_PAT@github.com/$TRAVIS_REPO_SLUG.git" 5 | 6 | git remote set-url origin $GITURL 7 | git pull 8 | git checkout master 9 | git config --global user.name $AUTHORNAME 10 | git config --global user.email $AUTHOREMAIL 11 | 12 | R CMD BATCH './builddocs.R' 13 | 14 | cp builddocs.Rout docs/ 15 | 16 | git add docs/ 17 | git commit -am "[ci skip] Documents produced in clean environment via Travis $TRAVIS_BUILD_NUMBER" 18 | git push -u --quiet origin master 19 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci-archive/r-security-practices/7cc795412beea68b681c8296966fd49ce67bfa62/docs/.nojekyll -------------------------------------------------------------------------------- /docs/administrators.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | R security practices 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
97 | 98 |
99 | 183 |
184 | 185 |
186 |
187 | 192 | 193 |
194 |
195 | 196 |
197 |
198 |

5 Administrators

199 |
200 |

5.1 Authorative mirrors

201 |
202 |

5.1.1 Principles

203 |
204 |
205 |

5.1.2 Good examples

206 |
207 |
208 |

5.1.3 Bad examples

209 |
210 |
211 |

5.1.4 Background

212 |
213 |
214 |
215 |

5.2 Internal CRANs

216 |
217 |

5.2.1 Principles

218 |
219 |
220 |

5.2.2 Good examples

221 |
222 |
223 |

5.2.3 Bad examples

224 |
225 |
226 |

5.2.4 Background

227 |
228 |
229 |
230 |

5.3 Validating package installs

231 |
232 |

5.3.1 Principles

233 |
234 |
235 |

5.3.2 Good examples

236 |
237 |
238 |

5.3.3 Bad examples

239 |
240 |
241 |

5.3.4 Background

242 |
243 |
244 |
245 |

5.4 Restricting privileges

246 |
247 |

5.4.1 Principles

248 |
249 |
250 |

5.4.2 Good examples

251 |
252 |
253 |

5.4.3 Bad examples

254 |
255 |
256 |

5.4.4 Background

257 | 258 |
259 |
260 |
261 |
262 | 263 |
264 |
265 |
266 | 267 | 268 |
269 |
270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 306 | 307 | 308 | 309 | 310 | -------------------------------------------------------------------------------- /docs/builddocs.Rout: -------------------------------------------------------------------------------- 1 | 2 | R version 3.4.0 (2017-04-21) -- "You Stupid Darkness" 3 | Copyright (C) 2017 The R Foundation for Statistical Computing 4 | Platform: x86_64-pc-linux-gnu (64-bit) 5 | 6 | R is free software and comes with ABSOLUTELY NO WARRANTY. 7 | You are welcome to redistribute it under certain conditions. 8 | Type 'license()' or 'licence()' for distribution details. 9 | 10 | Natural language support but running in an English locale 11 | 12 | R is a collaborative project with many contributors. 13 | Type 'contributors()' for more information and 14 | 'citation()' on how to cite R or R packages in publications. 15 | 16 | Type 'demo()' for some demos, 'help()' for on-line help, or 17 | 'help.start()' for an HTML browser interface to help. 18 | Type 'q()' to quit R. 19 | 20 | > library(rmarkdown) 21 | > bookdown::render_book("handouts/index.Rmd", "bookdown::gitbook", output_dir="docs") 22 | 23 | 24 | processing file: _main.Rmd 25 | | | | 0% | |...................... | 33% 26 | ordinary text without R code 27 | 28 | | |........................................... | 67% 29 | label: unnamed-chunk-1 (with options) 30 | List of 1 31 | $ echo: logi FALSE 32 | 33 | | |.................................................................| 100% 34 | ordinary text without R code 35 | 36 | 37 | output file: _main.knit.md 38 | 39 | /usr/bin/pandoc +RTS -K512m -RTS _main.utf8.md --to html --from markdown+autolink_bare_uris+ascii_identifiers+tex_math_single_backslash --output _main.html --smart --email-obfuscation none --standalone --section-divs --table-of-contents --toc-depth 3 --template /home/travis/R/Library/bookdown/templates/gitbook.html --highlight-style pygments --number-sections --include-in-header /tmp/Rtmp3OOFWb/rmarkdown-str2fea1ea94d82.html --mathjax 40 | 41 | Output created: docs/r-security-practices.html 42 | [1] "/home/travis/build/ropenscilabs/r-security-practices/docs/r-security-practices.html" 43 | > # slides=list.files("pres","*.Rmd",full.names=TRUE) 44 | > # 45 | > # for (f in slides) render(f,output_dir = "docs") 46 | > 47 | > proc.time() 48 | user system elapsed 49 | 0.852 0.080 0.949 50 | -------------------------------------------------------------------------------- /docs/developers.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | R security practices 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
97 | 98 |
99 | 183 |
184 | 185 |
186 |
187 | 192 | 193 |
194 |
195 | 196 |
197 |
198 |

4 Developers

199 |
200 |

4.1 Preventing unsafe arbitrary code execution

201 |
202 |

4.1.1 Principles

203 |
204 |
205 |

4.1.2 Good examples

206 |
207 |
208 |

4.1.3 Bad examples

209 |
210 |
211 |

4.1.4 Background

212 |
213 |
214 |
215 |

4.2 Signing your commits

216 |
217 |

4.2.1 Principles

218 |
219 |
220 |

4.2.2 Good examples

221 |
222 |
223 |

4.2.3 Bad examples

224 |
225 |
226 |

4.2.4 Background

227 |
228 |
229 |
230 |

4.3 Storing sensitive information

231 |
232 |

4.3.1 Principles

233 |
234 |
235 |

4.3.2 Good examples

236 |
237 |
238 |

4.3.3 Bad examples

239 |
240 |
241 |

4.3.4 Background

242 |
243 |
244 |
245 |

4.4 Downloading dependencies

246 |
247 |

4.4.1 Principles

248 |
249 |
250 |

4.4.2 Good examples

251 |
252 |
253 |

4.4.3 Bad examples

254 |
255 |
256 |

4.4.4 Background

257 |
258 |
259 |
260 |

4.5 System dependencies

261 |
262 |

4.5.1 Principles

263 |
264 |
265 |

4.5.2 Good examples

266 |
267 |
268 |

4.5.3 Bad examples

269 |
270 |
271 |

4.5.4 Background

272 |
273 |
274 |
275 |

4.6 File interactions

276 |
277 |

4.6.1 Principles

278 |
279 |
280 |

4.6.2 Good examples

281 |
282 |
283 |

4.6.3 Bad examples

284 |
285 |
286 |

4.6.4 Background

287 | 288 |
289 |
290 |
291 |
292 | 293 |
294 |
295 |
296 | 297 | 298 |
299 |
300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 336 | 337 | 338 | 339 | 340 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | R security practices 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
97 | 98 |
99 | 183 |
184 | 185 |
186 |
187 | 192 | 193 |
194 |
195 | 196 |
197 | 202 |
203 |

1 Who this book is for

204 |

This book is aimed at three (often overlapping) types of people involved with R – users, developers, and administrators.

205 |

The R community is pretty awesome but as a lot of us aren’t trained developers, we can inadvertently do things that are security boo-boos or open us to bad people taking advantage of our systems.

206 |

As a result, we give guidance to R people to help you avoid common issues.

207 | 208 |
209 |
210 | 211 |
212 |
213 |
214 | 215 | 216 |
217 |
218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 254 | 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /docs/intro.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | R security practices 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
97 | 98 |
99 | 183 |
184 | 185 |
186 |
187 | 192 | 193 |
194 |
195 | 196 |
197 |
198 |

2 Introduction

199 |

Welcome to our book on security best practices for R people!

200 |

In our Users section, we outline what you need to think about when you’re writing scripts and using R day to day.

201 |

In our Developers section, we outline best practices to developing R packages securely that helps you avoid doing things that allow bad things to happen because of your package.

202 |

In our Administrators section, we outline some best practices for maintaining R to minimise potential security issues.

203 |

The aim of the book is not too make R people security experts, but to give R people some pragmatic, easy-to-follow principles that result in more secure behaviour. We demonstrate the principles with some examples of how to do things and how not to do things. Then, if you’re interested in the details behind why you should do these things we provide explanations.

204 | 205 |
206 |
207 | 208 |
209 |
210 |
211 | 212 | 213 |
214 |
215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 251 | 252 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /docs/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci-archive/r-security-practices/7cc795412beea68b681c8296966fd49ce67bfa62/docs/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /docs/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci-archive/r-security-practices/7cc795412beea68b681c8296966fd49ce67bfa62/docs/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /docs/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ropensci-archive/r-security-practices/7cc795412beea68b681c8296966fd49ce67bfa62/docs/libs/bootstrap-3.3.5/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /docs/libs/bootstrap-3.3.5/shim/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | // Only run this code in IE 8 5 | if (!!window.navigator.userAgent.match("MSIE 8")) { 6 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document); 7 | }; 8 | -------------------------------------------------------------------------------- /docs/libs/bootstrap-3.3.5/shim/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl 2 | * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT 3 | * */ 4 | 5 | // Only run this code in IE 8 6 | if (!!window.navigator.userAgent.match("MSIE 8")) { 7 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b tbody > tr > td { 68 | vertical-align: top; 69 | } 70 | .book .book-body .page-wrapper .page-inner section.normal table tr.header { 71 | border-top-width: 2px; 72 | } 73 | .book .book-body .page-wrapper .page-inner section.normal table tr:last-child td { 74 | border-bottom-width: 2px; 75 | } 76 | .book .book-body .page-wrapper .page-inner section.normal table td, .book .book-body .page-wrapper .page-inner section.normal table th { 77 | border-left: none; 78 | border-right: none; 79 | } 80 | .book .book-body .page-wrapper .page-inner section.normal table.kable_wrapper > tbody > tr, .book .book-body .page-wrapper .page-inner section.normal table.kable_wrapper > tbody > tr > td { 81 | border-top: none; 82 | } 83 | .book .book-body .page-wrapper .page-inner section.normal table.kable_wrapper > tbody > tr:last-child > td { 84 | border-bottom: none; 85 | } 86 | 87 | div.theorem, div.lemma, div.corollary, div.proposition { 88 | font-style: italic; 89 | } 90 | span.theorem, span.lemma, span.corollary, span.proposition { 91 | font-style: normal; 92 | } 93 | div.proof:after { 94 | content: "\25a2"; 95 | float: right; 96 | } 97 | .header-section-number { 98 | padding-right: .5em; 99 | } 100 | -------------------------------------------------------------------------------- /docs/libs/gitbook-2.6.7/css/plugin-fontsettings.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Theme 1 3 | */ 4 | .color-theme-1 .dropdown-menu { 5 | background-color: #111111; 6 | border-color: #7e888b; 7 | } 8 | .color-theme-1 .dropdown-menu .dropdown-caret .caret-inner { 9 | border-bottom: 9px solid #111111; 10 | } 11 | .color-theme-1 .dropdown-menu .buttons { 12 | border-color: #7e888b; 13 | } 14 | .color-theme-1 .dropdown-menu .button { 15 | color: #afa790; 16 | } 17 | .color-theme-1 .dropdown-menu .button:hover { 18 | color: #73553c; 19 | } 20 | /* 21 | * Theme 2 22 | */ 23 | .color-theme-2 .dropdown-menu { 24 | background-color: #2d3143; 25 | border-color: #272a3a; 26 | } 27 | .color-theme-2 .dropdown-menu .dropdown-caret .caret-inner { 28 | border-bottom: 9px solid #2d3143; 29 | } 30 | .color-theme-2 .dropdown-menu .buttons { 31 | border-color: #272a3a; 32 | } 33 | .color-theme-2 .dropdown-menu .button { 34 | color: #62677f; 35 | } 36 | .color-theme-2 .dropdown-menu .button:hover { 37 | color: #f4f4f5; 38 | } 39 | .book .book-header .font-settings .font-enlarge { 40 | line-height: 30px; 41 | font-size: 1.4em; 42 | } 43 | .book .book-header .font-settings .font-reduce { 44 | line-height: 30px; 45 | font-size: 1em; 46 | } 47 | .book.color-theme-1 .book-body { 48 | color: #704214; 49 | background: #f3eacb; 50 | } 51 | .book.color-theme-1 .book-body .page-wrapper .page-inner section { 52 | background: #f3eacb; 53 | } 54 | .book.color-theme-2 .book-body { 55 | color: #bdcadb; 56 | background: #1c1f2b; 57 | } 58 | .book.color-theme-2 .book-body .page-wrapper .page-inner section { 59 | background: #1c1f2b; 60 | } 61 | .book.font-size-0 .book-body .page-inner section { 62 | font-size: 1.2rem; 63 | } 64 | .book.font-size-1 .book-body .page-inner section { 65 | font-size: 1.4rem; 66 | } 67 | .book.font-size-2 .book-body .page-inner section { 68 | font-size: 1.6rem; 69 | } 70 | .book.font-size-3 .book-body .page-inner section { 71 | font-size: 2.2rem; 72 | } 73 | .book.font-size-4 .book-body .page-inner section { 74 | font-size: 4rem; 75 | } 76 | .book.font-family-0 { 77 | font-family: Georgia, serif; 78 | } 79 | .book.font-family-1 { 80 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 81 | } 82 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal { 83 | color: #704214; 84 | } 85 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal a { 86 | color: inherit; 87 | } 88 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, 89 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2, 90 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h3, 91 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h4, 92 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h5, 93 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { 94 | color: inherit; 95 | } 96 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, 97 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2 { 98 | border-color: inherit; 99 | } 100 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { 101 | color: inherit; 102 | } 103 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal hr { 104 | background-color: inherit; 105 | } 106 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal blockquote { 107 | border-color: inherit; 108 | } 109 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, 110 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { 111 | background: #fdf6e3; 112 | color: #657b83; 113 | border-color: #f8df9c; 114 | } 115 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal .highlight { 116 | background-color: inherit; 117 | } 118 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table th, 119 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table td { 120 | border-color: #f5d06c; 121 | } 122 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr { 123 | color: inherit; 124 | background-color: #fdf6e3; 125 | border-color: #444444; 126 | } 127 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { 128 | background-color: #fbeecb; 129 | } 130 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal { 131 | color: #bdcadb; 132 | } 133 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal a { 134 | color: #3eb1d0; 135 | } 136 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, 137 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2, 138 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h3, 139 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h4, 140 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h5, 141 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { 142 | color: #fffffa; 143 | } 144 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, 145 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2 { 146 | border-color: #373b4e; 147 | } 148 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { 149 | color: #373b4e; 150 | } 151 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal hr { 152 | background-color: #373b4e; 153 | } 154 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal blockquote { 155 | border-color: #373b4e; 156 | } 157 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, 158 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { 159 | color: #9dbed8; 160 | background: #2d3143; 161 | border-color: #2d3143; 162 | } 163 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal .highlight { 164 | background-color: #282a39; 165 | } 166 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table th, 167 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table td { 168 | border-color: #3b3f54; 169 | } 170 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr { 171 | color: #b6c2d2; 172 | background-color: #2d3143; 173 | border-color: #3b3f54; 174 | } 175 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { 176 | background-color: #35394b; 177 | } 178 | .book.color-theme-1 .book-header { 179 | color: #afa790; 180 | background: transparent; 181 | } 182 | .book.color-theme-1 .book-header .btn { 183 | color: #afa790; 184 | } 185 | .book.color-theme-1 .book-header .btn:hover { 186 | color: #73553c; 187 | background: none; 188 | } 189 | .book.color-theme-1 .book-header h1 { 190 | color: #704214; 191 | } 192 | .book.color-theme-2 .book-header { 193 | color: #7e888b; 194 | background: transparent; 195 | } 196 | .book.color-theme-2 .book-header .btn { 197 | color: #3b3f54; 198 | } 199 | .book.color-theme-2 .book-header .btn:hover { 200 | color: #fffff5; 201 | background: none; 202 | } 203 | .book.color-theme-2 .book-header h1 { 204 | color: #bdcadb; 205 | } 206 | .book.color-theme-1 .book-body .navigation { 207 | color: #afa790; 208 | } 209 | .book.color-theme-1 .book-body .navigation:hover { 210 | color: #73553c; 211 | } 212 | .book.color-theme-2 .book-body .navigation { 213 | color: #383f52; 214 | } 215 | .book.color-theme-2 .book-body .navigation:hover { 216 | color: #fffff5; 217 | } 218 | /* 219 | * Theme 1 220 | */ 221 | .book.color-theme-1 .book-summary { 222 | color: #afa790; 223 | background: #111111; 224 | border-right: 1px solid rgba(0, 0, 0, 0.07); 225 | } 226 | .book.color-theme-1 .book-summary .book-search { 227 | background: transparent; 228 | } 229 | .book.color-theme-1 .book-summary .book-search input, 230 | .book.color-theme-1 .book-summary .book-search input:focus { 231 | border: 1px solid transparent; 232 | } 233 | .book.color-theme-1 .book-summary ul.summary li.divider { 234 | background: #7e888b; 235 | box-shadow: none; 236 | } 237 | .book.color-theme-1 .book-summary ul.summary li i.fa-check { 238 | color: #33cc33; 239 | } 240 | .book.color-theme-1 .book-summary ul.summary li.done > a { 241 | color: #877f6a; 242 | } 243 | .book.color-theme-1 .book-summary ul.summary li a, 244 | .book.color-theme-1 .book-summary ul.summary li span { 245 | color: #877f6a; 246 | background: transparent; 247 | font-weight: normal; 248 | } 249 | .book.color-theme-1 .book-summary ul.summary li.active > a, 250 | .book.color-theme-1 .book-summary ul.summary li a:hover { 251 | color: #704214; 252 | background: transparent; 253 | font-weight: normal; 254 | } 255 | /* 256 | * Theme 2 257 | */ 258 | .book.color-theme-2 .book-summary { 259 | color: #bcc1d2; 260 | background: #2d3143; 261 | border-right: none; 262 | } 263 | .book.color-theme-2 .book-summary .book-search { 264 | background: transparent; 265 | } 266 | .book.color-theme-2 .book-summary .book-search input, 267 | .book.color-theme-2 .book-summary .book-search input:focus { 268 | border: 1px solid transparent; 269 | } 270 | .book.color-theme-2 .book-summary ul.summary li.divider { 271 | background: #272a3a; 272 | box-shadow: none; 273 | } 274 | .book.color-theme-2 .book-summary ul.summary li i.fa-check { 275 | color: #33cc33; 276 | } 277 | .book.color-theme-2 .book-summary ul.summary li.done > a { 278 | color: #62687f; 279 | } 280 | .book.color-theme-2 .book-summary ul.summary li a, 281 | .book.color-theme-2 .book-summary ul.summary li span { 282 | color: #c1c6d7; 283 | background: transparent; 284 | font-weight: 600; 285 | } 286 | .book.color-theme-2 .book-summary ul.summary li.active > a, 287 | .book.color-theme-2 .book-summary ul.summary li a:hover { 288 | color: #f4f4f5; 289 | background: #252737; 290 | font-weight: 600; 291 | } 292 | -------------------------------------------------------------------------------- /docs/libs/gitbook-2.6.7/css/plugin-search.css: -------------------------------------------------------------------------------- 1 | .book .book-summary .book-search { 2 | padding: 6px; 3 | background: transparent; 4 | position: absolute; 5 | top: -50px; 6 | left: 0px; 7 | right: 0px; 8 | transition: top 0.5s ease; 9 | } 10 | .book .book-summary .book-search input, 11 | .book .book-summary .book-search input:focus, 12 | .book .book-summary .book-search input:hover { 13 | width: 100%; 14 | background: transparent; 15 | border: 1px solid #ccc; 16 | box-shadow: none; 17 | outline: none; 18 | line-height: 22px; 19 | padding: 7px 4px; 20 | color: inherit; 21 | box-sizing: border-box; 22 | } 23 | .book.with-search .book-summary .book-search { 24 | top: 0px; 25 | } 26 | .book.with-search .book-summary ul.summary { 27 | top: 50px; 28 | } 29 | -------------------------------------------------------------------------------- /docs/libs/gitbook-2.6.7/js/jquery.highlight.js: -------------------------------------------------------------------------------- 1 | require(["jQuery"], function(jQuery) { 2 | 3 | /* 4 | * jQuery Highlight plugin 5 | * 6 | * Based on highlight v3 by Johann Burkard 7 | * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html 8 | * 9 | * Code a little bit refactored and cleaned (in my humble opinion). 10 | * Most important changes: 11 | * - has an option to highlight only entire words (wordsOnly - false by default), 12 | * - has an option to be case sensitive (caseSensitive - false by default) 13 | * - highlight element tag and class names can be specified in options 14 | * 15 | * Copyright (c) 2009 Bartek Szopka 16 | * 17 | * Licensed under MIT license. 18 | * 19 | */ 20 | 21 | jQuery.extend({ 22 | highlight: function (node, re, nodeName, className) { 23 | if (node.nodeType === 3) { 24 | var match = node.data.match(re); 25 | if (match) { 26 | var highlight = document.createElement(nodeName || 'span'); 27 | highlight.className = className || 'highlight'; 28 | var wordNode = node.splitText(match.index); 29 | wordNode.splitText(match[0].length); 30 | var wordClone = wordNode.cloneNode(true); 31 | highlight.appendChild(wordClone); 32 | wordNode.parentNode.replaceChild(highlight, wordNode); 33 | return 1; //skip added node in parent 34 | } 35 | } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children 36 | !/(script|style)/i.test(node.tagName) && // ignore script and style nodes 37 | !(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted 38 | for (var i = 0; i < node.childNodes.length; i++) { 39 | i += jQuery.highlight(node.childNodes[i], re, nodeName, className); 40 | } 41 | } 42 | return 0; 43 | } 44 | }); 45 | 46 | jQuery.fn.unhighlight = function (options) { 47 | var settings = { className: 'highlight', element: 'span' }; 48 | jQuery.extend(settings, options); 49 | 50 | return this.find(settings.element + "." + settings.className).each(function () { 51 | var parent = this.parentNode; 52 | parent.replaceChild(this.firstChild, this); 53 | parent.normalize(); 54 | }).end(); 55 | }; 56 | 57 | jQuery.fn.highlight = function (words, options) { 58 | var settings = { className: 'highlight', element: 'span', caseSensitive: false, wordsOnly: false }; 59 | jQuery.extend(settings, options); 60 | 61 | if (words.constructor === String) { 62 | words = [words]; 63 | } 64 | words = jQuery.grep(words, function(word, i){ 65 | return word !== ''; 66 | }); 67 | words = jQuery.map(words, function(word, i) { 68 | return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 69 | }); 70 | if (words.length === 0) { return this; } 71 | 72 | var flag = settings.caseSensitive ? "" : "i"; 73 | var pattern = "(" + words.join("|") + ")"; 74 | if (settings.wordsOnly) { 75 | pattern = "\\b" + pattern + "\\b"; 76 | } 77 | var re = new RegExp(pattern, flag); 78 | 79 | return this.each(function () { 80 | jQuery.highlight(this, re, settings.element, settings.className); 81 | }); 82 | }; 83 | 84 | }); 85 | -------------------------------------------------------------------------------- /docs/libs/gitbook-2.6.7/js/lunr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.5.12 3 | * Copyright (C) 2015 Oliver Nightingale 4 | * MIT Licensed 5 | * @license 6 | */ 7 | !function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.5.12",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(t){return arguments.length&&null!=t&&void 0!=t?Array.isArray(t)?t.map(function(t){return t.toLowerCase()}):t.toString().trim().toLowerCase().split(/[\s\-\/]+/):[]},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,o=0;n>o;o++){for(var r=t[o],s=0;i>s&&(r=this._stack[s](r,o,t),void 0!==r);s++);void 0!==r&&e.push(r)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(r===t)return o;t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o]}return r===t?o:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,o=e+Math.floor(i/2),r=this.elements[o];i>1;)t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o];return r>t?o:t>r?o+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,o=0,r=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>r-1||o>s-1)break;a[i]!==h[o]?a[i]h[o]&&o++:(n.add(a[i]),i++,o++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;return this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone(),i.add.apply(i,n.toArray()),i},t.SortedSet.prototype.toJSON=function(){return this.toArray()},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.Store,this.tokenStore=new t.TokenStore,this.corpusTokens=new t.SortedSet,this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var t=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,t)},t.Index.prototype.off=function(t,e){return this.eventEmitter.removeListener(t,e)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;return n._fields=e.fields,n._ref=e.ref,n.documentStore=t.Store.load(e.documentStore),n.tokenStore=t.TokenStore.load(e.tokenStore),n.corpusTokens=t.SortedSet.load(e.corpusTokens),n.pipeline=t.Pipeline.load(e.pipeline),n},t.Index.prototype.field=function(t,e){var e=e||{},n={name:t,boost:e.boost||1};return this._fields.push(n),this},t.Index.prototype.ref=function(t){return this._ref=t,this},t.Index.prototype.add=function(e,n){var i={},o=new t.SortedSet,r=e[this._ref],n=void 0===n?!0:n;this._fields.forEach(function(n){var r=this.pipeline.run(t.tokenizer(e[n.name]));i[n.name]=r,t.SortedSet.prototype.add.apply(o,r)},this),this.documentStore.set(r,o),t.SortedSet.prototype.add.apply(this.corpusTokens,o.toArray());for(var s=0;s0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(t.tokenizer(e)),i=new t.Vector,o=[],r=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*r,h=this,l=this.tokenStore.expand(e).reduce(function(n,o){var r=h.corpusTokens.indexOf(o),s=h.idf(o),l=1,u=new t.SortedSet;if(o!==e){var c=Math.max(3,o.length-e.length);l=1/Math.log(c)}return r>-1&&i.insert(r,a*s*l),Object.keys(h.tokenStore.get(o)).forEach(function(t){u.add(t)}),n.union(u)},new t.SortedSet);o.push(l)},this);var a=o.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,o=new t.Vector,r=0;i>r;r++){var s=n.elements[r],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);o.insert(this.corpusTokens.indexOf(s),a*h)}return o},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",o=n+"[^aeiouy]*",r=i+"[aeiou]*",s="^("+o+")?"+r+o,a="^("+o+")?"+r+o+"("+r+")?$",h="^("+o+")?"+r+o+r+o,l="^("+o+")?"+i,u=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(l),p=/^(.+?)(ss|i)es$/,m=/^(.+?)([^s])s$/,v=/^(.+?)eed$/,y=/^(.+?)(ed|ing)$/,g=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),x=new RegExp("^"+o+i+"[^aeiouwxy]$"),k=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,_=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,F=/^(.+?)(s|t)(ion)$/,O=/^(.+?)e$/,P=/ll$/,N=new RegExp("^"+o+i+"[^aeiouwxy]$"),T=function(n){var i,o,r,s,a,h,l;if(n.length<3)return n;if(r=n.substr(0,1),"y"==r&&(n=r.toUpperCase()+n.substr(1)),s=p,a=m,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=v,a=y,s.test(n)){var T=s.exec(n);s=u,s.test(T[1])&&(s=g,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,l=x,a.test(n)?n+="e":h.test(n)?(s=g,n=n.replace(s,"")):l.test(n)&&(n+="e"))}if(s=k,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+t[o])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+e[o])}if(s=_,a=F,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=O,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=N,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=P,a=c,s.test(n)&&a.test(n)&&(s=g,n=n.replace(s,"")),"y"==r&&(n=r.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.stopWordFilter=function(e){return e&&t.stopWordFilter.stopWords[e]!==e?e:void 0},t.stopWordFilter.stopWords={a:"a",able:"able",about:"about",across:"across",after:"after",all:"all",almost:"almost",also:"also",am:"am",among:"among",an:"an",and:"and",any:"any",are:"are",as:"as",at:"at",be:"be",because:"because",been:"been",but:"but",by:"by",can:"can",cannot:"cannot",could:"could",dear:"dear",did:"did","do":"do",does:"does",either:"either","else":"else",ever:"ever",every:"every","for":"for",from:"from",get:"get",got:"got",had:"had",has:"has",have:"have",he:"he",her:"her",hers:"hers",him:"him",his:"his",how:"how",however:"however",i:"i","if":"if","in":"in",into:"into",is:"is",it:"it",its:"its",just:"just",least:"least",let:"let",like:"like",likely:"likely",may:"may",me:"me",might:"might",most:"most",must:"must",my:"my",neither:"neither",no:"no",nor:"nor",not:"not",of:"of",off:"off",often:"often",on:"on",only:"only",or:"or",other:"other",our:"our",own:"own",rather:"rather",said:"said",say:"say",says:"says",she:"she",should:"should",since:"since",so:"so",some:"some",than:"than",that:"that",the:"the",their:"their",them:"them",then:"then",there:"there",these:"these",they:"they","this":"this",tis:"tis",to:"to",too:"too",twas:"twas",us:"us",wants:"wants",was:"was",we:"we",were:"were",what:"what",when:"when",where:"where",which:"which","while":"while",who:"who",whom:"whom",why:"why",will:"will","with":"with",would:"would",yet:"yet",you:"you",your:"your"},t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){var e=t.replace(/^\W+/,"").replace(/\W+$/,"");return""===e?void 0:e},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t[0],o=t.slice(1);return i in n||(n[i]={docs:{}}),0===o.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(o,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n's that are truncated 95 | $('a').each(function(i, el) { 96 | if (el.offsetWidth >= el.scrollWidth) return; 97 | if (typeof el.title === 'undefined') return; 98 | el.title = el.text; 99 | }); 100 | 101 | // restore TOC scroll position 102 | var pos = gs.get('tocScrollTop'); 103 | if (typeof pos !== 'undefined') summary.scrollTop(pos); 104 | 105 | // highlight the TOC item that has same text as the heading in view as scrolling 106 | if (toc && toc.scroll_highlight !== false) (function() { 107 | // scroll the current TOC item into viewport 108 | var ht = $(window).height(), rect = li[0].getBoundingClientRect(); 109 | if (rect.top >= ht || rect.top <= 0 || rect.bottom <= 0) { 110 | summary.scrollTop(li[0].offsetTop); 111 | } 112 | // current chapter TOC items 113 | var items = $('a[href^="' + href + '"]').parent('li.chapter'), 114 | m = items.length; 115 | if (m === 0) { 116 | items = summary.find('li.chapter'); 117 | m = items.length; 118 | } 119 | if (m === 0) return; 120 | // all section titles on current page 121 | var hs = bookInner.find('.page-inner').find('h1,h2,h3'), n = hs.length, 122 | ts = hs.map(function(i, el) { return $(el).text(); }); 123 | if (n === 0) return; 124 | var scrollHandler = function(e) { 125 | var ht = $(window).height(); 126 | clearTimeout($.data(this, 'scrollTimer')); 127 | $.data(this, 'scrollTimer', setTimeout(function() { 128 | // find the first visible title in the viewport 129 | for (var i = 0; i < n; i++) { 130 | var rect = hs[i].getBoundingClientRect(); 131 | if (rect.top >= 0 && rect.bottom <= ht) break; 132 | } 133 | if (i === n) return; 134 | items.removeClass('active'); 135 | for (var j = 0; j < m; j++) { 136 | if (items.eq(j).children('a').first().text() === ts[i]) break; 137 | } 138 | if (j === m) j = 0; // highlight the chapter title 139 | // search bottom-up for a visible TOC item to highlight; if an item is 140 | // hidden, we check if its parent is visible, and so on 141 | while (j > 0 && items.eq(j).is(':hidden')) j--; 142 | items.eq(j).addClass('active'); 143 | }, 250)); 144 | }; 145 | bookInner.on('scroll.bookdown', scrollHandler); 146 | bookBody.on('scroll.bookdown', scrollHandler); 147 | })(); 148 | 149 | // do not refresh the page if the TOC item points to the current page 150 | $('a[href="' + href + '"]').parent('li.chapter').children('a') 151 | .on('click', function(e) { 152 | bookInner.scrollTop(0); 153 | bookBody.scrollTop(0); 154 | return false; 155 | }); 156 | 157 | var toolbar = config.toolbar; 158 | if (!toolbar || toolbar.position !== 'static') { 159 | var bookHeader = $('.book-header'); 160 | bookBody.addClass('fixed'); 161 | bookHeader.addClass('fixed') 162 | .css('background-color', bookBody.css('background-color')) 163 | .on('click.bookdown', function(e) { 164 | // the theme may have changed after user clicks the theme button 165 | bookHeader.css('background-color', bookBody.css('background-color')); 166 | }); 167 | } 168 | 169 | }); 170 | 171 | gitbook.events.bind("page.change", function(e) { 172 | // store TOC scroll position 173 | var summary = $('ul.summary'); 174 | gs.set('tocScrollTop', summary.scrollTop()); 175 | }); 176 | 177 | var bookBody = $('.book-body'), bookInner = bookBody.find('.body-inner'); 178 | var chapterTitle = function() { 179 | return bookInner.find('.page-inner').find('h1,h2').first().text(); 180 | }; 181 | var bookTitle = function() { 182 | return bookInner.find('.book-header > h1').first().text(); 183 | }; 184 | var saveScrollPos = function(e) { 185 | // save scroll position before page is reloaded 186 | gs.set('bodyScrollTop', { 187 | body: bookBody.scrollTop(), 188 | inner: bookInner.scrollTop(), 189 | focused: document.hasFocus(), 190 | title: chapterTitle() 191 | }); 192 | }; 193 | $(document).on('servr:reload', saveScrollPos); 194 | 195 | // check if the page is loaded in an iframe (e.g. the RStudio preview window) 196 | var inIFrame = function() { 197 | var inIframe = true; 198 | try { inIframe = window.self !== window.top; } catch (e) {} 199 | return inIframe; 200 | }; 201 | $(window).on('blur unload', function(e) { 202 | if (inIFrame()) saveScrollPos(e); 203 | gs.set('bookTitle', bookTitle()); 204 | }); 205 | 206 | $(function(e) { 207 | if (gs.get('bookTitle', '') !== bookTitle()) localStorage.clear(); 208 | var pos = gs.get('bodyScrollTop'); 209 | if (pos) { 210 | if (pos.title === chapterTitle()) { 211 | if (pos.body !== 0) bookBody.scrollTop(pos.body); 212 | if (pos.inner !== 0) bookInner.scrollTop(pos.inner); 213 | } 214 | if (pos.focused) bookInner.find('.page-wrapper').focus(); 215 | } 216 | // clear book body scroll position 217 | gs.remove('bodyScrollTop'); 218 | }); 219 | 220 | }); 221 | -------------------------------------------------------------------------------- /docs/libs/gitbook-2.6.7/js/plugin-fontsettings.js: -------------------------------------------------------------------------------- 1 | require(["gitbook", "lodash", "jQuery"], function(gitbook, _, $) { 2 | var fontState; 3 | 4 | var THEMES = { 5 | "white": 0, 6 | "sepia": 1, 7 | "night": 2 8 | }; 9 | 10 | var FAMILY = { 11 | "serif": 0, 12 | "sans": 1 13 | }; 14 | 15 | // Save current font settings 16 | function saveFontSettings() { 17 | gitbook.storage.set("fontState", fontState); 18 | update(); 19 | } 20 | 21 | // Increase font size 22 | function enlargeFontSize(e) { 23 | e.preventDefault(); 24 | if (fontState.size >= 4) return; 25 | 26 | fontState.size++; 27 | saveFontSettings(); 28 | }; 29 | 30 | // Decrease font size 31 | function reduceFontSize(e) { 32 | e.preventDefault(); 33 | if (fontState.size <= 0) return; 34 | 35 | fontState.size--; 36 | saveFontSettings(); 37 | }; 38 | 39 | // Change font family 40 | function changeFontFamily(index, e) { 41 | e.preventDefault(); 42 | 43 | fontState.family = index; 44 | saveFontSettings(); 45 | }; 46 | 47 | // Change type of color 48 | function changeColorTheme(index, e) { 49 | e.preventDefault(); 50 | 51 | var $book = $(".book"); 52 | 53 | if (fontState.theme !== 0) 54 | $book.removeClass("color-theme-"+fontState.theme); 55 | 56 | fontState.theme = index; 57 | if (fontState.theme !== 0) 58 | $book.addClass("color-theme-"+fontState.theme); 59 | 60 | saveFontSettings(); 61 | }; 62 | 63 | function update() { 64 | var $book = gitbook.state.$book; 65 | 66 | $(".font-settings .font-family-list li").removeClass("active"); 67 | $(".font-settings .font-family-list li:nth-child("+(fontState.family+1)+")").addClass("active"); 68 | 69 | $book[0].className = $book[0].className.replace(/\bfont-\S+/g, ''); 70 | $book.addClass("font-size-"+fontState.size); 71 | $book.addClass("font-family-"+fontState.family); 72 | 73 | if(fontState.theme !== 0) { 74 | $book[0].className = $book[0].className.replace(/\bcolor-theme-\S+/g, ''); 75 | $book.addClass("color-theme-"+fontState.theme); 76 | } 77 | }; 78 | 79 | function init(config) { 80 | var $bookBody, $book; 81 | 82 | //Find DOM elements. 83 | $book = gitbook.state.$book; 84 | $bookBody = $book.find(".book-body"); 85 | 86 | // Instantiate font state object 87 | fontState = gitbook.storage.get("fontState", { 88 | size: config.size || 2, 89 | family: FAMILY[config.family || "sans"], 90 | theme: THEMES[config.theme || "white"] 91 | }); 92 | 93 | update(); 94 | }; 95 | 96 | 97 | gitbook.events.bind("start", function(e, config) { 98 | var opts = config.fontsettings; 99 | 100 | // Create buttons in toolbar 101 | gitbook.toolbar.createButton({ 102 | icon: 'fa fa-font', 103 | label: 'Font Settings', 104 | className: 'font-settings', 105 | dropdown: [ 106 | [ 107 | { 108 | text: 'A', 109 | className: 'font-reduce', 110 | onClick: reduceFontSize 111 | }, 112 | { 113 | text: 'A', 114 | className: 'font-enlarge', 115 | onClick: enlargeFontSize 116 | } 117 | ], 118 | [ 119 | { 120 | text: 'Serif', 121 | onClick: _.partial(changeFontFamily, 0) 122 | }, 123 | { 124 | text: 'Sans', 125 | onClick: _.partial(changeFontFamily, 1) 126 | } 127 | ], 128 | [ 129 | { 130 | text: 'White', 131 | onClick: _.partial(changeColorTheme, 0) 132 | }, 133 | { 134 | text: 'Sepia', 135 | onClick: _.partial(changeColorTheme, 1) 136 | }, 137 | { 138 | text: 'Night', 139 | onClick: _.partial(changeColorTheme, 2) 140 | } 141 | ] 142 | ] 143 | }); 144 | 145 | 146 | // Init current settings 147 | init(opts); 148 | }); 149 | }); 150 | 151 | 152 | -------------------------------------------------------------------------------- /docs/libs/gitbook-2.6.7/js/plugin-search.js: -------------------------------------------------------------------------------- 1 | require(["gitbook", "lodash", "jQuery"], function(gitbook, _, $) { 2 | var index = null; 3 | var $searchInput, $searchForm; 4 | var $highlighted, hi = 0, hiOpts = { className: 'search-highlight' }; 5 | var collapse = false; 6 | 7 | // Use a specific index 8 | function loadIndex(data) { 9 | // [Yihui] In bookdown, I use a character matrix to store the chapter 10 | // content, and the index is dynamically built on the client side. 11 | // Gitbook prebuilds the index data instead: https://github.com/GitbookIO/plugin-search 12 | // We can certainly do that via R packages V8 and jsonlite, but let's 13 | // see how slow it really is before improving it. On the other hand, 14 | // lunr cannot handle non-English text very well, e.g. the default 15 | // tokenizer cannot deal with Chinese text, so we may want to replace 16 | // lunr with a dumb simple text matching approach. 17 | index = lunr(function () { 18 | this.ref('url'); 19 | this.field('title', { boost: 10 }); 20 | this.field('body'); 21 | }); 22 | data.map(function(item) { 23 | index.add({ 24 | url: item[0], 25 | title: item[1], 26 | body: item[2] 27 | }); 28 | }); 29 | } 30 | 31 | // Fetch the search index 32 | function fetchIndex() { 33 | return $.getJSON(gitbook.state.basePath+"/search_index.json") 34 | .then(loadIndex); // [Yihui] we need to use this object later 35 | } 36 | 37 | // Search for a term and return results 38 | function search(q) { 39 | if (!index) return; 40 | 41 | var results = _.chain(index.search(q)) 42 | .map(function(result) { 43 | var parts = result.ref.split("#"); 44 | return { 45 | path: parts[0], 46 | hash: parts[1] 47 | }; 48 | }) 49 | .value(); 50 | 51 | // [Yihui] Highlight the search keyword on current page 52 | hi = 0; 53 | $highlighted = results.length === 0 ? undefined : $('.page-inner') 54 | .unhighlight(hiOpts).highlight(q, hiOpts).find('span.search-highlight'); 55 | scrollToHighlighted(); 56 | toggleTOC(results.length > 0); 57 | 58 | return results; 59 | } 60 | 61 | // [Yihui] Scroll the chapter body to the i-th highlighted string 62 | function scrollToHighlighted() { 63 | if (!$highlighted) return; 64 | var n = $highlighted.length; 65 | if (n === 0) return; 66 | var $p = $highlighted.eq(hi), p = $p[0], rect = p.getBoundingClientRect(); 67 | if (rect.top < 0 || rect.bottom > $(window).height()) { 68 | ($(window).width() >= 1240 ? $('.body-inner') : $('.book-body')) 69 | .scrollTop(p.offsetTop - 100); 70 | } 71 | $highlighted.css('background-color', ''); 72 | // an orange background color on the current item and removed later 73 | $p.css('background-color', 'orange'); 74 | setTimeout(function() { 75 | $p.css('background-color', ''); 76 | }, 2000); 77 | } 78 | 79 | // [Yihui] Expand/collapse TOC 80 | function toggleTOC(show) { 81 | if (!collapse) return; 82 | var toc_sub = $('ul.summary').children('li[data-level]').children('ul'); 83 | if (show) return toc_sub.show(); 84 | var href = window.location.pathname; 85 | href = href.substr(href.lastIndexOf('/') + 1); 86 | if (href === '') href = 'index.html'; 87 | var li = $('a[href^="' + href + location.hash + '"]').parent('li.chapter').first(); 88 | toc_sub.hide().parent().has(li).children('ul').show(); 89 | li.children('ul').show(); 90 | } 91 | 92 | // Create search form 93 | function createForm(value) { 94 | if ($searchForm) $searchForm.remove(); 95 | if ($searchInput) $searchInput.remove(); 96 | 97 | $searchForm = $('
', { 98 | 'class': 'book-search', 99 | 'role': 'search' 100 | }); 101 | 102 | $searchInput = $('', { 103 | 'type': 'search', 104 | 'class': 'form-control', 105 | 'val': value, 106 | 'placeholder': 'Type to search' 107 | }); 108 | 109 | $searchInput.appendTo($searchForm); 110 | $searchForm.prependTo(gitbook.state.$book.find('.book-summary')); 111 | } 112 | 113 | // Return true if search is open 114 | function isSearchOpen() { 115 | return gitbook.state.$book.hasClass("with-search"); 116 | } 117 | 118 | // Toggle the search 119 | function toggleSearch(_state) { 120 | if (isSearchOpen() === _state) return; 121 | if (!$searchInput) return; 122 | 123 | gitbook.state.$book.toggleClass("with-search", _state); 124 | 125 | // If search bar is open: focus input 126 | if (isSearchOpen()) { 127 | gitbook.sidebar.toggle(true); 128 | $searchInput.focus(); 129 | } else { 130 | $searchInput.blur(); 131 | $searchInput.val(""); 132 | gitbook.storage.remove("keyword"); 133 | gitbook.sidebar.filter(null); 134 | $('.page-inner').unhighlight(hiOpts); 135 | toggleTOC(false); 136 | } 137 | } 138 | 139 | // Recover current search when page changed 140 | function recoverSearch() { 141 | var keyword = gitbook.storage.get("keyword", ""); 142 | 143 | createForm(keyword); 144 | 145 | if (keyword.length > 0) { 146 | if(!isSearchOpen()) { 147 | toggleSearch(true); // [Yihui] open the search box 148 | } 149 | gitbook.sidebar.filter(_.pluck(search(keyword), "path")); 150 | } 151 | } 152 | 153 | 154 | gitbook.events.bind("start", function(e, config) { 155 | // [Yihui] disable search 156 | if (config.search === false) return; 157 | collapse = !config.toc || config.toc.collapse === 'section' || 158 | config.toc.collapse === 'subsection'; 159 | 160 | // Pre-fetch search index and create the form 161 | fetchIndex() 162 | // [Yihui] recover search after the page is loaded 163 | .then(recoverSearch); 164 | 165 | 166 | // Type in search bar 167 | $(document).on("keyup", ".book-search input", function(e) { 168 | var key = (e.keyCode ? e.keyCode : e.which); 169 | // [Yihui] Escape -> close search box; Up/Down: previous/next highlighted 170 | if (key == 27) { 171 | e.preventDefault(); 172 | toggleSearch(false); 173 | } else if (key == 38) { 174 | if (hi <= 0 && $highlighted) hi = $highlighted.length; 175 | hi--; 176 | scrollToHighlighted(); 177 | } else if (key == 40) { 178 | hi++; 179 | if ($highlighted && hi >= $highlighted.length) hi = 0; 180 | scrollToHighlighted(); 181 | } 182 | }).on("input", ".book-search input", function(e) { 183 | var q = $(this).val().trim(); 184 | if (q.length === 0) { 185 | gitbook.sidebar.filter(null); 186 | gitbook.storage.remove("keyword"); 187 | $('.page-inner').unhighlight(hiOpts); 188 | toggleTOC(false); 189 | } else { 190 | var results = search(q); 191 | gitbook.sidebar.filter( 192 | _.pluck(results, "path") 193 | ); 194 | gitbook.storage.set("keyword", q); 195 | } 196 | }); 197 | 198 | // Create the toggle search button 199 | gitbook.toolbar.createButton({ 200 | icon: 'fa fa-search', 201 | label: 'Search', 202 | position: 'left', 203 | onClick: toggleSearch 204 | }); 205 | 206 | // Bind keyboard to toggle search 207 | gitbook.keyboard.bind(['f'], toggleSearch); 208 | }); 209 | 210 | // [Yihui] do not try to recover search; always start fresh 211 | // gitbook.events.bind("page.change", recoverSearch); 212 | }); 213 | -------------------------------------------------------------------------------- /docs/libs/gitbook-2.6.7/js/plugin-sharing.js: -------------------------------------------------------------------------------- 1 | require(["gitbook", "lodash", "jQuery"], function(gitbook, _, $) { 2 | var SITES = { 3 | 'github': { 4 | 'label': 'Github', 5 | 'icon': 'fa fa-github', 6 | 'onClick': function(e) { 7 | e.preventDefault(); 8 | var repo = $('meta[name="github-repo"]').attr('content'); 9 | if (typeof repo === 'undefined') throw("Github repo not defined"); 10 | window.open("https://github.com/"+repo); 11 | } 12 | }, 13 | 'facebook': { 14 | 'label': 'Facebook', 15 | 'icon': 'fa fa-facebook', 16 | 'onClick': function(e) { 17 | e.preventDefault(); 18 | window.open("http://www.facebook.com/sharer/sharer.php?s=100&p[url]="+encodeURIComponent(location.href)); 19 | } 20 | }, 21 | 'twitter': { 22 | 'label': 'Twitter', 23 | 'icon': 'fa fa-twitter', 24 | 'onClick': function(e) { 25 | e.preventDefault(); 26 | window.open("http://twitter.com/home?status="+encodeURIComponent(document.title+" "+location.href)); 27 | } 28 | }, 29 | 'google': { 30 | 'label': 'Google+', 31 | 'icon': 'fa fa-google-plus', 32 | 'onClick': function(e) { 33 | e.preventDefault(); 34 | window.open("https://plus.google.com/share?url="+encodeURIComponent(location.href)); 35 | } 36 | }, 37 | 'weibo': { 38 | 'label': 'Weibo', 39 | 'icon': 'fa fa-weibo', 40 | 'onClick': function(e) { 41 | e.preventDefault(); 42 | window.open("http://service.weibo.com/share/share.php?content=utf-8&url="+encodeURIComponent(location.href)+"&title="+encodeURIComponent(document.title)); 43 | } 44 | }, 45 | 'instapaper': { 46 | 'label': 'Instapaper', 47 | 'icon': 'fa fa-instapaper', 48 | 'onClick': function(e) { 49 | e.preventDefault(); 50 | window.open("http://www.instapaper.com/text?u="+encodeURIComponent(location.href)); 51 | } 52 | }, 53 | 'vk': { 54 | 'label': 'VK', 55 | 'icon': 'fa fa-vk', 56 | 'onClick': function(e) { 57 | e.preventDefault(); 58 | window.open("http://vkontakte.ru/share.php?url="+encodeURIComponent(location.href)); 59 | } 60 | } 61 | }; 62 | 63 | 64 | 65 | gitbook.events.bind("start", function(e, config) { 66 | var opts = config.sharing; 67 | if (!opts) return; 68 | 69 | // Create dropdown menu 70 | var menu = _.chain(opts.all) 71 | .map(function(id) { 72 | var site = SITES[id]; 73 | 74 | return { 75 | text: site.label, 76 | onClick: site.onClick 77 | }; 78 | }) 79 | .compact() 80 | .value(); 81 | 82 | // Create main button with dropdown 83 | if (menu.length > 0) { 84 | gitbook.toolbar.createButton({ 85 | icon: 'fa fa-share-alt', 86 | label: 'Share', 87 | position: 'right', 88 | dropdown: [menu] 89 | }); 90 | } 91 | 92 | // Direct actions to share 93 | _.each(SITES, function(site, sideId) { 94 | if (!opts[sideId]) return; 95 | 96 | gitbook.toolbar.createButton({ 97 | icon: site.icon, 98 | label: site.text, 99 | position: 'right', 100 | onClick: site.onClick 101 | }); 102 | }); 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /docs/libs/navigation-1.1/tabsets.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * jQuery Plugin: Sticky Tabs 5 | * 6 | * @author Aidan Lister 7 | * adapted by Ruben Arslan to activate parent tabs too 8 | * http://www.aidanlister.com/2014/03/persisting-the-tab-state-in-bootstrap/ 9 | */ 10 | (function($) { 11 | "use strict"; 12 | $.fn.rmarkdownStickyTabs = function() { 13 | var context = this; 14 | // Show the tab corresponding with the hash in the URL, or the first tab 15 | var showStuffFromHash = function() { 16 | var hash = window.location.hash; 17 | var selector = hash ? 'a[href="' + hash + '"]' : 'li.active > a'; 18 | var $selector = $(selector, context); 19 | if($selector.data('toggle') === "tab") { 20 | $selector.tab('show'); 21 | // walk up the ancestors of this element, show any hidden tabs 22 | $selector.parents('.section.tabset').each(function(i, elm) { 23 | var link = $('a[href="#' + $(elm).attr('id') + '"]'); 24 | if(link.data('toggle') === "tab") { 25 | link.tab("show"); 26 | } 27 | }); 28 | } 29 | }; 30 | 31 | 32 | // Set the correct tab when the page loads 33 | showStuffFromHash(context); 34 | 35 | // Set the correct tab when a user uses their back/forward button 36 | $(window).on('hashchange', function() { 37 | showStuffFromHash(context); 38 | }); 39 | 40 | // Change the URL when tabs are clicked 41 | $('a', context).on('click', function(e) { 42 | history.pushState(null, null, this.href); 43 | showStuffFromHash(context); 44 | }); 45 | 46 | return this; 47 | }; 48 | }(jQuery)); 49 | 50 | window.buildTabsets = function(tocID) { 51 | 52 | // build a tabset from a section div with the .tabset class 53 | function buildTabset(tabset) { 54 | 55 | // check for fade and pills options 56 | var fade = tabset.hasClass("tabset-fade"); 57 | var pills = tabset.hasClass("tabset-pills"); 58 | var navClass = pills ? "nav-pills" : "nav-tabs"; 59 | 60 | // determine the heading level of the tabset and tabs 61 | var match = tabset.attr('class').match(/level(\d) /); 62 | if (match === null) 63 | return; 64 | var tabsetLevel = Number(match[1]); 65 | var tabLevel = tabsetLevel + 1; 66 | 67 | // find all subheadings immediately below 68 | var tabs = tabset.find("div.section.level" + tabLevel); 69 | if (!tabs.length) 70 | return; 71 | 72 | // create tablist and tab-content elements 73 | var tabList = $(''); 74 | $(tabs[0]).before(tabList); 75 | var tabContent = $('
'); 76 | $(tabs[0]).before(tabContent); 77 | 78 | // build the tabset 79 | var activeTab = 0; 80 | tabs.each(function(i) { 81 | 82 | // get the tab div 83 | var tab = $(tabs[i]); 84 | 85 | // get the id then sanitize it for use with bootstrap tabs 86 | var id = tab.attr('id'); 87 | 88 | // see if this is marked as the active tab 89 | if (tab.hasClass('active')) 90 | activeTab = i; 91 | 92 | // remove any table of contents entries associated with 93 | // this ID (since we'll be removing the heading element) 94 | $("div#" + tocID + " li a[href='#" + id + "']").parent().remove(); 95 | 96 | // sanitize the id for use with bootstrap tabs 97 | id = id.replace(/[.\/?&!#<>]/g, '').replace(/\s/g, '_'); 98 | tab.attr('id', id); 99 | 100 | // get the heading element within it, grab it's text, then remove it 101 | var heading = tab.find('h' + tabLevel + ':first'); 102 | var headingText = heading.html(); 103 | heading.remove(); 104 | 105 | // build and append the tab list item 106 | var a = $('' + headingText + ''); 107 | a.attr('href', '#' + id); 108 | a.attr('aria-controls', id); 109 | var li = $('
  • '); 110 | li.append(a); 111 | tabList.append(li); 112 | 113 | // set it's attributes 114 | tab.attr('role', 'tabpanel'); 115 | tab.addClass('tab-pane'); 116 | tab.addClass('tabbed-pane'); 117 | if (fade) 118 | tab.addClass('fade'); 119 | 120 | // move it into the tab content div 121 | tab.detach().appendTo(tabContent); 122 | }); 123 | 124 | // set active tab 125 | $(tabList.children('li')[activeTab]).addClass('active'); 126 | var active = $(tabContent.children('div.section')[activeTab]); 127 | active.addClass('active'); 128 | if (fade) 129 | active.addClass('in'); 130 | 131 | if (tabset.hasClass("tabset-sticky")) 132 | tabset.rmarkdownStickyTabs(); 133 | } 134 | 135 | // convert section divs with the .tabset class to tabsets 136 | var tabsets = $("div.section.tabset"); 137 | tabsets.each(function(i) { 138 | buildTabset($(tabsets[i])); 139 | }); 140 | }; 141 | 142 | -------------------------------------------------------------------------------- /docs/r-security-practices.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
    58 | 59 |
    60 | 70 |
    71 | 72 |
    73 |
    74 | 79 | 80 |
    81 |
    82 | 83 |
    84 | 85 | 86 |
    87 |

    1 R security practices

    88 |

    This is a repository of guidance materials to help people improve the security of R usage.

    89 |

    The core guidance is available in the book.

    90 |
    91 |

    1.1 Who this is for

    92 |

    This work is aimed at three (often overlapping) types of people involved with R – users, developers, and administrators.

    93 |
    94 |
    95 |

    1.2 Motivation

    96 |

    The R community is pretty awesome but as a lot of us aren’t trained developers, we can inadvertently do things that are security boo-boos or open us to bad people taking advantage of our systems. I’m sure we’d like to avoid that!

    97 | 98 |
    99 |
    100 |
    101 | 102 |
    103 |
    104 |
    105 | 106 | 107 |
    108 |
    109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /docs/r-users.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | R security practices 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
    97 | 98 |
    99 | 183 |
    184 | 185 |
    186 |
    187 | 192 | 193 |
    194 |
    195 | 196 |
    197 |
    198 |

    3 R Users

    199 |
    200 |

    3.1 Working with the file system

    201 |

    In R you can work with the file system. R can do anything you can do with files across your network so you need to be careful.

    202 |
    203 |

    3.1.0.1 Principles

    204 |
      205 |
    • Interact with files through pre-defined interfaces geared towards the type of file you want to access
    • 206 |
    • Use relative references
    • 207 |
    208 |
    209 |
    210 |

    3.1.0.2 Good examples

    211 |
    212 |
    213 |

    3.1.0.3 Bad examples

    214 |
    215 |
    216 |

    3.1.0.4 Background

    217 |
    218 |
    219 |

    3.1.1 File locations

    220 |
      221 |
    • Working directory
    • 222 |
    • Network files
    • 223 |
    224 |
    225 |

    3.1.1.1 Principles

    226 |
    227 |
    228 |

    3.1.1.2 Good examples

    229 |
    230 |
    231 |

    3.1.1.3 Bad examples

    232 |
    233 |
    234 |

    3.1.1.4 Background

    235 |
    236 |
    237 |
    238 |

    3.1.2 File permissions

    239 |
      240 |
    • Execute permissions
    • 241 |
    • Groups
    • 242 |
    243 |
    244 |
    245 |

    3.1.3 Cleanup

    246 |
      247 |
    • The use of on.exit
    • 248 |
    • The use of unlink
    • 249 |
    250 |
    251 |

    3.1.3.1 Principles

    252 |
    253 |
    254 |

    3.1.3.2 Good examples

    255 |
    256 |
    257 |

    3.1.3.3 Bad examples

    258 |
    259 |
    260 |

    3.1.3.4 Background

    261 |
    262 |
    263 |
    264 |
    265 |

    3.2 Unsafe sourcing

    266 |

    Don’t source files from internet without checking them!

    267 |

    You should understand somethting entirely before you execute it and you should verify that a file is the same file you were expecting.

    268 |

    Using the package notary, you can do this easily:

    269 |
    library(notary)
    270 |
    271 |

    3.2.0.1 Principles

    272 |
    273 |
    274 |

    3.2.0.2 Good examples

    275 |
    276 |
    277 |

    3.2.0.3 Bad examples

    278 |
    279 |
    280 |

    3.2.0.4 Background

    281 |
    282 |
    283 |
    284 |

    3.3 Dynamic string creation & SQL injection

    285 |
    286 |

    3.3.0.1 Principles

    287 |
      288 |
    • Always cleanse parameters that are input by a user
    • 289 |
    290 |
    291 |
    292 |

    3.3.0.2 Good examples

    293 |
    294 |
    3.3.0.2.1 Using DBI
    295 |

    Here we accept a user’s input to some code that constructs a request to the database in a safe way.

    296 |

    This uses DBI but you can also use RODBCext if you use RODBC for interfacing with your database package.

    297 |
    doDBcall <- function(input){
    298 |   basesql <- "SELECT * FROM iris WHERE species = ?species"
    299 |   sqltoexecute<-DBI::sqlInterpolate(DBI::ANSI(), basesql, species = input)
    300 |   # send SQL to the DB with DBI::dbExecute()
    301 |   return(sqltoexecute)
    302 | }
    303 | 
    304 | doDBcall("setosa")
    305 |
    ## <SQL> SELECT * FROM iris WHERE species = 'setosa'
    306 |
    doDBcall("virginica';DROP TABLE iris;")
    307 |
    ## <SQL> SELECT * FROM iris WHERE species = 'virginica'';DROP TABLE iris;'
    308 |
    309 |
    310 |
    3.3.0.2.2 Using shiny
    311 |

    A common thing you might do is make it easy for someone to analyse some data dynamically, by allowing them to change columns. We use the column names to constrain choices instead of letting people type answers in - this is also a better user experience too!

    312 |
    library(shiny)
    313 | shinyApp(
    314 |   ui=fluidPage(selectInput("x","X-axis",
    315 |                     choices=colnames(iris)[-5]),
    316 |         selectInput("y","Y-axis",
    317 |                     choices=colnames(iris)[-5]),
    318 |         plotOutput("myplot")),
    319 |   server=function(input, output) {
    320 |     output$myplot<-renderPlot(plot(iris[,input$x],iris[,input$y]))
    321 |   }
    322 | )
    323 |
    324 |
    325 |
    326 |

    3.3.0.3 Bad examples

    327 |
    328 |
    3.3.0.3.1 Using DBI
    329 |
    doDBcall <- function(input){
    330 |   basesql <- "SELECT * FROM iris WHERE species = '"
    331 |   sqltoexecute<-paste0(basesql,input,"'")
    332 |   # send SQL to the DB with DBI::dbExecute()
    333 |   return(sqltoexecute)
    334 | }
    335 | 
    336 | doDBcall("setosa")
    337 |
    ## [1] "SELECT * FROM iris WHERE species = 'setosa'"
    338 |
    doDBcall("virginica';DROP TABLE iris;")
    339 |
    ## [1] "SELECT * FROM iris WHERE species = 'virginica';DROP TABLE iris;'"
    340 |
    341 |
    342 |
    3.3.0.3.2 Using shiny
    343 |
    344 |
    345 |
    346 |

    3.3.0.4 Background

    347 |

    SQL Injection aka SQLi is one of the most common security issues in web development, and yet is one of the easiest to avoid.

    348 |

    SQLi occurs where a user can input text in an unconstrained manner and what they input isn’t sanitised to prevent them ending the statement you were expecting to execute early and adding their own followup statement.

    349 |
    350 | XKCD Exploits of a Mom 351 |

    XKCD Exploits of a Mom

    352 |
    353 |
    354 |
    355 |
    356 |

    3.4 Password handling

    357 |
      358 |
    • Never store passwords in files that go into source control!
    • 359 |
    360 |

    361 |
    362 |
    363 |

    3.5 System calls

    364 |

    You can execute any command-line code from R. This gives you a lot of access to functionality that can be dangerous!

    365 | 366 |
    367 |
    368 |
    369 | 370 |
    371 |
    372 |
    373 | 374 | 375 |
    376 |
    377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 413 | 414 | 415 | 416 | 417 | -------------------------------------------------------------------------------- /docs/references.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | R security practices 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
    97 | 98 |
    99 | 183 |
    184 | 185 |
    186 |
    187 | 192 | 193 |
    194 |
    195 | 196 |
    197 |
    198 |

    References

    199 | 200 |
    201 |
    202 | 203 |
    204 |
    205 |
    206 | 207 | 208 |
    209 |
    210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 246 | 247 | 248 | 249 | 250 | -------------------------------------------------------------------------------- /docs/search_index.json: -------------------------------------------------------------------------------- 1 | [ 2 | ["r-security-practices.html", "1 R security practices 1.1 Who this is for 1.2 Motivation", " 1 R security practices This is a repository of guidance materials to help people improve the security of R usage. The core guidance is available in the book. 1.1 Who this is for This work is aimed at three (often overlapping) types of people involved with R – users, developers, and administrators. 1.2 Motivation The R community is pretty awesome but as a lot of us aren’t trained developers, we can inadvertently do things that are security boo-boos or open us to bad people taking advantage of our systems. I’m sure we’d like to avoid that! "] 3 | ] 4 | -------------------------------------------------------------------------------- /handouts/01-intro.Rmd: -------------------------------------------------------------------------------- 1 | # Introduction {#intro} 2 | 3 | Welcome to our book on security best practices for R people! 4 | 5 | In our **Users** section, we outline what you need to think about when you're writing scripts and using R day to day. 6 | 7 | In our **Developers** section, we outline best practices to developing R packages securely that helps you avoid doing things that allow bad things to happen because of your package. 8 | 9 | In our **Administrators** section, we outline some best practices for maintaining R to minimise potential security issues. 10 | 11 | The aim of the book is not to make R people security experts, but to give R people some pragmatic, easy-to-follow principles that result in more secure behaviour. We demonstrate the principles with some examples of how to do things and how not to do things. Then, if you're interested in the details behind why you should do these things we provide explanations. 12 | -------------------------------------------------------------------------------- /handouts/02-users.Rmd: -------------------------------------------------------------------------------- 1 | # R Users 2 | 3 | ## Working with the file system 4 | In R you can work with the file system. R can do anything you can do with files across your network so you need to be careful. 5 | 6 | #### Principles 7 | - Interact with files through pre-defined interfaces geared towards the type of file you want to access 8 | - Use relative references 9 | 10 | #### Good examples 11 | #### Bad examples 12 | #### Background 13 | 14 | ### File locations 15 | - Working directory 16 | - Network files 17 | 18 | 19 | #### Principles 20 | #### Good examples 21 | #### Bad examples 22 | #### Background 23 | 24 | ### File permissions 25 | - Execute permissions 26 | - Groups 27 | 28 | ### Cleanup 29 | - The use of `on.exit` 30 | - The use of `unlink` 31 | 32 | 33 | #### Principles 34 | #### Good examples 35 | #### Bad examples 36 | #### Background 37 | 38 | ## Unsafe sourcing 39 | Don't source files from the internet without checking them! 40 | 41 | You should understand something entirely before you execute it and you should verify that a file is the same file you were expecting. 42 | 43 | Using the package `notary`, you can do this easily: 44 | ```{r} 45 | library(notary) 46 | ``` 47 | 48 | 49 | #### Principles 50 | #### Good examples 51 | #### Bad examples 52 | #### Background 53 | 54 | ## Dynamic string creation & SQL injection 55 | 56 | 57 | #### Principles 58 | - Always cleanse parameters that are input by a user 59 | 60 | #### Good examples 61 | 62 | ##### Using DBI 63 | Here we accept a user's input to some code that constructs a request to the database in a safe way. 64 | 65 | This uses DBI but you can also use RODBCext if you use RODBC for interfacing with your database package. 66 | 67 | ```{r} 68 | doDBcall <- function(input){ 69 | basesql <- "SELECT * FROM iris WHERE species = ?species" 70 | sqltoexecute<-DBI::sqlInterpolate(DBI::ANSI(), basesql, species = input) 71 | # send SQL to the DB with DBI::dbExecute() 72 | return(sqltoexecute) 73 | } 74 | 75 | doDBcall("setosa") 76 | doDBcall("virginica';DROP TABLE iris;") 77 | ``` 78 | 79 | ##### Using shiny 80 | A common thing you might do is make it easy for someone to analyse some data dynamically, by allowing them to change columns. We use the column names to constrain choices instead of letting people type answers in - this is also a better user experience too! 81 | 82 | ```{r eval=FALSE} 83 | library(shiny) 84 | shinyApp( 85 | ui=fluidPage(selectInput("x","X-axis", 86 | choices=colnames(iris)[-5]), 87 | selectInput("y","Y-axis", 88 | choices=colnames(iris)[-5]), 89 | plotOutput("myplot")), 90 | server=function(input, output) { 91 | output$myplot<-renderPlot(plot(iris[,input$x],iris[,input$y])) 92 | } 93 | ) 94 | ``` 95 | 96 | #### Bad examples 97 | ##### Using DBI 98 | ```{r} 99 | doDBcall <- function(input){ 100 | basesql <- "SELECT * FROM iris WHERE species = '" 101 | sqltoexecute<-paste0(basesql,input,"'") 102 | # send SQL to the DB with DBI::dbExecute() 103 | return(sqltoexecute) 104 | } 105 | 106 | doDBcall("setosa") 107 | doDBcall("virginica';DROP TABLE iris;") 108 | ``` 109 | 110 | ##### Using shiny 111 | 112 | 113 | #### Background 114 | [SQL Injection](https://www.owasp.org/index.php/SQL_Injection) aka SQLi is one of the most common security issues in web development, and yet is one of the easiest to avoid. 115 | 116 | SQLi occurs where a user can input text in an unconstrained manner and what they input isn't sanitised to prevent them ending the statement you were expecting to execute early and adding their own followup statement. 117 | 118 | ![XKCD Exploits of a Mom](https://imgs.xkcd.com/comics/exploits_of_a_mom.png) 119 | 120 | ## Password handling 121 | - Never store passwords in files that go into source control! 122 | 123 | 124 | 125 | ## System calls 126 | You can execute any command-line code from R. This gives you a lot of access to functionality that can be dangerous! 127 | -------------------------------------------------------------------------------- /handouts/03-developers.Rmd: -------------------------------------------------------------------------------- 1 | # Developers 2 | 3 | ## Preventing unsafe arbitrary code execution 4 | 5 | ### Principles 6 | ### Good examples 7 | ### Bad examples 8 | ### Background 9 | 10 | ## Signing your commits 11 | 12 | ### Principles 13 | ### Good examples 14 | ### Bad examples 15 | ### Background 16 | 17 | ## Storing sensitive information 18 | 19 | ### Principles 20 | ### Good examples 21 | ### Bad examples 22 | ### Background 23 | 24 | ## Downloading dependencies 25 | 26 | ### Principles 27 | ### Good examples 28 | ### Bad examples 29 | ### Background 30 | 31 | ## System dependencies 32 | 33 | ### Principles 34 | ### Good examples 35 | ### Bad examples 36 | ### Background 37 | 38 | ## File interactions 39 | 40 | ### Principles 41 | ### Good examples 42 | ### Bad examples 43 | ### Background 44 | -------------------------------------------------------------------------------- /handouts/04-administrators.Rmd: -------------------------------------------------------------------------------- 1 | # Administrators 2 | 3 | ## Authorative mirrors 4 | 5 | ### Principles 6 | ### Good examples 7 | ### Bad examples 8 | ### Background 9 | 10 | ## Internal CRANs 11 | 12 | ### Principles 13 | ### Good examples 14 | ### Bad examples 15 | ### Background 16 | 17 | ## Validating package installs 18 | 19 | ### Principles 20 | ### Good examples 21 | ### Bad examples 22 | ### Background 23 | 24 | ## Restricting privileges 25 | 26 | ### Principles 27 | ### Good examples 28 | ### Bad examples 29 | ### Background 30 | -------------------------------------------------------------------------------- /handouts/06-references.Rmd: -------------------------------------------------------------------------------- 1 | `r if (knitr:::is_html_output()) '# References {-}'` 2 | -------------------------------------------------------------------------------- /handouts/index.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "R security practices" 3 | author: "Steph Locke" 4 | date: "`r Sys.Date()`" 5 | knit: "bookdown::render_book" 6 | site: bookdown::bookdown_site 7 | output: 8 | bookdown::gitbook: default 9 | bookdown::pdf_book: default 10 | documentclass: book 11 | link-citations: yes 12 | github-repo: ropenscilabs/r-security-practices 13 | description: "A guide for R users about using and developing R securely" 14 | --- 15 | 16 | 17 | # Who this book is for 18 | This book is aimed at three (often overlapping) types of people involved with R -- users, developers, and administrators. 19 | 20 | The R community is pretty awesome but as a lot of us aren't trained developers, we can inadvertently do things that are security boo-boos or open us to bad people taking advantage of our systems. 21 | 22 | As a result, we give guidance to R people to help you avoid common issues. 23 | -------------------------------------------------------------------------------- /roadmap.md: -------------------------------------------------------------------------------- 1 | # Secutity Roadmap 2 | 3 | ## Signing and validation of R packages (and other things from the internet) 4 | 5 | To be fleshed out and annotated as things become clearer. I am a novice in this area so misunderstandings here will probably be shared by other users. 6 | 7 | * `install.packages` - to get this to support signed releases we would have to have signatures with the package and that is going to require CRAN support. We could support this for non-CRAN repos though (drat repos, etc). 8 | * `install_github` - here we need to be able to verify the signature of a github package. Commits can be signed and then that shows up on the releases. To be able to verify this on installation we need to agree that the developer is someone in our web of trust. 9 | * `source` (and other functions like `readLines`) can happily consume urls. We could validate these 10 | * When building packages, libraries might be downloaded (e.g., Jeroen's collection of windows libraries). These should be signed and the signatures verified during installation (we could survey packages that use `configure` scripts to work out how many packages are potentially affected here. 11 | 12 | ## Best practices for avoiding risky behaviour 13 | 14 | * Avoiding SQL injection 15 | * Being careful with `system` 16 | * etc... 17 | 18 | ## Package threat monitoring 19 | 20 | Packages have access to the system at the level of privilege of the user running it. This means that an R package can do all sorts of nasty things that a user can do, particularly with `system`; see [rpwnd](https://github.com/hrbrmstr/rpwnd) for a non-exhaustive list and proof of concept. 21 | 22 | **Static analysis** This will probably be better for detecting risky behaviour than malicious behaviour. There will be patterns that we could detect in the style of `lintr` or `goodpractice`. 23 | 24 | **Dynamic analysis** Run a package in a sand-boxed environment and watch for things like disk and network access. In particular anything that sends information *out* is a problem, but even some reading from remote system could send all sorts of information. Does the package try to read from the file system at all? 25 | 26 | ## Motivating people 27 | 28 | People don't care about this stuff. Pair things that try to improve security with short motivational videos/gifs that show the extent of the exploit and with a video/gif that shows how a best practice/alternative tooling would prevent it. The shorter and more obvious the better. 29 | -------------------------------------------------------------------------------- /rsecuritypractices.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | --------------------------------------------------------------------------------