├── .gitignore ├── Gemfile ├── Gemfile.lock ├── _config.yml ├── _sass └── custom │ └── custom.scss ├── assets ├── css │ └── endpoints.css └── js │ └── endpoints.js ├── docs ├── modules │ ├── assets │ │ ├── apireq.gif │ │ ├── modulereq.gif │ │ └── modulereq2.gif │ └── modules.md ├── python │ ├── helpers │ │ ├── command_helpers │ │ │ └── command_helpers.md │ │ ├── helpers.md │ │ ├── network_helpers │ │ │ └── network_helpers.md │ │ ├── notification_helpers │ │ │ └── notification_helpers.md │ │ └── opkg_helpers │ │ │ └── opkg_helpers.md │ ├── jobs │ │ ├── job │ │ │ └── job.md │ │ ├── job_manager │ │ │ └── job_manager.md │ │ ├── job_runner │ │ │ └── job_runner.md │ │ └── jobs.md │ ├── logger │ │ ├── logger.md │ │ └── pretty_formatter │ │ │ └── pretty_formatter.md │ ├── modules │ │ ├── module │ │ │ └── module.md │ │ ├── modules.md │ │ └── request │ │ │ └── request.md │ └── python.md ├── rest │ ├── authentication │ │ └── authentication.md │ ├── campaigns │ │ └── campaigns.md │ ├── dashboard │ │ └── dashboard.md │ ├── errors │ │ └── errors.md │ ├── generic │ │ └── generic.md │ ├── modules │ │ └── modules.md │ ├── notifications │ │ └── notifications.md │ ├── pineap │ │ └── pineap.md │ ├── recon │ │ └── recon.md │ ├── rest.md │ └── settings │ │ └── settings.md └── typescript │ └── typescript.md └── index.md /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | _site/ 3 | vendor/ 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | # Hello! This is where you manage which Jekyll version is used to run. 3 | # When you want to use a different version, change it below, save the 4 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: 5 | # 6 | # bundle exec jekyll serve 7 | # 8 | # This will help ensure the proper Jekyll version is running. 9 | # Happy Jekylling! 10 | gem "jekyll", "~> 3.8.5" 11 | # This is the default theme for new Jekyll sites. You may change this to anything you like. 12 | #gem "minima", "~> 2.5" 13 | #gem "just-the-docs" 14 | gem "jekyll-remote-theme" 15 | 16 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and 17 | # uncomment the line below. To upgrade, run `bundle update github-pages`. 18 | gem "github-pages", "~> 204", group: :jekyll_plugins 19 | # If you have any plugins, put them here! 20 | 21 | 22 | group :jekyll_plugins do 23 | gem "jekyll-feed", "~> 0.13" 24 | end 25 | 26 | # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem 27 | # and associated library. 28 | install_if -> { RUBY_PLATFORM =~ %r!mingw|mswin|java! } do 29 | gem "tzinfo", "~> 1.2" 30 | gem "tzinfo-data" 31 | end 32 | 33 | # Performance-booster for watching directories on Windows 34 | gem "wdm", "~> 0.1.1", :install_if => Gem.win_platform? 35 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (6.0.3.4) 5 | concurrent-ruby (~> 1.0, >= 1.0.2) 6 | i18n (>= 0.7, < 2) 7 | minitest (~> 5.1) 8 | tzinfo (~> 1.1) 9 | zeitwerk (~> 2.2, >= 2.2.2) 10 | addressable (2.8.0) 11 | public_suffix (>= 2.0.2, < 5.0) 12 | coffee-script (2.4.1) 13 | coffee-script-source 14 | execjs 15 | coffee-script-source (1.11.1) 16 | colorator (1.1.0) 17 | commonmarker (0.17.13) 18 | ruby-enum (~> 0.5) 19 | concurrent-ruby (1.1.7) 20 | dnsruby (1.61.5) 21 | simpleidn (~> 0.1) 22 | em-websocket (0.5.2) 23 | eventmachine (>= 0.12.9) 24 | http_parser.rb (~> 0.6.0) 25 | ethon (0.12.0) 26 | ffi (>= 1.3.0) 27 | eventmachine (1.2.7) 28 | execjs (2.7.0) 29 | faraday (1.3.0) 30 | faraday-net_http (~> 1.0) 31 | multipart-post (>= 1.2, < 3) 32 | ruby2_keywords 33 | faraday-net_http (1.0.1) 34 | ffi (1.14.2) 35 | forwardable-extended (2.6.0) 36 | gemoji (3.0.1) 37 | github-pages (204) 38 | github-pages-health-check (= 1.16.1) 39 | jekyll (= 3.8.5) 40 | jekyll-avatar (= 0.7.0) 41 | jekyll-coffeescript (= 1.1.1) 42 | jekyll-commonmark-ghpages (= 0.1.6) 43 | jekyll-default-layout (= 0.1.4) 44 | jekyll-feed (= 0.13.0) 45 | jekyll-gist (= 1.5.0) 46 | jekyll-github-metadata (= 2.13.0) 47 | jekyll-mentions (= 1.5.1) 48 | jekyll-optional-front-matter (= 0.3.2) 49 | jekyll-paginate (= 1.1.0) 50 | jekyll-readme-index (= 0.3.0) 51 | jekyll-redirect-from (= 0.15.0) 52 | jekyll-relative-links (= 0.6.1) 53 | jekyll-remote-theme (= 0.4.1) 54 | jekyll-sass-converter (= 1.5.2) 55 | jekyll-seo-tag (= 2.6.1) 56 | jekyll-sitemap (= 1.4.0) 57 | jekyll-swiss (= 1.0.0) 58 | jekyll-theme-architect (= 0.1.1) 59 | jekyll-theme-cayman (= 0.1.1) 60 | jekyll-theme-dinky (= 0.1.1) 61 | jekyll-theme-hacker (= 0.1.1) 62 | jekyll-theme-leap-day (= 0.1.1) 63 | jekyll-theme-merlot (= 0.1.1) 64 | jekyll-theme-midnight (= 0.1.1) 65 | jekyll-theme-minimal (= 0.1.1) 66 | jekyll-theme-modernist (= 0.1.1) 67 | jekyll-theme-primer (= 0.5.4) 68 | jekyll-theme-slate (= 0.1.1) 69 | jekyll-theme-tactile (= 0.1.1) 70 | jekyll-theme-time-machine (= 0.1.1) 71 | jekyll-titles-from-headings (= 0.5.3) 72 | jemoji (= 0.11.1) 73 | kramdown (= 1.17.0) 74 | liquid (= 4.0.3) 75 | mercenary (~> 0.3) 76 | minima (= 2.5.1) 77 | nokogiri (>= 1.10.4, < 2.0) 78 | rouge (= 3.13.0) 79 | terminal-table (~> 1.4) 80 | github-pages-health-check (1.16.1) 81 | addressable (~> 2.3) 82 | dnsruby (~> 1.60) 83 | octokit (~> 4.0) 84 | public_suffix (~> 3.0) 85 | typhoeus (~> 1.3) 86 | html-pipeline (2.14.0) 87 | activesupport (>= 2) 88 | nokogiri (>= 1.4) 89 | http_parser.rb (0.6.0) 90 | i18n (0.9.5) 91 | concurrent-ruby (~> 1.0) 92 | jekyll (3.8.5) 93 | addressable (~> 2.4) 94 | colorator (~> 1.0) 95 | em-websocket (~> 0.5) 96 | i18n (~> 0.7) 97 | jekyll-sass-converter (~> 1.0) 98 | jekyll-watch (~> 2.0) 99 | kramdown (~> 1.14) 100 | liquid (~> 4.0) 101 | mercenary (~> 0.3.3) 102 | pathutil (~> 0.9) 103 | rouge (>= 1.7, < 4) 104 | safe_yaml (~> 1.0) 105 | jekyll-avatar (0.7.0) 106 | jekyll (>= 3.0, < 5.0) 107 | jekyll-coffeescript (1.1.1) 108 | coffee-script (~> 2.2) 109 | coffee-script-source (~> 1.11.1) 110 | jekyll-commonmark (1.3.1) 111 | commonmarker (~> 0.14) 112 | jekyll (>= 3.7, < 5.0) 113 | jekyll-commonmark-ghpages (0.1.6) 114 | commonmarker (~> 0.17.6) 115 | jekyll-commonmark (~> 1.2) 116 | rouge (>= 2.0, < 4.0) 117 | jekyll-default-layout (0.1.4) 118 | jekyll (~> 3.0) 119 | jekyll-feed (0.13.0) 120 | jekyll (>= 3.7, < 5.0) 121 | jekyll-gist (1.5.0) 122 | octokit (~> 4.2) 123 | jekyll-github-metadata (2.13.0) 124 | jekyll (>= 3.4, < 5.0) 125 | octokit (~> 4.0, != 4.4.0) 126 | jekyll-mentions (1.5.1) 127 | html-pipeline (~> 2.3) 128 | jekyll (>= 3.7, < 5.0) 129 | jekyll-optional-front-matter (0.3.2) 130 | jekyll (>= 3.0, < 5.0) 131 | jekyll-paginate (1.1.0) 132 | jekyll-readme-index (0.3.0) 133 | jekyll (>= 3.0, < 5.0) 134 | jekyll-redirect-from (0.15.0) 135 | jekyll (>= 3.3, < 5.0) 136 | jekyll-relative-links (0.6.1) 137 | jekyll (>= 3.3, < 5.0) 138 | jekyll-remote-theme (0.4.1) 139 | addressable (~> 2.0) 140 | jekyll (>= 3.5, < 5.0) 141 | rubyzip (>= 1.3.0) 142 | jekyll-sass-converter (1.5.2) 143 | sass (~> 3.4) 144 | jekyll-seo-tag (2.6.1) 145 | jekyll (>= 3.3, < 5.0) 146 | jekyll-sitemap (1.4.0) 147 | jekyll (>= 3.7, < 5.0) 148 | jekyll-swiss (1.0.0) 149 | jekyll-theme-architect (0.1.1) 150 | jekyll (~> 3.5) 151 | jekyll-seo-tag (~> 2.0) 152 | jekyll-theme-cayman (0.1.1) 153 | jekyll (~> 3.5) 154 | jekyll-seo-tag (~> 2.0) 155 | jekyll-theme-dinky (0.1.1) 156 | jekyll (~> 3.5) 157 | jekyll-seo-tag (~> 2.0) 158 | jekyll-theme-hacker (0.1.1) 159 | jekyll (~> 3.5) 160 | jekyll-seo-tag (~> 2.0) 161 | jekyll-theme-leap-day (0.1.1) 162 | jekyll (~> 3.5) 163 | jekyll-seo-tag (~> 2.0) 164 | jekyll-theme-merlot (0.1.1) 165 | jekyll (~> 3.5) 166 | jekyll-seo-tag (~> 2.0) 167 | jekyll-theme-midnight (0.1.1) 168 | jekyll (~> 3.5) 169 | jekyll-seo-tag (~> 2.0) 170 | jekyll-theme-minimal (0.1.1) 171 | jekyll (~> 3.5) 172 | jekyll-seo-tag (~> 2.0) 173 | jekyll-theme-modernist (0.1.1) 174 | jekyll (~> 3.5) 175 | jekyll-seo-tag (~> 2.0) 176 | jekyll-theme-primer (0.5.4) 177 | jekyll (> 3.5, < 5.0) 178 | jekyll-github-metadata (~> 2.9) 179 | jekyll-seo-tag (~> 2.0) 180 | jekyll-theme-slate (0.1.1) 181 | jekyll (~> 3.5) 182 | jekyll-seo-tag (~> 2.0) 183 | jekyll-theme-tactile (0.1.1) 184 | jekyll (~> 3.5) 185 | jekyll-seo-tag (~> 2.0) 186 | jekyll-theme-time-machine (0.1.1) 187 | jekyll (~> 3.5) 188 | jekyll-seo-tag (~> 2.0) 189 | jekyll-titles-from-headings (0.5.3) 190 | jekyll (>= 3.3, < 5.0) 191 | jekyll-watch (2.2.1) 192 | listen (~> 3.0) 193 | jemoji (0.11.1) 194 | gemoji (~> 3.0) 195 | html-pipeline (~> 2.2) 196 | jekyll (>= 3.0, < 5.0) 197 | kramdown (1.17.0) 198 | liquid (4.0.3) 199 | listen (3.4.1) 200 | rb-fsevent (~> 0.10, >= 0.10.3) 201 | rb-inotify (~> 0.9, >= 0.9.10) 202 | mercenary (0.3.6) 203 | minima (2.5.1) 204 | jekyll (>= 3.5, < 5.0) 205 | jekyll-feed (~> 0.9) 206 | jekyll-seo-tag (~> 2.1) 207 | minitest (5.14.3) 208 | multipart-post (2.1.1) 209 | nokogiri (1.13.3-x86_64-linux) 210 | racc (~> 1.4) 211 | octokit (4.20.0) 212 | faraday (>= 0.9) 213 | sawyer (~> 0.8.0, >= 0.5.3) 214 | pathutil (0.16.2) 215 | forwardable-extended (~> 2.6) 216 | public_suffix (3.1.1) 217 | racc (1.6.0) 218 | rb-fsevent (0.10.4) 219 | rb-inotify (0.10.1) 220 | ffi (~> 1.0) 221 | rouge (3.13.0) 222 | ruby-enum (0.8.0) 223 | i18n 224 | ruby2_keywords (0.0.2) 225 | rubyzip (2.3.0) 226 | safe_yaml (1.0.5) 227 | sass (3.7.4) 228 | sass-listen (~> 4.0.0) 229 | sass-listen (4.0.0) 230 | rb-fsevent (~> 0.9, >= 0.9.4) 231 | rb-inotify (~> 0.9, >= 0.9.7) 232 | sawyer (0.8.2) 233 | addressable (>= 2.3.5) 234 | faraday (> 0.8, < 2.0) 235 | simpleidn (0.2.1) 236 | unf (~> 0.1.4) 237 | terminal-table (1.8.0) 238 | unicode-display_width (~> 1.1, >= 1.1.1) 239 | thread_safe (0.3.6) 240 | typhoeus (1.4.0) 241 | ethon (>= 0.9.0) 242 | tzinfo (1.2.9) 243 | thread_safe (~> 0.1) 244 | tzinfo-data (1.2020.6) 245 | tzinfo (>= 1.0.0) 246 | unf (0.1.4) 247 | unf_ext 248 | unf_ext (0.0.7.7) 249 | unicode-display_width (1.7.0) 250 | wdm (0.1.1) 251 | zeitwerk (2.4.2) 252 | 253 | PLATFORMS 254 | x86_64-linux 255 | 256 | DEPENDENCIES 257 | github-pages (~> 204) 258 | jekyll (~> 3.8.5) 259 | jekyll-feed (~> 0.13) 260 | jekyll-remote-theme 261 | tzinfo (~> 1.2) 262 | tzinfo-data 263 | wdm (~> 0.1.1) 264 | 265 | BUNDLED WITH 266 | 2.2.5 267 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: pmarsceill/just-the-docs@v0.3.3 2 | 3 | title: WiFi Pineapple Mark 7 Developer Docs 4 | description: Developer Documentation for the Hak5 WiFi Pineapple Mark 7 5 | baseurl: "/mk7-docs" 6 | url: "https://hak5.github.io" 7 | 8 | permalink: pretty 9 | exclude: ["vendor", ".gem", "Gemfile"] 10 | 11 | search_enabled: true 12 | search: 13 | heading_level: 2 14 | previews: 2 15 | preview_words_before: 3 16 | preview_words_after: 3 17 | tokenizer_seperator: /[\s/\+/ 18 | rel_url: true 19 | button: false 20 | 21 | heading_achors: true 22 | 23 | aux_links: 24 | "View on GitHub": 25 | - "//github.com/hak5/mk7-docs" 26 | aux_links_new_tab: true 27 | 28 | nav_sort: case_sensitive 29 | 30 | back_to_top: true 31 | back_to_top_text: "Back to Top" 32 | 33 | footer_content: "Hak5" 34 | 35 | last_edit_timestamp: true 36 | last_edit_time_format: "%b %e %Y at %I:%M %p" 37 | 38 | gh_edit_link: true 39 | gh_edit_link_text: "Edit on GitHub" 40 | gh_edit_repository: "https://github.com/hak5/mk7-docs" 41 | gh_edit_branch: "gh-pages" 42 | gh_edit_view_mode: "edit" 43 | 44 | color_scheme: "light" 45 | 46 | -------------------------------------------------------------------------------- /_sass/custom/custom.scss: -------------------------------------------------------------------------------- 1 | @media (min-width: 50rem) { 2 | .site-header { 3 | height: 130px; 4 | max-height: 120px; 5 | border-bottom: 1px solid #eeebee; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /assets/css/endpoints.css: -------------------------------------------------------------------------------- 1 | .endpoint-collapsible, .endpoint-collapsible-non-click { 2 | display: flex; 3 | justify-content: space-between; 4 | background-color: #eceff6; 5 | color: white; 6 | cursor: pointer; 7 | padding: 18px; 8 | width: 100%; 9 | border: none; 10 | text-align: left; 11 | outline: none; 12 | font-size: 15px; 13 | } 14 | 15 | .api-name { 16 | color: #7556ed; 17 | } 18 | .api-rest-label { 19 | padding: 3px 3px 3px 3px; 20 | border-radius: 3px; 21 | margin-right: 4px; 22 | } 23 | .api-rest-label-get { 24 | background-color: #52ce0e; 25 | } 26 | .api-rest-label-post { 27 | background-color: #119bdf; 28 | } 29 | .api-rest-label-put { 30 | background-color: #e08f10; 31 | } 32 | .api-rest-label-delete { 33 | background-color: #e02a10; 34 | } 35 | 36 | .api-label-post { 37 | color: #119bdf; 38 | } 39 | .api-label-get { 40 | color: #52ce0e; 41 | } 42 | .api-label-put { 43 | color: #e08f10; 44 | } 45 | .api-label-delete { 46 | color: #e02a10; 47 | } 48 | 49 | .endpoint-panel, .collapsible:hover { 50 | background-color: #d3cde4; 51 | } 52 | 53 | .endpoint-content { 54 | margin-top: -16px; 55 | padding: 14px 18px 14px 18px; 56 | display: none; 57 | overflow: hidden; 58 | background-color: #f5f6fa; 59 | } 60 | 61 | .endpoint-content-always-show { 62 | padding: 14px 18px 14px 18px; 63 | overflow: hidden; 64 | background-color: #f5f6fa; 65 | } 66 | 67 | .code-block div { 68 | background-color: #e2e2e2; 69 | } 70 | 71 | .code-block * { 72 | background-color: #e2e2e2; 73 | } 74 | -------------------------------------------------------------------------------- /assets/js/endpoints.js: -------------------------------------------------------------------------------- 1 | function togglePanel(el) { 2 | el.classList.toggle("endpoint-panel"); 3 | var content = el.parentElement.nextElementSibling; 4 | if (content.style.display === "block") { 5 | content.style.display = "none"; 6 | } else { 7 | content.style.display = "block"; 8 | } 9 | } 10 | 11 | function addHandlers() { 12 | document.querySelectorAll('.endpoint-collapsible').forEach(item => { 13 | item.addEventListener('click', event => { 14 | togglePanel(item); 15 | }) 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /docs/modules/assets/apireq.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hak5/mk7-docs/e14b416e7f3c9f1f2b12f729551476aaeb89ea10/docs/modules/assets/apireq.gif -------------------------------------------------------------------------------- /docs/modules/assets/modulereq.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hak5/mk7-docs/e14b416e7f3c9f1f2b12f729551476aaeb89ea10/docs/modules/assets/modulereq.gif -------------------------------------------------------------------------------- /docs/modules/assets/modulereq2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hak5/mk7-docs/e14b416e7f3c9f1f2b12f729551476aaeb89ea10/docs/modules/assets/modulereq2.gif -------------------------------------------------------------------------------- /docs/modules/modules.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: WiFi Pineapple Module Development Introduction 4 | nav_order: 010 5 | has_children: false 6 | has_toc: true 7 | --- 8 | 9 | # WiFi Pineapple Modules 10 | {: .no_toc } 11 | 12 | Table of contents 13 | {: .text-delta } 14 | 1. TOC 15 | {:toc} 16 | --- 17 | 18 | ## Introduction 19 | WiFi Pineapple Mark VII Modules consist of two major pieces, a front-end and a back-end. 20 | 21 | For the Mark VII, the front-end is written in [Angular](https://angular.io) with [Angular Material](https://material.angular.io). For those familiar with WiFi Pineapple NANO and TETRA module development which used AngularJS, you will find the code to be similar for the most part, but with some new concepts introduced. It is recommended that you become familiar with with the basics of Angular, however the examples below should be fairly easy to follow. 22 | 23 | The back-end can be written in almost any language you would like, however we will be using Python 3 in the examples below, because it has the most mature library and comes by default in the WiFi Pineapple Mark VII firmware. 24 | 25 | To illustrate the full development process, this article will explain how to create a basic WiFi Pineapple Mark VII module that will send and receive data from a Python 3 back-end. 26 | 27 | ## Module Development Overview 28 | As mentioned in the introduction section, the front-end is written in Angular. Because of Angular's differences from the previous generation AngularJS, the front-end is compiled from HTML, TypeScript and CSS into a single Universal Module Definition (UMD). This UMD file is then ingested by the WiFi Pineapple's Web Interface and offers the user of your module interactive controls. 29 | 30 | The back-end design has been further decoupled since the previous generation of the WiFi Pineapple, and it's now easier to write your module back-end in a language that you prefer instead of PHP. We have developed a Python library to make module development much easier, with libraries for other languages in the pipeline. 31 | 32 | When the user visits your module front-end, an API request is sent to the WiFi Pineapple server that will start the back-end. This process opens a [Unix Domain Socket (UDS)](https://en.wikipedia.org/wiki/Unix_domain_socket) that allows the WiFi Pineapple server to forward front-end module requests to your desired back-end, and send the back-end response back to the front-end. 33 | 34 | ## Creating a Module 35 | To start, make sure that you have `python3`, `nodeJS` and `npm` installed on your system. Then, fork the [WiFi Pineapple Mark VII Modules git repository](https://github.com/hak5/mk7-modules). Inside this repository, each module has it's own folder which contains the source-code for the front-end and back-end. 36 | 37 | The repository also contains a script called `create.sh`, which will guide you through module generation. 38 | 39 | ```bash 40 | (mk7-modules) >> ./create.sh 41 | __ ___ ______ _ _____ _ _ 42 | \ \ / (_) ____(_) | __ (_) | | 43 | \ \ /\ / / _| |__ _ | |__) | _ __ ___ __ _ _ __ _ __ | | ___ 44 | \ \/ \/ / | | __| | | | ___/ | '_ \ / _ \/ _` | '_ \| '_ \| |/ _ \ 45 | \ /\ / | | | | | | | | | | | | __/ (_| | |_) | |_) | | __/ 46 | \/ \/ |_|_| |_| |_| |_|_| |_|\___|\__,_| .__/| .__/|_|\___| Mark 7 47 | Module Creation Helper | | | | 48 | Version 1.0 |_| |_| 49 | 50 | 51 | [*] Module Name: MyModule 52 | [*] Module Title: My Module 53 | [*] Module Author: foxtrot 54 | [*] Module Short Description: This is my first module! 55 | [*] Creating New Module (TestModule). 56 | [!] Would you like to prepare the Angular workspace? [Y/n] y 57 | [*] Preparing the Angular workspace. 58 | [*] Prepared the Angular workspace. 59 | [*] A new module has been created! Exiting. 60 | (mk7-modules) >> 61 | ``` 62 | 63 | Once you have generated your module, change directory into it and you'll see the new Angular project that contains some skeleton code for the UI portion of your module. 64 | 65 | There is also a helper script inside your new module directory named build.sh. This simple script will assist in building the module correctly for the WiFi Pineapple and can optionally package it for you. 66 | 67 | ```bash 68 | (MyModule) >> ./build.sh 69 | [*] Angular Build Succeeded 70 | [*] Skipping Packaging (Run ./build.sh package to generate) 71 | (MyModule) >> 72 | ``` 73 | Modules are packaged into a .tar.gz archive, which can be used to upload to the WiFi Pineapple through it's Management UI. 74 | 75 | Alternatively, developers can build their modules without the packaging step, and upload the entire `dist/YourModuleName/` directory to to their WiFi Pineapple's `/pineapple/modules/` directory via SFTP, to speed up the development process. 76 | 77 | ## Editing Module Properties 78 | Each module contains a JSON file (`projects/YourModuleName/src/module.json`) that contains your module's name, author, version and more. 79 | ``` 80 | ───────┬──────────────────────────────────────────────────────────────────────────── 81 | │ File: projects/MyModule/src/module.json 82 | ───────┼──────────────────────────────────────────────────────────────────────────── 83 | 1 │ { 84 | 2 │ "name": "MyModule", 85 | 3 │ "title": "My Module", 86 | 4 │ "description": "This is my first module!", 87 | 5 │ "version": "1.0", 88 | 6 │ "author": "foxtrot" 89 | 7 │ } 90 | ───────┴──────────────────────────────────────────────────────────────────────────── 91 | ``` 92 | 93 | **Note**: It is important to bump your module's version number for every release you submit to the WiFi Pineapple Mark VII Modules Repository. 94 | 95 | ## Writing the Module Front End 96 | For most people, the main directory of interest will be `projects/YourModuleName/src`. It contains the HTML, TypeScript and CSS for your module front-end, and will contain our back-end Python program too. 97 | 98 | ```bash 99 | (src) >> ls 100 | assets lib module.json module.svg public-api.ts 101 | (src) >> ls assets/ 102 | README 103 | (src) >> ls lib/ 104 | components modules MyModule.module.ts services 105 | (src) >> 106 | ``` 107 | 108 | Most editing will be done in the `lib/components` and `lib/services` directory. The `components` directory contains the Angular components that can be rendered in your module's UI. The `services` directory contains services that your components can use to share data. 109 | 110 | The assets directory contains files that will be installed alongside your module once it is packaged, such as configuration files, READMEs, etc. 111 | 112 | By default, there is some boiler-plate example code in the `lib/components/YourModuleName.*` files. 113 | 114 | ``` 115 | ──────┬──────────────────────────────────────────────────────────────────── 116 | │ File: components/MyModule.component.html 117 | ──────┼──────────────────────────────────────────────────────────────────── 118 | 1 │ 119 | 2 │ Welcome to MyModule 120 | 3 │ 121 | 4 │ This is the example module. 122 | 5 │ 123 | 6 │ 124 | ─────────────────────────────────────────────────────────────────────────── 125 | ──────┬──────────────────────────────────────────────────────────────────── 126 | │ File: components/MyModule.component.ts 127 | ──────┼──────────────────────────────────────────────────────────────────── 128 | 1 │ import { Component, OnInit } from '@angular/core'; 129 | 2 │ import { ApiService } from '../services/api.service'; 130 | 3 │ 131 | 4 │ @Component({ 132 | 5 │ selector: 'lib-MyModule', 133 | 6 │ templateUrl: './MyModule.component.html', 134 | 7 │ styleUrls: ['./MyModule.component.css'] 135 | 8 │ }) 136 | 9 │ export class MyModuleComponent implements OnInit { 137 | 10 │ constructor(private API: ApiService) { } 138 | 11 │ 139 | 12 │ ngOnInit() { 140 | 13 │ } 141 | 14 │ } 142 | ─────────────────────────────────────────────────────────────────────────── 143 | ``` 144 | This boiler-plate code can be easily adapted to make an API request and display the output to the module's HTML, by using the `ApiService` service. In this example, we will get the WiFi Pineapple's firmware version from the internal 'status' API endpoint. 145 | 146 | ``` 147 | ──────┬──────────────────────────────────────────────────────────────────── 148 | │ File: components/MyModule.component.html 149 | ──────┼──────────────────────────────────────────────────────────────────── 150 | 1 │ 151 | 2 │ Welcome to MyModule 152 | 3 │ 153 | 4 │ 157 | 8 │
158 | 9 │ The version is: {{ apiResponse }} 159 | 10 │
160 | 11 │
161 | 12 │
162 | ─────────────────────────────────────────────────────────────────────────── 163 | ──────┬──────────────────────────────────────────────────────────────────── 164 | │ File: components/MyModule.component.ts 165 | ──────┼──────────────────────────────────────────────────────────────────── 166 | 1 │ import { Component, OnInit } from '@angular/core'; 167 | 2 │ import { ApiService } from '../services/api.service'; 168 | 3 │ 169 | 4 │ @Component({ 170 | 5 │ selector: 'lib-MyModule', 171 | 6 │ templateUrl: './MyModule.component.html', 172 | 7 │ styleUrls: ['./MyModule.component.css'] 173 | 8 │ }) 174 | 9 │ export class MyModuleComponent implements OnInit { 175 | 10 │ constructor(private API: ApiService) { } 176 | 11 │ 177 | 12 │ apiResponse = 'Press the button above to get the version.'; 178 | 13 │ 179 | 14 │ doAPIAction(): void { 180 | 15 │ this.API.APIGet('/api/status', (response) => { 181 | 16 │ this.apiResponse = response.versionString; 182 | 17 │ }) 183 | 18 │ } 184 | 19 │ 185 | 20 │ ngOnInit() { 186 | 21 │ } 187 | 22 │ } 188 | ─────────────────────────────────────────────────────────────────────────── 189 | ``` 190 | 191 | After building, packaging and uploading the module to the WiFi Pineapple's Management UI, our module now has a button to request the version via the API. 192 | 193 | ![API Request](../assets/apireq.gif) 194 | 195 | ## Writing the Module Back End 196 | Now that we have a working module front-end, we can focus on writing a custom API endpoint for our module. We'll be using the built-in [Python 3 library](x) to write our back-end module. 197 | 198 | As mentioned earlier in this article, the module back-end also lives inside the `projects/YourModuleName/src` directory. **It must be named 'module.py'**. 199 | 200 | As a basic starting point, we'll create a `module.py` with a simple function to send the words 'Hello World' back to the module UI. 201 | 202 | ``` 203 | ──────┬──────────────────────────────────────────────────────────────────── 204 | │ File: module.py 205 | ──────┼──────────────────────────────────────────────────────────────────── 206 | 1 │ #!/usr/bin/env python3 207 | 2 │ 208 | 3 │ import logging 209 | 4 │ 210 | 5 │ from pineapple.modules import Module, Request 211 | 6 │ 212 | 7 │ module = Module('MyModule', logging.DEBUG) 213 | 8 │ 214 | 9 │ @module.handles_action('hello_world') 215 | 10 │ def hello_world(request: Request): 216 | 11 │ return 'Hello World' 217 | 12 │ 218 | 13 │ if __name__ == '__main__': 219 | 14 │ module.start() 220 | ─────────────────────────────────────────────────────────────────────────── 221 | ``` 222 | Looking closer, we can see that we have registered a new module with our module's name on line 7, and defined an action called `hello_world` with a function that returns 'Hello World' on lines 9 through 11. 223 | 224 | We can now update our UI to use our own back-end function. 225 | 226 | ``` 227 | ──────┬──────────────────────────────────────────────────────────────────── 228 | │ File: components/MyModule.component.html 229 | ──────┼──────────────────────────────────────────────────────────────────── 230 | 1 │ 231 | 2 │ Welcome to MyModule 232 | 3 │ 233 | 4 │ 237 | 9 │
238 | 10 │ The API response was: {{ apiResponse }} 239 | 11 │
240 | 12 │
241 | 13 │
242 | ─────────────────────────────────────────────────────────────────────────── 243 | ──────┬──────────────────────────────────────────────────────────────────── 244 | │ File: components/MyModule.component.ts 245 | ──────┼──────────────────────────────────────────────────────────────────── 246 | 1 │ import { Component, OnInit } from '@angular/core'; 247 | 2 │ import { ApiService } from '../services/api.service'; 248 | 3 │ 249 | 4 │ @Component({ 250 | 5 │ selector: 'lib-MyModule', 251 | 6 │ templateUrl: './MyModule.component.html', 252 | 7 │ styleUrls: ['./MyModule.component.css'] 253 | 8 │ }) 254 | 9 │ export class MyModuleComponent implements OnInit { 255 | 10 │ constructor(private API: ApiService) { } 256 | 11 │ 257 | 12 │ apiResponse = 'Press the button above to get the response.'; 258 | 13 │ 259 | 14 │ doAPIAction(): void { 260 | 15 │ this.API.request({ 261 | 16 │ module: 'MyModule', 262 | 17 │ action: 'hello_world', 263 | 18 │ }, (response) => { 264 | 19 │ this.apiResponse = response; 265 | 20 │ }) 266 | 21 │ } 267 | 22 │ 268 | 23 │ ngOnInit() { 269 | 24 │ } 270 | 25 │ } 271 | ─────────────────────────────────────────────────────────────────────────── 272 | ``` 273 | Note in our `doAPIAction()` function that we are no longer using `this.API.APIGet()`, but instead are using `this.API.request()`, which takes two arguments: A JSON structure containing a minimum of `module` (our module's name), `action` (the function we defined earlier in our Python back-end), and a callback function for the module response. Read more about the [TypeScript API here.](https://hak5.github.io/mk7-docs/docs/typescript/typescript/) 274 | 275 | After making those changes, building, packaging and re-uploading it to the WiFi Pineapple, our module now makes a request to a custom function defined in our back-end. 276 | 277 | ![Module Request](../assets/modulereq.gif) 278 | 279 | The last step is getting data sent from your module's front-end to your module's back-end. Luckily, this is as simple as adding to the JSON structure we supply to `this.API.request()` and using the Python library to access it. In the next changes, we'll add a text input to the front-end and adapt our code to use it. 280 | 281 | ``` 282 | ──────┬─────────────────────────────────────────────────────────────────── 283 | │ File: components/MyModule.component.html 284 | ──────┼─────────────────────────────────────────────────────────────────── 285 | 1 │ 286 | 2 │ Welcome to MyModule 287 | 3 │ 288 | 4 │ 289 | 5 │ Message to send to Module 290 | 6 │ 291 | 7 │ 292 | 8 │ 293 | 9 │ 297 | 13 │
298 | 14 │ 299 | 15 │ The API response was: {{ apiResponse }} 300 | 16 │
301 | 17 │
302 | 18 │
303 | ─────────────────────────────────────────────────────────────────────────── 304 | ──────┬──────────────────────────────────────────────────────────────────── 305 | │ File: components/MyModule.component.ts 306 | ──────┼──────────────────────────────────────────────────────────────────── 307 | 1 │ import { Component, OnInit } from '@angular/core'; 308 | 2 │ import { ApiService } from '../services/api.service'; 309 | 3 │ 310 | 4 │ @Component({ 311 | 5 │ selector: 'lib-MyModule', 312 | 6 │ templateUrl: './MyModule.component.html', 313 | 7 │ styleUrls: ['./MyModule.component.css'] 314 | 8 │ }) 315 | 9 │ export class MyModuleComponent implements OnInit { 316 | 10 │ constructor(private API: ApiService) { } 317 | 11 │ 318 | 12 │ userInput = ''; 319 | 13 │ apiResponse = 'Press the button above to get the response.'; 320 | 14 │ 321 | 15 │ doAPIAction(): void { 322 | 16 │ this.API.request({ 323 | 17 │ module: 'MyModule', 324 | 18 │ action: 'hello_world', 325 | 19 │ user_input: this.userInput 326 | 20 │ }, (response) => { 327 | 21 │ this.apiResponse = response; 328 | 22 │ }) 329 | 23 │ } 330 | 24 │ 331 | 25 │ ngOnInit() { 332 | 26 │ } 333 | 27 │ } 334 | ─────────────────────────────────────────────────────────────────────────── 335 | ``` 336 | Looking closer, we can see that in our HTML we've added some Material input and bound it using ngModel to our empty initialized variable in our TypeScript. We've also added the `user_input` property to our module request function, which we can access in our module back-end. 337 | 338 | ``` 339 | ──────┬──────────────────────────────────────────────────────────────────── 340 | │ File: module.py 341 | ──────┼──────────────────────────────────────────────────────────────────── 342 | 1 │ #!/usr/bin/env python3 343 | 2 │ 344 | 3 │ import logging 345 | 4 │ 346 | 5 │ from pineapple.modules import Module, Request 347 | 6 │ 348 | 7 │ module = Module('MyModule', logging.DEBUG) 349 | 8 │ 350 | 9 │ @module.handles_action('hello_world') 351 | 10 │ def check_dependencies(request: Request): 352 | 11 │ return 'You said: {}'.format(request.user_input) 353 | 12 │ 354 | 13 │ if __name__ == '__main__': 355 | 14 │ module.start() 356 | ─────────────────────────────────────────────────────────────────────────── 357 | ``` 358 | Once we build, package and re-upload this version to the WiFi Pineapple, we can test it out and make sure it's accepting our supplied data as expected. 359 | 360 | ![Module Request 2](../assets/modulereq2.gif) 361 | -------------------------------------------------------------------------------- /docs/python/helpers/command_helpers/command_helpers.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Command Helpers 4 | nav_order: 302 5 | parent: Helpers 6 | grand_parent: WiFi Pineapple Python Documentation 7 | has_toc: false 8 | --- 9 | 10 | 11 | 12 | # WiFi Pineapple Python Command Helpers 13 | {: .no_toc } 14 | 15 | Table of contents 16 | {: .text-delta } 17 | 1. TOC 18 | {:toc} 19 | --- 20 | 21 | ## Introduction 22 | Python API 23 | 24 | ## Functions 25 | 31 |
32 |
33 | ```python 34 | def grep_output(command: str, grep_text: str, grep_options: List[str] = None) -> bytes 35 | ``` 36 |
37 | Run a command and pipe it to grep for some output. 38 | The output is returned. 39 |

40 | For example this command:
41 | ps -aux | grep pineap
42 | Looks like this:
43 | grep_output('ps -aux', 'pineap')
44 |
45 |

Parameters

46 | 51 |

Returns

52 | 55 |
56 | 62 |
63 |
64 | ```python 65 | def check_for_process(process_name) -> bool 66 | ``` 67 |
68 | Check if a process is running by its name. 69 |

70 |

Parameters

71 | 74 |

Returns

75 | 78 |
79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /docs/python/helpers/helpers.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Helpers 4 | nav_order: 301 5 | parent: WiFi Pineapple Python Documentation 6 | has_children: true 7 | has_toc: false 8 | --- 9 | 10 | 11 | 12 | # WiFi Pineapple Python Helpers Class 13 | {: .no_toc } 14 | 15 | Table of contents 16 | {: .text-delta } 17 | 1. TOC 18 | {:toc} 19 | --- 20 | 21 | ## Introduction 22 | Python API 23 | 24 | ## Functions 25 | 31 |
32 |
33 | ```python 34 | def json_to_bytes(message) -> bytes 35 | ``` 36 |
37 | JSON deserialize a message and then decode it. 38 |

39 | Use this to convert your json message to bytes before publishing it over the socket. 40 |
41 |

Parameters

42 | 45 |

Returns

46 | 49 |
50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /docs/python/helpers/network_helpers/network_helpers.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Network Helpers 4 | nav_order: 303 5 | parent: Helpers 6 | grand_parent: WiFi Pineapple Python Documentation 7 | has_toc: false 8 | --- 9 | 10 | 11 | 12 | # WiFi Pineapple Python Network Helpers 13 | {: .no_toc } 14 | 15 | Table of contents 16 | {: .text-delta } 17 | 1. TOC 18 | {:toc} 19 | --- 20 | 21 | ## Introduction 22 | Python API 23 | 24 | ## Functions 25 | 31 |
32 |
33 | ```python 34 | def check_for_internet(url: str = 'https://downloads.hak5.org/internet', timout: int = 10, logger: Optional[Logger] = None) -> bool 35 | ``` 36 |
37 | Attempt to connect to a given url. If a connection was established then assume there is an internet connection.
38 | If the connection fails to establish or times out then assume there is not internet. 39 | Run a command and pipe it to grep for some output. 40 |

41 |

Parameters

42 | 47 |

Returns

48 | 51 |
52 | 58 |
59 |
60 | ```python 61 | def get_interfaces() -> List[str] 62 | ``` 63 |
64 | Get a list of current network interfaces. 65 |

66 |

Returns

67 | 70 |
71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /docs/python/helpers/notification_helpers/notification_helpers.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Notification Helpers 4 | nav_order: 304 5 | parent: Helpers 6 | grand_parent: WiFi Pineapple Python Documentation 7 | has_toc: false 8 | --- 9 | 10 | 11 | 12 | # WiFi Pineapple Python Notification Helpers 13 | {: .no_toc } 14 | 15 | Table of contents 16 | {: .text-delta } 17 | 1. TOC 18 | {:toc} 19 | --- 20 | 21 | ## Introduction 22 | Python API 23 | 24 | ## Constants 25 | 29 |
30 |
31 | ```python 32 | INFO = 0 33 | WARN = 1 34 | ERROR = 2 35 | OTHER = 3 36 | SUCCESS = 4 37 | ``` 38 |
39 |

40 |
41 | 42 | ## Functions 43 | 49 |
50 |
51 | ```python 52 | def send_notification(message: str, module_name: str, level: int = INFO) -> bool 53 | ``` 54 |
55 | Send a notification over the WiFi Pineapple's Notification socket 56 |

57 |

Parameters

58 | 63 |

Returns

64 | 67 |
68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /docs/python/helpers/opkg_helpers/opkg_helpers.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Opkg Helpers 4 | nav_order: 305 5 | parent: Helpers 6 | grand_parent: WiFi Pineapple Python Documentation 7 | has_toc: false 8 | --- 9 | 10 | 11 | 12 | # WiFi Pineapple Python Opkg Helpers 13 | {: .no_toc } 14 | 15 | Table of contents 16 | {: .text-delta } 17 | 1. TOC 18 | {:toc} 19 | --- 20 | 21 | ## Introduction 22 | Python API 23 | 24 | ## Classes 25 | 31 |
32 |
33 | ```python 34 | class OpkgJob(Job[bool]) 35 | ``` 36 |
37 | A job to be used with the background JobManager that installs or uninstalls dependencies. 38 |

39 |

Methods

40 | 46 |
47 |
48 | ```python 49 | def do_work(self, logger: Logger) -> bool 50 | ``` 51 |
52 | If `self.package` is a List: 53 | Attempt to install each every package in the list. If a single package fails to install then this method will return False. 54 |

55 |

Parameters

56 | 59 |

Returns

60 | 63 |
64 | 70 |
71 |
72 | ```python 73 | def stop(self) 74 | ``` 75 |
76 | Kill the opkg process if it is running. 77 |
78 |
79 | 80 | ## Functions 81 | 87 |
88 |
89 | ```python 90 | def update_repository(logger: Optional[Logger] = None) -> Tuple[bool, str] 91 | ``` 92 |
93 | Update the opkg package repository. 94 |

95 |

Parameters

96 | 99 |

Returns

100 | 104 |
105 | 111 |
112 |
113 | ```python 114 | def check_if_installed(package: str, logger: Optional[Logger] = None) -> bool 115 | ``` 116 |
117 | Check if a package is already installed via opkg. 118 |

119 |

Parameters

120 | 124 |

Returns

125 | 128 |
129 | 135 |
136 |
137 | ```python 138 | def install_dependency(package: str, logger: Optional[Logger] = None, skip_repo_update: bool = False) -> [bool, str] 139 | ``` 140 |
141 | Install a package via opkg if its not currently installed. 142 |

143 |

Parameters

144 | 149 |

Returns

150 | 154 |
155 | 161 |
162 |
163 | ```python 164 | def uninstall_dependency(package: str, logger: Optional[Logger] = None) -> [bool, str] 165 | ``` 166 |
167 | Uninstall a package via opkg if its currently installed. 168 |

169 |

Parameters

170 | 174 |

Returns

175 | 179 |
180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /docs/python/jobs/job/job.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Job 4 | nav_order: 307 5 | parent: Jobs 6 | grand_parent: WiFi Pineapple Python Documentation 7 | has_toc: false 8 | --- 9 | 10 | 11 | 12 | # WiFi Pineapple Python Job 13 | {: .no_toc } 14 | 15 | Table of contents 16 | {: .text-delta } 17 | 1. TOC 18 | {:toc} 19 | --- 20 | 21 | ## Introduction 22 | Python API 23 | 24 | ## Types 25 | 31 |
32 |
33 | ```python 34 | TResult = TypeVar('TResult') 35 | ``` 36 |
37 |
38 | 39 | 40 | ## Classes 41 | 47 |
48 |
49 | ```python 50 | class Job(Generic[TResult]) 51 | ``` 52 |
53 | A job to be used with the background JobManager that installs or uninstalls dependencies. 54 |

55 |

Properties

56 | 62 |
63 |
64 | ```python 65 | def was_successful(self) -> bool 66 | ``` 67 |
68 | Checks if the job complete without an error. 69 | If the job has not completed or if it complete with no errors return True. 70 | If the job completed with an error then return False. 71 |

72 |

Returns

73 | 76 |
77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /docs/python/jobs/job_manager/job_manager.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Job Manager 4 | nav_order: 308 5 | parent: Jobs 6 | grand_parent: WiFi Pineapple Python Documentation 7 | has_toc: false 8 | --- 9 | 10 | 11 | 12 | # WiFi Pineapple Python Job Manager Class 13 | {: .no_toc } 14 | 15 | Table of contents 16 | {: .text-delta } 17 | 1. TOC 18 | {:toc} 19 | --- 20 | 21 | ## Introduction 22 | Python API 23 | 24 | ## Classes 25 | 31 |
32 |
33 | ```python 34 | class JobManager 35 | ``` 36 |
37 | A job to be used with the background JobManager that installs or uninstalls dependencies. 38 |

39 |

Methods

40 | 46 |
47 |
48 | ```python 49 | def get_job(self, job_id: str, remove_if_complete: bool = True) -> Optional[Job] 50 | ``` 51 |
52 | Attempt to get a job by its id. If the job_id doesn't exist then None is returned. 53 | If `remove_if_complete` is True the job will be deleted from memory only if it is completed. 54 | This is the default behavior to prevent JobManager from tacking up unnecessary memory. 55 |

56 |

Parameters

57 |
    58 |
  • job_id: The id of the job to find.
  • 59 |
  • remove_if_complete: True to delete the job from memory after its complete. (Default: True)
  • 60 |
61 |
62 |

Returns

63 |
    64 |
  • Optional[Job]: an instance of Job if found, else None
  • 65 |
66 |
67 | 73 |
74 |
75 | ```python 76 | def prune_completed_jobs(self) 77 | ``` 78 |
79 | Removes all completed jobs from memory. 80 |
81 |
82 | 88 |
89 |
90 | ```python 91 | def remove_job(self, job_id: str) 92 | ``` 93 |
94 | Remove a job from memory based on its id. 95 | This will remove the job regardless of its completion status. 96 |

97 |

Parameters

98 |
    99 |
  • job_id: The id of the job to delete.
  • 100 |
101 |
102 |
103 | 109 |
110 |
111 | ```python 112 | def execute_job(self, job: Job, callbacks: List[Callable[[Job], None]] = None) -> str 113 | ``` 114 |
115 | Assign an id to a job and execute it in a background thread. 116 | The id will be returned and the job can be tracked by calling `get_job` and providing it the id. 117 |

118 |

Parameters

119 |
    120 |
  • job: an instance of Job to start running.
  • 121 |
  • callbacks: An optional list of functions that take `job` as a parameter to be called when completed. These will be called regardless if `job` raises an exception or not.
  • 122 |
123 |
124 |

Returns

125 |
    126 |
  • str: The id of the running job.
  • 127 |
128 |
129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /docs/python/jobs/job_runner/job_runner.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Job Runner 4 | nav_order: 309 5 | parent: Jobs 6 | grand_parent: WiFi Pineapple Python Documentation 7 | has_toc: false 8 | --- 9 | 10 | 11 | 12 | # WiFi Pineapple Python Job Runner 13 | {: .no_toc } 14 | 15 | Table of contents 16 | {: .text-delta } 17 | 1. TOC 18 | {:toc} 19 | --- 20 | 21 | ## Introduction 22 | Python API 23 | 24 | ## Classes 25 | 31 |
32 |
33 | ```python 34 | class JobRunner(Thread) 35 | ``` 36 |
37 |

Methods

38 | 44 |
45 |
46 | ```python 47 | def run(self) 48 | ``` 49 |
50 | Call the `do_work` method on `self.job` and assign the results to `self.job.result`. 51 | If an exception is raised by the `do_work` method, catch it and set `self.job.error` equal to it. 52 | After `do_work` finishes set `self.job.is_complete` equal to True. 53 |
54 |
55 | 61 |
62 |
63 | ```python 64 | def stop(self) 65 | ``` 66 |
67 | Call the `stop` method on `self.job` if the job is running. 68 |
69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /docs/python/jobs/jobs.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Jobs 4 | nav_order: 306 5 | parent: WiFi Pineapple Python Documentation 6 | has_children: true 7 | has_toc: false 8 | --- 9 | 10 | # WiFi Pineapple Python Jobs Class 11 | {: .no_toc } 12 | 13 | Table of contents 14 | {: .text-delta } 15 | 1. TOC 16 | {:toc} 17 | --- 18 | 19 | ## Introduction 20 | Python API 21 | -------------------------------------------------------------------------------- /docs/python/logger/logger.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Logger 4 | nav_order: 310 5 | parent: WiFi Pineapple Python Documentation 6 | has_children: true 7 | has_toc: false 8 | --- 9 | 10 | 11 | 12 | # WiFi Pineapple Python Logger 13 | {: .no_toc } 14 | 15 | Table of contents 16 | {: .text-delta } 17 | 1. TOC 18 | {:toc} 19 | --- 20 | 21 | ## Introduction 22 | Python API 23 | 24 | ## Functions 25 | 31 |
32 |
33 | ```python 34 | def get_logger(name: str, level: int, log_to_file: bool = True, console_logger_level: int = logging.DEBUG) -> Logger 35 | ``` 36 |
37 |

Parameters

38 |
    39 |
  • name: Name of Logger
  • 40 |
  • level: Logging level.
  • 41 |
  • log_to_file: Log to a file in /tmp/. Default: True
  • 42 |
  • console_logger_level: Console log level Default: DEBUG
  • 43 |
44 |

Returns

45 |
    46 |
  • Logger
  • 47 |
48 |
49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /docs/python/logger/pretty_formatter/pretty_formatter.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Pretty Formatter 4 | nav_order: 311 5 | parent: Logger 6 | grand_parent: WiFi Pineapple Python Documentation 7 | has_toc: false 8 | --- 9 | 10 | # WiFi Pineapple Python Command Helpers 11 | {: .no_toc } 12 | 13 | Table of contents 14 | {: .text-delta } 15 | 1. TOC 16 | {:toc} 17 | --- 18 | 19 | ## Introduction 20 | Python API 21 | 22 | -------------------------------------------------------------------------------- /docs/python/modules/module/module.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Module 4 | nav_order: 314 5 | parent: Modules 6 | grand_parent: WiFi Pineapple Python Documentation 7 | has_toc: false 8 | --- 9 | 10 | 11 | 12 | # WiFi Pineapple Python Module 13 | {: .no_toc } 14 | 15 | Table of contents 16 | {: .text-delta } 17 | 1. TOC 18 | {:toc} 19 | --- 20 | 21 | ## Introduction 22 | Python API 23 | 24 | ## Classes 25 | 31 |
32 |
33 | ```python 34 | class Module 35 | ``` 36 |
37 | A Pineapple Module 38 |

39 |

Methods

40 | 46 |
47 |
48 | ```python 49 | def __init__(self, name: str, log_level: int = logging.WARNING): 50 | ``` 51 |
52 | A WiFi Pineapple Module object in Python. 53 |

54 |

Parameters

55 |
    56 |
  • name: The name of the module.
  • 57 |
  • The level of logging you wish to show. Default WARNING
  • 58 |
59 |
60 |

Returns

61 |
    62 |
  • Optional[Job]: an instance of Job if found, else None
  • 63 |
64 |
65 | 66 | 72 |
73 |
74 | ```python 75 | def shutdown(self, sig=None, frame=None) 76 | ``` 77 |
78 | Attempt to clean shutdown the module. 79 | If your module has anything it needs to close or otherwise cleanup upon shutdown, please override this and do what you need to here. Be sure you call `super.shutdown()` in your new implementation. 80 | 81 | This method may also be called to handle signals such as SIGINT. If it was called as a signal handler the signal `sig` and frame `frame` will be passed into this method. 82 |
83 |
84 | 85 | 91 |
92 |
93 | ```python 94 | def start(self) 95 | ``` 96 |
97 | Main loop for the module which will run as long as `_running` is True. 98 | This will listen for data coming over `_module_socket` and deserialize it to a `Request` object. 99 | That object is then passed to `handle_request` for further processing. 100 |

101 |
102 | 103 | 109 |
110 |
111 | ```python 112 | def register_action_handler(self, action: str, handler: Callable[[Request], Union[Any, Tuple[Any, bool]]]) 113 | ``` 114 |
115 | Manually register an function `handler` to handle an action `action`. 116 | This function will be called anytime a request with the matching action is received. 117 | The action handler must take a positional argument of type `Request`. This must be the first argument. 118 |
119 |
120 | Usage Example:
121 |     module = Module('example')
122 |     def save_file(request: Request) -> Union[Any, Tuple[Any, bool]]:
123 |         ...
124 | 
125 |     module.register_action_handler(save_file)
126 | 
127 |

128 |

Parameters

129 |
    130 |
  • action: The request action to handle
  • 131 |
  • handler: A function that takes `Request` that gets called when the matching `action` is received.
  • 132 |
133 |
134 |
135 | 136 | 142 |
143 |
144 | ```python 145 | def handles_action(self, action: str) 146 | ``` 147 |
148 | A decorator that registers a function as an handler for a given action `action` in a request. 149 | The decorated function is expected take an instance of `Request` as its first argument and can return either Any or a tuple with two values - Any, bool - in that order. 150 | 151 | If the function does not return a tuple, The response is assumed to be successful and the returned value will be json serialized and placed into the 'payload' of the response body. 152 | 153 |
154 |
155 | Example Function:
156 |     @handles_action('save_file')
157 |     def save_file(request: Request) -> str:
158 |         ...
159 |         return 'Filed saved successfully!'
160 | Example Response:
161 |     { "payload": "File saved successfully!" }
162 | 
163 |
164 | If a tuple is returned, the first value in the tuple will the data sent back to the user. The second value must be a boolean that indicates whether the function was successful (True) or not (False). If this value is True, the data in the first index will be sent back in the response payload. 165 | 166 |
167 |
168 | Example Function:
169 |     @handles_action('save_file')
170 |     def save_file(request: Request) -> Tuple[str, bool]:
171 |         ...
172 |         return 'Filed saved successfully!', True
173 | Example Response:
174 |     { "payload": "File saved successfully!" }
175 | 
176 |
177 | 178 | However, if this value is False, The data in the first index will be sent back as an error. 179 |
180 | Example Function:
181 |     @handles_action('save_file')
182 |     def save_file(request: Request) -> Tuple[str, bool]:
183 |         ...
184 |         return 'There was an issue saving the file.', False
185 | Example Response:
186 |     { "error": There was an issue saving the file." }
187 | 
188 |

189 |

Parameters

190 |
    191 |
  • action: The request action to handle
  • 192 |
193 |
194 |
195 | 196 | 202 |
203 |
204 | ```python 205 | def register_shutdown_handler(self, handler: Callable[[Optional[int]], None]) 206 | ``` 207 |
208 | Manually register a function `handler` to be called on the module shutdown lifecycle event. 209 | This handler function must take an integer as a parameter which may be the kill signal sent to the application. 210 | 211 | Depending on how the module is shutdown, the signal value may be None. 212 | 213 |
214 | Example:
215 |     module = Module('example')
216 |     def stop_all_tasks(signal: int):
217 |         ...
218 |     module.register_shutdown_handler(stop_all_tasks)
219 | 
220 |

221 |

Parameters

222 |
    223 |
  • handler: A function to be called on shutdown lifecycle event.
  • 224 |
225 |
226 |
227 | 228 | 229 | 235 |
236 |
237 | ```python 238 | def on_shutdown(self) 239 | ``` 240 |
241 | A decorator that registers a function as a shutdown handler to be called on the shutdown lifecycle event. 242 | 243 | In the example below, the function `stop_all_tasks` will be called when the module process is terminated. 244 | 245 |
246 | Example:
247 |     @module.on_shutdown()
248 |     def stop_all_tasks(signal: int):
249 |         ...
250 | 
251 |

252 |
253 | 254 | 260 |
261 |
262 | ```python 263 | def register_startup_handler(self, handler: Callable[[Optional[int]], None]) 264 | ``` 265 |
266 | Manually register a function `handler` to be called on the module start lifecycle event. 267 | This handler function most not take any arguments. 268 | 269 |
270 | Example:
271 |     module = Module('example')
272 |     def copy_configs():
273 |         ...
274 |     module.register_startup_handler(copy_configs)
275 | 
276 |

277 |

Parameters

278 |
    279 |
  • handler: A function to be called on shutdown lifecycle event.
  • 280 |
281 |
282 |
283 | 284 | 285 | 291 |
292 |
293 | ```python 294 | def on_start(self) 295 | ``` 296 |
297 | A decorator that registers a function as a startup handler to be called on the start lifecycle event. In the example below, the function `copy_configs` will be called when the modules `start` method is called. 298 | 299 |
300 | Example:
301 |     @module.on_start()
302 |     def copy_configs():
303 |         ...
304 | 
305 |

306 |
307 | 308 | 314 |
315 |
316 | ```python 317 | def send_notification(self, message: str, level: int) -> bool 318 | ``` 319 |
320 | Send a notification over the WiFi Pineapples notification socket 321 |

322 |

Parameters

323 |
    324 |
  • message: Notification message
  • 325 |
  • level: Notification level
  • 326 |
327 |
328 |
329 | 330 | 331 | -------------------------------------------------------------------------------- /docs/python/modules/modules.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Modules 4 | nav_order: 312 5 | parent: WiFi Pineapple Python Documentation 6 | has_children: true 7 | has_toc: false 8 | --- 9 | 10 | 11 | # WiFi Pineapple Python Modules 12 | {: .no_toc } 13 | 14 | Table of contents 15 | {: .text-delta } 16 | 1. TOC 17 | {:toc} 18 | --- 19 | 20 | ## Introduction 21 | Python API 22 | 23 | -------------------------------------------------------------------------------- /docs/python/modules/request/request.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Request 4 | nav_order: 313 5 | parent: Modules 6 | grand_parent: WiFi Pineapple Python Documentation 7 | has_toc: false 8 | --- 9 | 10 | 11 | 12 | # WiFi Pineapple Python Requests 13 | {: .no_toc } 14 | 15 | Table of contents 16 | {: .text-delta } 17 | 1. TOC 18 | {:toc} 19 | --- 20 | 21 | ## Introduction 22 | Python API 23 | 24 | ## Classes 25 | 31 |
32 |
33 | ```python 34 | class Module 35 | ``` 36 |
37 | A Pineapple Module 38 |

39 |

Methods

40 | 46 |
47 |
48 | ```python 49 | def __init__(self) 50 | ``` 51 |
52 | A WiFi Pineapple Module Request. Contains at least `self.module` and `self.action`, as well as any parameters given in a TypeScript API request. 53 |

54 |
55 | 56 | 62 |
63 |
64 | ```python 65 | def __repr__(self) 66 | ``` 67 |
68 | Dumps the given data from the API rquest into Request. 69 |
70 |
71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /docs/python/python.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: WiFi Pineapple Python Documentation 4 | nav_order: 300 5 | has_children: true 6 | has_toc: false 7 | --- 8 | 9 | # WiFi Pineapple Python Documentation 10 | {: .no_toc } 11 | 12 | Table of contents 13 | {: .text-delta } 14 | 1. TOC 15 | {:toc} 16 | --- 17 | 18 | ## Introduction 19 | Python API 20 | -------------------------------------------------------------------------------- /docs/rest/authentication/authentication.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Authentication 4 | nav_order: 102 5 | parent: WiFi Pineapple REST Documentation 6 | --- 7 | 8 | 9 | 10 | # REST Authentication 11 | {: .no_toc } 12 | 13 | ## Table of Contents 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | 20 | --- 21 | 22 | ## Introduction 23 | The WiFi Pineapple REST API will only accept requests that are sent with a valid token. Currently, the only way to generate a token is to login as the user. API Token generation is a planned feature. 24 | 25 | ```bash 26 | foxtrot@intent:~$ curl -X POST http://172.16.42.1:1471/api/login -d '{"username": "root", "password": "test"}' 27 | {"token":"eyJVc2VyIjoicm9vdCIsIkV4cGlyeSI6IjIwMjAtMDUtMTdUMTg6NDM6NTEuNjg1NjA5NTJaIn0=.VZpkUmWREeLMtKGx0wZFeWczj8hImbPnulTT5zpnQpM="} 28 | foxtrot@intent:~$ 29 | ``` 30 | 31 | The API endpoint responds with an error for invalid credentials, or a token on success. The token can then be used to make other API requests. 32 | 33 | ```bash 34 | foxtrot@intent:~$ curl -X PUT http://172.16.42.1:1471/api/notifications -H "Authorization: Bearer eyJVc2VyIjoicm9vdCIsIkV4cGlyeSI6IjIwMjAtMDUtMTdUMTg6NDM6NTEuNjg1NjA5NTJaIn0=.VZpkUmWREeLMtKGx0wZFeWczj8hImbPnulTT5zpnQpM=" -d '{"level": 0, "message": "Hello World!"}' 35 | {"success":true} 36 | foxtrot@intent:~$ 37 | ``` 38 | ```bash 39 | foxtrot@intent:~$ curl -X GET http://172.16.42.1:1471/api/notifications -H "Authorization: Bearer eyJVc2VyIjoicm9vdCIsIkV4cGlyeSI6IjIwMjAtMDUtMTdUMTg6NDM6NTEuNjg1NjA5NTJa 40 | In0=.VZpkUmWREeLMtKGx0wZFeWczj8hImbPnulTT5zpnQpM=" 41 | [{"id":1,"message":"Hello World!","level":0,"time":"2020-09-18T10:30:21.031669675Z","read":false,"displayed":false,"module_name":""}] 42 | foxtrot@intent:~$ 43 | ``` 44 | 45 | ## Endpoints 46 | 53 |
54 | Obtain an authentication token.
55 |

Request Body

56 |
57 | ```json 58 | { 59 | "username": string, 60 | "password": string, 61 | } 62 | ``` 63 |
64 | 65 |

Response

66 | If successful, returns authentication token: 67 |
68 | ```json 69 | { 70 | "token": string 71 | } 72 | ``` 73 |
74 | If unsuccessful: See REST Error Responses 75 |
76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /docs/rest/campaigns/campaigns.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Campaigns 4 | nav_order: 106 5 | parent: WiFi Pineapple REST Documentation 6 | --- 7 | 8 | 9 | 10 | # Campaigns 11 | {: .no_toc } 12 | 13 | ## Table of Contents 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | 20 | --- 21 | 22 | ## Introduction 23 | Manage Campaigns 24 | 25 | ## Types 26 | 29 |
30 |
31 | ```typescript 32 | export interface Campaign { 33 | enabled?: boolean; 34 | content?: string; 35 | name: string; 36 | created: number; 37 | type: number; 38 | timeout?: boolean; 39 | } 40 | ``` 41 |
42 |
43 | 46 |
47 |
48 | ```typescript 49 | export interface CampaignReport { 50 | fileName: string; 51 | fullPath: string; 52 | } 53 | ``` 54 |
55 |
56 | 57 | ## Endpoints 58 | 65 |
66 |

Response

67 | If successful, returns all campaigns: 68 |
69 | ```json 70 | { 71 | "campaigns": Campaign[] 72 | } 73 | ``` 74 |
75 | If unsuccessful: [See REST Error Responses](../../errors/errors) 76 |
77 | 84 |
85 |

Response

86 | If successful, returns all campaign reports: 87 |
88 | ```json 89 | { 90 | "fullPath": string, 91 | "fileName": string 92 | } 93 | ``` 94 |
95 | If unsuccessful: [See REST Error Responses](../../errors/errors) 96 |
97 | 104 |
105 |

Response

106 | If successful, returns success state: 107 |
108 | ```json 109 | { 110 | "success": true 111 | } 112 | ``` 113 |
114 | If unsuccessful: [See REST Error Responses](../../errors/errors) 115 |
116 | 123 |
124 |

Request Body

125 |
126 | ```json 127 | { 128 | "name": string, 129 | "mode": number, 130 | "autoRun": bool, 131 | "interval": string, 132 | "plainReport": bool, 133 | "htmlReport": bool, 134 | "storagePath": string, 135 | "enableC2": bool, 136 | "enableC2Exfil": bool 137 | } 138 | ``` 139 |
140 | 141 |

Response

142 | If successful, returns success state: 143 |
144 | ```json 145 | { 146 | "success": true 147 | } 148 | ``` 149 |
150 | If unsuccessful: [See REST Error Responses](../../errors/errors) 151 |
152 | 159 |
160 |

Response

161 | If successful, returns success state: 162 |
163 | ```json 164 | { 165 | "success": true 166 | } 167 | ``` 168 |
169 | If unsuccessful: [See REST Error Responses](../../errors/errors) 170 |
171 | 178 |
179 |

Response

180 | If successful, returns the requested campaign: 181 |
182 | ```json 183 | { 184 | "campaign": Campaign 185 | } 186 | ``` 187 |
188 | If unsuccessful: [See REST Error Responses](../../errors/errors) 189 |
190 | 197 |
198 |

Request Body

199 |
200 | ```json 201 | { 202 | "name": string, 203 | "content": string 204 | } 205 | ``` 206 |
207 | 208 |

Response

209 | If successful, returns success state: 210 |
211 | ```json 212 | { 213 | "success": true 214 | } 215 | ``` 216 |
217 | If unsuccessful: [See REST Error Responses](../../errors/errors) 218 |
219 | 226 |
227 |

Response

228 | If successful, returns success state: 229 |
230 | ```json 231 | { 232 | "success": true 233 | } 234 | ``` 235 |
236 | If unsuccessful: [See REST Error Responses](../../errors/errors) 237 |
238 | 245 |
246 |

Response

247 | If successful, returns success state: 248 |
249 | ```json 250 | { 251 | "success": true 252 | } 253 | ``` 254 |
255 | If unsuccessful: [See REST Error Responses](../../errors/errors) 256 |
257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /docs/rest/dashboard/dashboard.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Dashboard 4 | nav_order: 105 5 | parent: WiFi Pineapple REST Documentation 6 | --- 7 | 8 | 9 | 10 | # Dashboard 11 | {: .no_toc } 12 | 13 | ## Table of Contents 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | --- 20 | 21 | ## Introduction 22 | Dashboard resources and helpers. 23 | 24 | ## Types 25 | 26 | 29 |
30 |
31 | ```typescript 32 | export interface DashboardCardData { 33 | clientsConnected: string; 34 | previousClients: string; 35 | diskUsage: DiskUsageInterface; 36 | mostPopularTraffic: TrafficInterface; 37 | reconScansRan: number; 38 | ssidsSeen: SSIDsSeenInterface; 39 | systemStatus: SystemStatusInterface; 40 | totalBandwidthUsed: number; 41 | } 42 | ``` 43 |
44 |
45 | 48 |
49 |
50 | ```typescript 51 | interface DiskUsageInterface { 52 | rootUsage: string; 53 | } 54 | ``` 55 |
56 |
57 | 60 |
61 |
62 | ```typescript 63 | interface TrafficInterface { 64 | first: string; 65 | second: string; 66 | third: string; 67 | } 68 | ``` 69 |
70 |
71 | 74 |
75 |
76 | ```typescript 77 | interface SSIDsSeenInterface { 78 | totalSSIDs: string; 79 | currentSSIDs: string; 80 | } 81 | ``` 82 |
83 |
84 | 87 |
88 |
89 | ```typescript 90 | interface SystemStatusInterface { 91 | cpuUsage: number; 92 | memoryUsage: number; 93 | temperature: number; 94 | } 95 | ``` 96 |
97 |
98 | 99 | ## Endpoints 100 | 107 |
108 |

Response

109 | If successful, RETURNS: 110 |
111 | ```json 112 | { 113 | "systemStatus": SystemStatus, 114 | "diskUsage": DiskUsage, 115 | "clientsConnected": string, 116 | "previousClients": string, 117 | "ssidsSeen": SSIDsSeen 118 | } 119 | ``` 120 |
121 | If unsuccessful: [See REST Error Responses](../../errors/errors) 122 |
123 | 130 |
131 |

Response

132 | If successful, returns latest news: 133 |
134 | ```json 135 | { 136 | "news": NewsItem[] 137 | } 138 | ``` 139 |
140 | If unsuccessful: [See REST Error Responses](../../errors/errors) 141 |
142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /docs/rest/errors/errors.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: REST Errors 4 | nav_order: 101 5 | parent: WiFi Pineapple REST Documentation 6 | --- 7 | 8 | # REST Errors 9 | {: .no_toc } 10 | 11 | ## Table of Contents 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | 18 | --- 19 | 20 | ## Introduction 21 | The WiFi Pineapple REST API will return an error message when an error (any non-200 response) happens. 22 | 23 | On success, API requests will return a **200 OK** response, and may include a JSON body. 24 | 25 | On error, API requests may return a range of errors, most commonly a **500 Internal Server Error** or a **400 Bad Request**, as well as a JSON body containing an error message: 26 | ```json 27 | { 28 | "error": string 29 | } 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /docs/rest/generic/generic.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Generic 4 | nav_order: 104 5 | parent: WiFi Pineapple REST Documentation 6 | --- 7 | 8 | 9 | 10 | # Generic 11 | {: .no_toc } 12 | 13 | ## Table of Contents 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | 20 | --- 21 | 22 | ## Introduction 23 | Generic actions such as shutdown, reboot, and getting the device status. 24 | 25 | ## Endpoints 26 | 33 |
34 |

Response

35 | If successful, returns success state: 36 |
37 | ```json 38 | { 39 | "success": true 40 | } 41 | ``` 42 |
43 | If unsuccessful: [See REST Error Responses](../../errors/errors) 44 |
45 | 52 |
53 |

Response

54 | If successful, returns success state: 55 |
56 | ```json 57 | { 58 | "success": true 59 | } 60 | ``` 61 |
62 | If unsuccessful: [See REST Error Responses](../../errors/errors) 63 |
64 | 71 |
72 |

Response

73 | If successful, returns device status: 74 |
75 | ```json 76 | { 77 | "versionString": string, 78 | "version": string, 79 | "uptime": number, 80 | "hostname": string, 81 | "time": number 82 | "warnings": PineappleWarnings, 83 | "errors" PineappleErrors 84 | } 85 | ``` 86 |
87 | If unsuccessful: [See REST Error Responses](../../errors/errors) 88 |
89 | 96 |
97 |

Response

98 | If successful, returns device model: 99 |
100 | ```json 101 | { 102 | "device": string 103 | } 104 | ``` 105 |
106 | If unsuccessful: [See REST Error Responses](../../errors/errors) 107 |
108 | 115 |
116 |

Request Body

117 |
118 | ```json 119 | { 120 | "filename": string 121 | } 122 | ``` 123 |
124 | 125 |

Response

126 | If successful, returns stream of specified file 127 |
128 | If unsuccessful: [See REST Error Responses](../../errors/errors) 129 |
130 | 137 |
138 |

Response

139 | If successful, returns an OUI vendor: 140 |
141 | ```json 142 | { 143 | "available": true, 144 | "vendor": string 145 | } 146 | ``` 147 |
148 | If unsuccessful: [See REST Error Responses](../../errors/errors) 149 |
150 | 157 |
158 |

Response

159 | If successful, returns internet connectivity status: 160 |
161 | ```json 162 | { 163 | "online": boolean 164 | } 165 | ``` 166 |
167 | If unsuccessful: [See REST Error Responses](../../errors/errors) 168 |
169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /docs/rest/modules/modules.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Modules 4 | nav_order: 111 5 | parent: WiFi Pineapple REST Documentation 6 | --- 7 | 8 | 9 | 10 | # Modules 11 | {: .no_toc } 12 | 13 | ## Table of Contents 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | 20 | --- 21 | 22 | ## Introduction 23 | Modules are third-party extensions for the WiFi Pineapple. 24 | 25 | ## Endpoints 26 | 33 |
34 |

Request Body

35 |
36 | ```json 37 | { 38 | BODY 39 | } 40 | ``` 41 |
42 | 43 |

Response

44 | If successful, RETURNS: 45 |
46 | ```json 47 | { 48 | RESPONSE 49 | } 50 | ``` 51 |
52 | If unsuccessful: [See REST Error Responses](../../errors/errors) 53 |
54 | 61 |
62 |

Request Body

63 |
64 | ```json 65 | { 66 | BODY 67 | } 68 | ``` 69 |
70 | 71 |

Response

72 | If successful, RETURNS: 73 |
74 | ```json 75 | { 76 | RESPONSE 77 | } 78 | ``` 79 |
80 | If unsuccessful: [See REST Error Responses](../../errors/errors) 81 |
82 | 89 |
90 |

Request Body

91 |
92 | ```json 93 | { 94 | BODY 95 | } 96 | ``` 97 |
98 | 99 |

Response

100 | If successful, RETURNS: 101 |
102 | ```json 103 | { 104 | RESPONSE 105 | } 106 | ``` 107 |
108 | If unsuccessful: [See REST Error Responses](../../errors/errors) 109 |
110 | 117 |
118 |

Request Body

119 |
120 | ```json 121 | { 122 | BODY 123 | } 124 | ``` 125 |
126 | 127 |

Response

128 | If successful, RETURNS: 129 |
130 | ```json 131 | { 132 | RESPONSE 133 | } 134 | ``` 135 |
136 | If unsuccessful: [See REST Error Responses](../../errors/errors) 137 |
138 | 145 |
146 |

Request Body

147 |
148 | ```json 149 | { 150 | BODY 151 | } 152 | ``` 153 |
154 | 155 |

Response

156 | If successful, RETURNS: 157 |
158 | ```json 159 | { 160 | RESPONSE 161 | } 162 | ``` 163 |
164 | If unsuccessful: [See REST Error Responses](../../errors/errors) 165 |
166 | 173 |
174 |

Request Body

175 |
176 | ```json 177 | { 178 | BODY 179 | } 180 | ``` 181 |
182 | 183 |

Response

184 | If successful, RETURNS: 185 |
186 | ```json 187 | { 188 | RESPONSE 189 | } 190 | ``` 191 |
192 | If unsuccessful: [See REST Error Responses](../../errors/errors) 193 |
194 | 201 |
202 |

Request Body

203 |
204 | ```json 205 | { 206 | BODY 207 | } 208 | ``` 209 |
210 | 211 |

Response

212 | If successful, RETURNS: 213 |
214 | ```json 215 | { 216 | RESPONSE 217 | } 218 | ``` 219 |
220 | If unsuccessful: [See REST Error Responses](../../errors/errors) 221 |
222 | 229 |
230 |

Request Body

231 |
232 | ```json 233 | { 234 | BODY 235 | } 236 | ``` 237 |
238 | 239 |

Response

240 | If successful, RETURNS: 241 |
242 | ```json 243 | { 244 | RESPONSE 245 | } 246 | ``` 247 |
248 | If unsuccessful: [See REST Error Responses](../../errors/errors) 249 |
250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /docs/rest/notifications/notifications.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Notifications 4 | nav_order: 103 5 | parent: WiFi Pineapple REST Documentation 6 | --- 7 | 8 | 9 | 10 | # Notifications 11 | {: .no_toc } 12 | 13 | ## Table of Contents 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | 20 | --- 21 | 22 | ## Introduction 23 | The notifications system allows modules or other tools to communicate with the WiFi Pineapple user via the UI. You may specify a notification level, message, and optionally the name of the module that sent the notification. 24 | 25 | ## Endpoints 26 | 33 |
34 |

Request Body

35 |
36 | ```json 37 | { 38 | "level": number, 39 | "message": string, 40 | "module_name": string 41 | } 42 | ``` 43 |
44 | 45 |

Response

46 | If successful, returns success state: 47 |
48 | ```json 49 | { 50 | "success": true 51 | } 52 | ``` 53 |
54 | If unsuccessful: [See REST Error Responses](../../errors/errors) 55 |
56 | 63 |
64 |

Response

65 | If successful, returns an array of notifications: 66 |
67 | ```json 68 | [{ 69 | "id": number, 70 | "message": string, 71 | "level": number, 72 | "time": string, 73 | "read": bool, 74 | "displayed": bool, 75 | "module_name": string 76 | }] 77 | ``` 78 |
79 | If unsuccessful: [See REST Error Responses](../../errors/errors) 80 |
81 | 88 |
89 |

Response

90 | If successful, returns success state: 91 |
92 | ```json 93 | { 94 | "success": true 95 | } 96 | ``` 97 |
98 | If unsuccessful: [See REST Error Responses](../../errors/errors) 99 |
100 | 107 |
108 |

Response

109 | If successful, returns success state: 110 |
111 | ```json 112 | { 113 | "success": true 114 | } 115 | ``` 116 |
117 | If unsuccessful: [See REST Error Responses](../../errors/errors) 118 |
119 | 126 |
127 |

Response

128 | If successful, returns success state: 129 |
130 | ```json 131 | { 132 | "success": true 133 | } 134 | ``` 135 |
136 | If unsuccessful: [See REST Error Responses](../../errors/errors) 137 |
138 | 145 |
146 |

Response

147 | If successful, returns success state: 148 |
149 | ```json 150 | { 151 | "success": true 152 | } 153 | ``` 154 |
155 | If unsuccessful: [See REST Error Responses](../../errors/errors) 156 |
157 | 164 |
165 |

Response

166 | If successful, returns success state: 167 |
168 | ```json 169 | { 170 | "success": true 171 | } 172 | ``` 173 |
174 | If unsuccessful: [See REST Error Responses](../../errors/errors) 175 |
176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /docs/rest/pineap/pineap.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: PineAP 4 | nav_order: 107 5 | parent: WiFi Pineapple REST Documentation 6 | --- 7 | 8 | 9 | 10 | # PineAP 11 | {: .no_toc } 12 | 13 | ## Table of Contents 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | 20 | --- 21 | 22 | ## Introduction 23 | Manage PineAP 24 | 25 | ## Types 26 | 29 |
30 |
31 | ```typescript 32 | export interface PineAPSettings { 33 | "enablePineAP": bool, 34 | "autostartPineAP": bool, 35 | "ap_channel": string, 36 | "beacon_interval": string, 37 | "beacon_response_interval": string, 38 | "beacon_responses": bool, 39 | "broadcast_ssid_pool": bool, 40 | "capture_ssids": bool, 41 | "connect_notifications": bool, 42 | "disconnect_notifications": bool, 43 | "karma": bool, 44 | "logging": bool, 45 | "pineap_mac": string, 46 | "target_mac": string, 47 | } 48 | ``` 49 |
50 |
51 | 54 |
55 |
56 | ```typescript 57 | export interface ChallengeResponse { 58 | "type": string, 59 | "username": string, 60 | "challenge": string, 61 | "response": string 62 | } 63 | ``` 64 |
65 |
66 | 69 |
70 |
71 | ```typescript 72 | export interface BasicResponse { 73 | "type": string, 74 | "identity": string, 75 | "password": string, 76 | } 77 | ``` 78 |
79 |
80 | 83 |
84 |
85 | ```typescript 86 | export interface Log { 87 | "type": int, 88 | "mac": string, 89 | "ssid": string, 90 | "duplicates": int, 91 | "created": int, 92 | "updated": int, 93 | } 94 | ``` 95 |
96 |
97 | 98 | ## Endpoints 99 | 106 |
107 |

Response

108 | If successful, returns current PineAP settings: 109 |
110 | ```json 111 | { 112 | "enablePineAP": bool, 113 | "AutoStart": bool, 114 | "ap_channel": string, 115 | "beacon_interval": string, 116 | "beacon_response_interval": string, 117 | "beacon_responses": bool, 118 | "broadcast_ssid_pool": bool, 119 | "capture_ssids": bool, 120 | "connect_notifications": bool, 121 | "disconnect_notifications": bool, 122 | "karma": bool, 123 | "logging": bool, 124 | "pineap_mac": string, 125 | "target_mac": string 126 | } 127 | ``` 128 |
129 | If unsuccessful: [See REST Error Responses](../../errors/errors) 130 |
131 | 138 |
139 |

Request Body

140 |
141 | ```json 142 | { 143 | "enablePineAP": bool, 144 | "AutoStart": bool, 145 | "ap_channel": string, 146 | "beacon_interval": string, 147 | "beacon_response_interval": string, 148 | "beacon_responses": bool, 149 | "broadcast_ssid_pool": bool, 150 | "capture_ssids": bool, 151 | "connect_notifications": bool, 152 | "disconnect_notifications": bool, 153 | "karma": bool, 154 | "logging": bool, 155 | "pineap_mac": string, 156 | "target_mac": string 157 | } 158 | ``` 159 |
160 | 161 |

Response

162 | If successful, returns success state: 163 |
164 | ```json 165 | { 166 | "success": true 167 | } 168 | ``` 169 |
170 | If unsuccessful: [See REST Error Responses](../../errors/errors) 171 |
172 | 179 |
180 |

Response

181 | If successful, returns a string of SSIDs seperated by newlines. 182 | If unsuccessful: [See REST Error Responses](../../errors/errors) 183 |
184 | 191 |
192 |

Response

193 | If successful, returns: 194 |
195 | ```json 196 | { 197 | "success": true 198 | } 199 | ``` 200 |
201 | If unsuccessful: [See REST Error Responses](../../errors/errors) 202 |
203 | 210 |
211 |

Request Body

212 |
213 | ```json 214 | { 215 | "ssid": string 216 | } 217 | ``` 218 |
219 | 220 |

Response

221 | If successful, returns: 222 |
223 | ```json 224 | { 225 | "success": true 226 | } 227 | ``` 228 |
229 | If unsuccessful: [See REST Error Responses](../../errors/errors) 230 |
231 | 238 |
239 |

Request Body

240 |
241 | ```json 242 | { 243 | "ssid": string 244 | } 245 | ``` 246 |
247 | 248 |

Response

249 | If successful, returns: 250 |
251 | ```json 252 | { 253 | "success": true 254 | } 255 | ``` 256 |
257 | If unsuccessful: [See REST Error Responses](../../errors/errors) 258 |
259 | 266 |
267 |

Response

268 | If successful, returns: 269 |
270 | ```json 271 | { 272 | "handshakes": Handshake[] 273 | } 274 | ``` 275 |
276 | If unsuccessful: [See REST Error Responses](../../errors/errors) 277 |
278 | 285 |
286 |

Response

287 | If successful, returns: 288 |
289 | ```json 290 | { 291 | "success": true 292 | } 293 | ``` 294 |
295 | If unsuccessful: [See REST Error Responses](../../errors/errors) 296 |
297 | 304 |
305 |

Request Body

306 |
307 | ```json 308 | { 309 | "bssid": string, 310 | "channel": number, 311 | } 312 | ``` 313 |
314 | 315 |

Response

316 | If successful, returns: 317 |
318 | ```json 319 | { 320 | "success": true 321 | } 322 | ``` 323 |
324 | If unsuccessful: [See REST Error Responses](../../errors/errors) 325 |
326 | 333 |
334 |

Response

335 | If successful, returns: 336 |
337 | ```json 338 | { 339 | "success": true 340 | } 341 | ``` 342 |
343 | If unsuccessful: [See REST Error Responses](../../errors/errors) 344 |
345 | 352 |
353 |

Response

354 | If successful, returns: 355 |
356 | ```json 357 | { 358 | "captureRunning": bool, 359 | "bssid": string 360 | } 361 | ``` 362 |
363 | If unsuccessful: [See REST Error Responses](../../errors/errors) 364 |
365 | 372 |
373 |

Request Body

374 |
375 | ```json 376 | { 377 | "type": string, 378 | "bssid": bssid 379 | } 380 | ``` 381 |
382 | 383 |

Response

384 | If successful, returns: 385 |
386 | ```json 387 | { 388 | "success": true 389 | } 390 | ``` 391 |
392 | If unsuccessful: [See REST Error Responses](../../errors/errors) 393 |
394 | 401 |
402 |

Response

403 | If successful, returns: 404 |
405 | ```json 406 | { 407 | "enabled": bool, 408 | "associations": bool, 409 | "ssid": string, 410 | "mac": string, 411 | "type": string, 412 | "downgrade": string 413 | } 414 | ``` 415 |
416 | If unsuccessful: [See REST Error Responses](../../errors/errors) 417 |
418 | 425 |
426 |

Request Body

427 |
428 | ```json 429 | { 430 | "enabled": bool, 431 | "associations": bool, 432 | "ssid": string, 433 | "mac": string, 434 | "type": string, 435 | "downgrade": string 436 | } 437 | ``` 438 |
439 | 440 |

Response

441 | If successful, returns: 442 |
443 | ```json 444 | { 445 | "success": true 446 | } 447 | ``` 448 |
449 | If unsuccessful: [See REST Error Responses](../../errors/errors) 450 |
451 | 458 |
459 |

Response

460 | If successful, returns: 461 |
462 | ```json 463 | { 464 | "installed": bool 465 | } 466 | ``` 467 |
468 | If unsuccessful: [See REST Error Responses](../../errors/errors) 469 |
470 | 477 |
478 |

Response

479 | If successful, returns: 480 |
481 | ```json 482 | { 483 | "success": true 484 | } 485 | ``` 486 |
487 | If unsuccessful: [See REST Error Responses](../../errors/errors) 488 |
489 | 496 |
497 |

Request Body

498 |
499 | ```json 500 | { 501 | "state": string, 502 | "country": string, 503 | "locality": string, 504 | "organization": string, 505 | "email": string, 506 | "commonname": string 507 | } 508 | ``` 509 |
510 | 511 |

Response

512 | If successful, returns: 513 |
514 | ```json 515 | { 516 | "success": true 517 | } 518 | ``` 519 |
520 | If unsuccessful: [See REST Error Responses](../../errors/errors) 521 |
522 | 529 |
530 |

Response

531 | If successful, returns `BasicResponse[]` 532 | If unsuccessful: [See REST Error Responses](../../errors/errors) 533 |
534 | 541 |
542 |

Response

543 | If successful, returns: 544 |
545 | ```json 546 | { 547 | "success": true 548 | } 549 | ``` 550 |
551 | If unsuccessful: [See REST Error Responses](../../errors/errors) 552 |
553 | 560 |
561 |

Response

562 | If successful, returns `ChallengeResponse[]`. 563 | If unsuccessful: [See REST Error Responses](../../errors/errors) 564 |
565 | 572 |
573 |

Response

574 | If successful, returns: 575 |
576 | ```json 577 | { 578 | "success": true 579 | } 580 | ``` 581 |
582 | If unsuccessful: [See REST Error Responses](../../errors/errors) 583 |
584 | 591 |
592 |

Response

593 | If successful, returns `Client[]`. 594 | If unsuccessful: [See REST Error Responses](../../errors/errors) 595 |
596 | 603 |
604 |

Response

605 | If successful, returns `number`. 606 | If unsuccessful: [See REST Error Responses](../../errors/errors) 607 |
608 | 615 |
616 |

Request Body

617 |
618 | ```json 619 | { 620 | "mac": string 621 | } 622 | ``` 623 |
624 | 625 |

Response

626 | If successful, returns: 627 |
628 | ```json 629 | { 630 | "success": true 631 | } 632 | ``` 633 |
634 | If unsuccessful: [See REST Error Responses](../../errors/errors) 635 |
636 | 643 |
644 |

Response

645 | If successful, returns `PreviousClient[]`. 646 | If unsuccessful: [See REST Error Responses](../../errors/errors) 647 |
648 | 655 |
656 |

Request Body

657 |
658 | ```json 659 | { 660 | "mac": string 661 | } 662 | ``` 663 |
664 | 665 |

Response

666 | If successful, returns: 667 |
668 | ```json 669 | { 670 | "success": true 671 | } 672 | ``` 673 |
674 | If unsuccessful: [See REST Error Responses](../../errors/errors) 675 |
676 | 683 |
684 |

Response

685 | If successful, returns `Log[]`. 686 | If unsuccessful: [See REST Error Responses](../../errors/errors) 687 |
688 | 695 |
696 |

Response

697 | If successful, returns: 698 |
699 | ```json 700 | { 701 | "mode": string 702 | } 703 | ``` 704 |
705 | If unsuccessful: [See REST Error Responses](../../errors/errors) 706 |
707 | 714 |
715 |

Request Body

716 |
717 | ```json 718 | { 719 | "mode": string 720 | } 721 | ``` 722 |
723 | 724 |

Response

725 | If successful, returns: 726 |
727 | ```json 728 | { 729 | "success": true 730 | } 731 | ``` 732 |
733 | If unsuccessful: [See REST Error Responses](../../errors/errors) 734 |
735 | 742 |
743 |

Response

744 | If successful, returns MAC addresses seperated by newlines. 745 | If unsuccessful: [See REST Error Responses](../../errors/errors) 746 |
747 | 754 |
755 |

Request Body

756 |
757 | ```json 758 | { 759 | "mac": string 760 | } 761 | ``` 762 |
763 | 764 |

Response

765 | If successful, returns: 766 |
767 | ```json 768 | { 769 | "success": true 770 | } 771 | ``` 772 |
773 | If unsuccessful: [See REST Error Responses](../../errors/errors) 774 |
775 | 782 |
783 |

Request Body

784 |
785 | ```json 786 | { 787 | "mac": string 788 | } 789 | ``` 790 |
791 | 792 |

Response

793 | If successful, returns: 794 |
795 | ```json 796 | { 797 | "success": true 798 | } 799 | ``` 800 |
801 | If unsuccessful: [See REST Error Responses](../../errors/errors) 802 |
803 | 810 |
811 |

Response

812 | If successful, returns: 813 |
814 | ```json 815 | { 816 | "mode": string 817 | } 818 | ``` 819 |
820 | If unsuccessful: [See REST Error Responses](../../errors/errors) 821 |
822 | 829 |
830 |

Request Body

831 |
832 | ```json 833 | { 834 | "mode": string 835 | } 836 | ``` 837 |
838 | 839 |

Response

840 | If successful, returns: 841 |
842 | ```json 843 | { 844 | "success": true 845 | } 846 | ``` 847 |
848 | If unsuccessful: [See REST Error Responses](../../errors/errors) 849 |
850 | 857 |
858 |

Response

859 | If successful, returns a string of SSIDs seperated by newlines. 860 | If unsuccessful: [See REST Error Responses](../../errors/errors) 861 |
862 | 869 |
870 |

Request Body

871 |
872 | ```json 873 | { 874 | "mac": string 875 | } 876 | ``` 877 |
878 | 879 |

Response

880 | If successful, returns: 881 |
882 | ```json 883 | { 884 | "success": true 885 | } 886 | ``` 887 |
888 | If unsuccessful: [See REST Error Responses](../../errors/errors) 889 |
890 | 897 |
898 |

Request Body

899 |
900 | ```json 901 | { 902 | "ssid": string 903 | } 904 | ``` 905 |
906 | 907 |

Response

908 | If successful, returns: 909 |
910 | ```json 911 | { 912 | "success": true 913 | } 914 | ``` 915 |
916 | If unsuccessful: [See REST Error Responses](../../errors/errors) 917 |
918 | 925 |
926 |

Request Body

927 |
928 | ```json 929 | { 930 | "bssid": string, 931 | "multiplier": number, 932 | "channel": number, 933 | "clients": string[] 934 | } 935 | ``` 936 |
937 | 938 |

Response

939 | If successful, returns: 940 |
941 | ```json 942 | { 943 | "success": true 944 | } 945 | ``` 946 |
947 | If unsuccessful: [See REST Error Responses](../../errors/errors) 948 |
949 | 956 |
957 |

Request Body

958 |
959 | ```json 960 | { 961 | "bssid": string, 962 | "mac": string, 963 | "multiplier": number, 964 | "channel": number 965 | } 966 | ``` 967 |
968 | 969 |

Response

970 | If successful, returns: 971 |
972 | ```json 973 | { 974 | "success": true 975 | } 976 | ``` 977 |
978 | If unsuccessful: [See REST Error Responses](../../errors/errors) 979 |
980 | 981 | 982 | 983 | 984 | -------------------------------------------------------------------------------- /docs/rest/recon/recon.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Recon 4 | nav_order: 108 5 | parent: WiFi Pineapple REST Documentation 6 | --- 7 | 8 | 9 | 10 | # Recon 11 | {: .no_toc } 12 | 13 | ## Table of Contents 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | 20 | --- 21 | 22 | ## Introduction 23 | Manage Recon 24 | 25 | ## Types 26 | 29 |
30 |
31 | ```typescript 32 | export interface ReconResult { 33 | "APResults": []APResult 34 | "OutOfRangeResult": []APClient 35 | "UnassociatedResult": []APClient 36 | } 37 | ``` 38 |
39 |
40 | 41 | 44 |
45 |
46 | ```typescript 47 | export interface APResult { 48 | "ssid": string 49 | "bssid": string 50 | "encryption": number 51 | "hidden": number 52 | "wps": number 53 | "channel": number 54 | "signal": number 55 | "data": number 56 | "last_seen": number 57 | "probes": number 58 | "clients": []APClient 59 | } 60 | ``` 61 |
62 |
63 | 64 | 67 |
68 |
69 | ```typescript 70 | export interface APClient { 71 | "client_mac": string 72 | "ap_mac": string 73 | "ap_channel": number 74 | "data": number 75 | "broadcast_probes": number 76 | "direct_probes": number 77 | "last_seen": string 78 | } 79 | ``` 80 |
81 |
82 | 83 | ## Endpoints 84 | 91 |
92 |

Request Body

93 |
94 | ```json 95 | { 96 | "live": bool, 97 | "scan_time": number, 98 | "band": string 99 | } 100 | ``` 101 |
102 | 103 |

Response

104 | If successful, returns scan status: 105 |
106 | ```json 107 | { 108 | "scanRunning": bool, 109 | "scanID": number 110 | } 111 | ``` 112 |
113 | If unsuccessful: [See REST Error Responses](../../errors/errors) 114 |
115 | 122 |
123 |

Response

124 | If successful, returns success state: 125 |
126 | ```json 127 | { 128 | "success": true 129 | } 130 | ``` 131 |
132 | If unsuccessful: [See REST Error Responses](../../errors/errors) 133 |
134 | 141 |
142 |

Response

143 | If successful, returns the current scan status: 144 |
145 | ```json 146 | { 147 | "captureRunning": bool, 148 | "scanRunning": bool, 149 | "continuous": bool, 150 | "scanPercent": number, 151 | "scanID": number 152 | } 153 | ``` 154 |
155 | If unsuccessful: [See REST Error Responses](../../errors/errors) 156 |
157 | 164 |
165 |

Response

166 | If successful, returns array of previous scans: 167 |
168 | ```json 169 | [{ 170 | "scan_id": number, 171 | "date": string 172 | }] 173 | ``` 174 |
175 | If unsuccessful: [See REST Error Responses](../../errors/errors) 176 |
177 | 184 |
185 |

Response

186 | If successful, returns specified scan data: 187 |
188 | ```json 189 | { 190 | "APResults": APResult[], 191 | "OutOfRangeClientResults": APClient[], 192 | "UnassociatedClientResults": APClient[] 193 | } 194 | ``` 195 |
196 | If unsuccessful: [See REST Error Responses](../../errors/errors) 197 |
198 | 205 |
206 |

Response

207 | If successful, returns success state: 208 |
209 | ```json 210 | { 211 | "success": true 212 | } 213 | ``` 214 |
215 | If unsuccessful: [See REST Error Responses](../../errors/errors) 216 |
217 | 224 |
225 |

Response

226 | If successful, returns a JSON stream of the specified scan 227 |
228 | If unsuccessful: [See REST Error Responses](../../errors/errors) 229 |
230 | 231 | 232 | 233 | 234 | -------------------------------------------------------------------------------- /docs/rest/rest.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: WiFi Pineapple REST Documentation 4 | nav_order: 100 5 | has_children: true 6 | has_toc: false 7 | --- 8 | 9 | # WiFi Pineapple REST Documentation 10 | {: .no_toc } 11 | 12 | --- 13 | 14 | ## Introduction 15 | The WiFi Pineapple Mark VII exposes a powerful REST API that allows you to control aspects of the device via a WiFi Pineapple Module or independently via HTTP requests. 16 | 17 | ## Sections 18 | - [REST Errors](errors/errors.md) 19 | - [Authentication](authentication/authentication.md) 20 | - [Notifications](notifications/notifications.md) 21 | - [Generic](generic/generic.md) 22 | - [Dashboard](dashboard/dashboard.md) 23 | - [Campaigns](campaigns/campaigns.md) 24 | - [PineAP](pineap/pineap.md) 25 | - [Recon](recon/recon.md) 26 | - [Modules](modules/modules.md) 27 | - [Settings](settings/settings.md) 28 | 29 | 30 | -------------------------------------------------------------------------------- /docs/rest/settings/settings.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Settings 4 | nav_order: 109 5 | parent: WiFi Pineapple REST Documentation 6 | --- 7 | 8 | 9 | 10 | # Settings 11 | {: .no_toc } 12 | 13 | ## Table of Contents 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | 20 | --- 21 | 22 | ## Introduction 23 | Manage Device Settings 24 | 25 | ## Types 26 | 29 |
30 |
31 | ```typescript 32 | export interface Handshake { 33 | "mac" string, 34 | "type": string, 35 | "extension" string, 36 | "source": string 37 | } 38 | ``` 39 |
40 |
41 | 42 | 45 |
46 |
47 | ```typescript 48 | export interface ClientModeNetwork { 49 | bssid: string; 50 | channel: string; 51 | encryption: boolean; 52 | password: string; 53 | quality: string; 54 | ssid: string; 55 | hidden: boolean; 56 | signal: string; 57 | } 58 | ``` 59 |
60 |
61 | 62 | 63 | 64 | ## Endpoints 65 | 72 |
73 |

Request Body

74 |
75 | ```json 76 | { 77 | "old_password": string, 78 | "new_password": string, 79 | "confirm_password": string, 80 | } 81 | ``` 82 |
83 | 84 |

Response

85 | If successful, returns: 86 |
87 | ```json 88 | { 89 | "success": true 90 | } 91 | ``` 92 |
93 | If unsuccessful: [See REST Error Responses](../../errors/errors) 94 |
95 | 102 |
103 |

Response

104 | If successful, returns: 105 |
106 | ```json 107 | { 108 | "timezone": string 109 | } 110 | ``` 111 |
112 | If unsuccessful: [See REST Error Responses](../../errors/errors) 113 |
114 | 121 |
122 |

Request Body

123 |
124 | ```json 125 | { 126 | "timezone": string 127 | } 128 | ``` 129 |
130 | 131 |

Response

132 | If successful, returns: 133 |
134 | ```json 135 | { 136 | "success": true 137 | } 138 | ``` 139 |
140 | If unsuccessful: [See REST Error Responses](../../errors/errors) 141 |
142 | 149 |
150 |

Request Body

151 |
152 | ```json 153 | { 154 | "timestamp": string 155 | } 156 | ``` 157 |
158 | 159 |

Response

160 | If successful, returns: 161 |
162 | ```json 163 | { 164 | "success": true 165 | } 166 | ``` 167 |
168 | If unsuccessful: [See REST Error Responses](../../errors/errors) 169 |
170 | 177 |
178 |

Response

179 | If successful, returns: 180 |
181 | ```json 182 | { 183 | "button_script": string 184 | } 185 | ``` 186 |
187 | If unsuccessful: [See REST Error Responses](../../errors/errors) 188 |
189 | 196 |
197 |

Request Body

198 |
199 | ```json 200 | { 201 | "button_script": string 202 | } 203 | ``` 204 |
205 | 206 |

Response

207 | If successful, returns: 208 |
209 | ```json 210 | { 211 | "success": true 212 | } 213 | ``` 214 |
215 | If unsuccessful: [See REST Error Responses](../../errors/errors) 216 |
217 | 224 |
225 |

Response

226 | If successful, returns `Resources`: 227 | If unsuccessful: [See REST Error Responses](../../errors/errors) 228 |
229 | 236 |
237 |

Response

238 | If successful, returns: 239 |
240 | ```json 241 | { 242 | "devices": string 243 | } 244 | ``` 245 |
246 | If unsuccessful: [See REST Error Responses](../../errors/errors) 247 |
248 | 255 |
256 |

Response

257 | If successful, returns: 258 |
259 | ```json 260 | { 261 | "channel": string 262 | } 263 | ``` 264 |
265 | If unsuccessful: [See REST Error Responses](../../errors/errors) 266 |
267 | 274 |
275 |

Request Body

276 |
277 | ```json 278 | { 279 | "channel": string 280 | } 281 | ``` 282 |
283 | 284 |

Response

285 | If successful, returns: 286 |
287 | ```json 288 | { 289 | "success": true 290 | } 291 | ``` 292 |
293 | If unsuccessful: [See REST Error Responses](../../errors/errors) 294 |
295 | 302 |
303 |

Response

304 | If successful, returns: 305 |
306 | ```json 307 | { 308 | "update_found": bool, 309 | "update_version": string, 310 | "update_changelog": string, 311 | "channel_closed": bool, 312 | } 313 | ``` 314 |
315 | If unsuccessful: [See REST Error Responses](../../errors/errors) 316 |
317 | 324 |
325 |

Response

326 | If successful, returns: 327 |
328 | ```json 329 | { 330 | "success": true 331 | } 332 | ``` 333 |
334 | If unsuccessful: [See REST Error Responses](../../errors/errors) 335 |
336 | 343 |
344 |

Response

345 | If successful, returns: 346 |
347 | ```json 348 | { 349 | "success": true 350 | } 351 | ``` 352 |
353 | If unsuccessful: [See REST Error Responses](../../errors/errors) 354 |
355 | 362 |
363 |

Form Body

364 |
    365 |
  • `c2config`: Cloud C2 Configuration
  • 366 |
367 | 368 |

Response

369 | If successful, returns: 370 |
371 | ```json 372 | { 373 | "success": true 374 | } 375 | ``` 376 |
377 | If unsuccessful: [See REST Error Responses](../../errors/errors) 378 |
379 | 386 |
387 |

Response

388 | If successful, returns: 389 |
390 | ```json 391 | { 392 | "success": true 393 | } 394 | ``` 395 |
396 | If unsuccessful: [See REST Error Responses](../../errors/errors) 397 |
398 | 405 |
406 |

Response

407 | If successful, returns: 408 |
409 | ```json 410 | { 411 | "enrolled": bool 412 | } 413 | ``` 414 |
415 | If unsuccessful: [See REST Error Responses](../../errors/errors) 416 |
417 | 424 |
425 |

Response

426 | If successful, returns: 427 |
428 | ```json 429 | { 430 | "routes": string 431 | } 432 | ``` 433 |
434 | If unsuccessful: [See REST Error Responses](../../errors/errors) 435 |
436 | 443 |
444 |

Response

445 | If successful, returns: 446 |
447 | ```json 448 | { 449 | "ssid": string, 450 | "password": string, 451 | "hidden": bool, 452 | "disabled": bool 453 | } 454 | ``` 455 |
456 | If unsuccessful: [See REST Error Responses](../../errors/errors) 457 |
458 | 465 |
466 |

Request Body

467 |
468 | ```json 469 | { 470 | "ssid": string, 471 | "password": string, 472 | "confirm_password": string, 473 | "hidden": bool, 474 | "disabled": bool 475 | } 476 | ``` 477 |
478 | 479 |

Response

480 | If successful, returns: 481 |
482 | ```json 483 | { 484 | "success": true 485 | } 486 | ``` 487 |
488 | If unsuccessful: [See REST Error Responses](../../errors/errors) 489 |
490 | 497 |
498 |

Response

499 | If successful, returns: 500 |
501 | ```json 502 | { 503 | "ssid": string, 504 | "country": string, 505 | "channel": number, 506 | "hidden": bool 507 | } 508 | ``` 509 |
510 | If unsuccessful: [See REST Error Responses](../../errors/errors) 511 |
512 | 519 |
520 |

Request Body

521 |
522 | ```json 523 | { 524 | "ssid": string, 525 | "country": string, 526 | "channel": number, 527 | "hidden": bool 528 | } 529 | ``` 530 |
531 | 532 |

Response

533 | If successful, returns: 534 |
535 | ```json 536 | { 537 | "success": true 538 | } 539 | ``` 540 |
541 | If unsuccessful: [See REST Error Responses](../../errors/errors) 542 |
543 | 550 |
551 |

Response

552 | If successful, returns: 553 |
554 | ```json 555 | { 556 | "ssid": string, 557 | "bssid": string, 558 | "auth": string, 559 | "password": string, 560 | "hidden": bool, 561 | "disabled": bool, 562 | "capture_handshakes": bool 563 | } 564 | ``` 565 |
566 | If unsuccessful: [See REST Error Responses](../../errors/errors) 567 |
568 | 575 |
576 |

Request Body

577 |
578 | ```json 579 | { 580 | "ssid": string, 581 | "bssid": string, 582 | "auth": string, 583 | "password": string, 584 | "confirm_password": string, 585 | "hidden": bool, 586 | "disabled": bool, 587 | "capture_handshakes": bool 588 | } 589 | ``` 590 |
591 | 592 |

Response

593 | If successful, returns: 594 |
595 | ```json 596 | { 597 | "success": true 598 | } 599 | ``` 600 |
601 | If unsuccessful: [See REST Error Responses](../../errors/errors) 602 |
603 | 610 |
611 |

Response

612 | If successful, returns: 613 |
614 | ```json 615 | { 616 | "interfaces": string[], 617 | "connected": bool, 618 | "ssid": string, 619 | "ip": string 620 | } 621 | ``` 622 |
623 | If unsuccessful: [See REST Error Responses](../../errors/errors) 624 |
625 | 632 |
633 |

Request Body

634 |
635 | ```json 636 | { 637 | "interface": string 638 | } 639 | ``` 640 |
641 | 642 |

Response

643 | If successful, returns `ScanResult[]` 644 | If unsuccessful: [See REST Error Responses](../../errors/errors) 645 |
646 | 653 |
654 |

Request Body

655 |
656 | ```json 657 | { 658 | "ssid": string, 659 | "bssid": string, 660 | "encryption": string, 661 | "password": string, 662 | "hidden": bool, 663 | "interface": string 664 | } 665 | ``` 666 |
667 | 668 |

Response

669 | If successful, returns: 670 |
671 | ```json 672 | { 673 | "success": true 674 | } 675 | ``` 676 |
677 | If unsuccessful: [See REST Error Responses](../../errors/errors) 678 |
679 | 686 |
687 |

Response

688 | If successful, returns: 689 |
690 | ```json 691 | { 692 | "success": true 693 | } 694 | ``` 695 |
696 | If unsuccessful: [See REST Error Responses](../../errors/errors) 697 |
698 | 705 |
706 |

Response

707 | If successful, returns an array of network interfaces. 708 | If unsuccessful: [See REST Error Responses](../../errors/errors) 709 |
710 | 717 |
718 |

Response

719 | If successful, returns: 720 |
721 | ```json 722 | { 723 | "success": true 724 | } 725 | ``` 726 |
727 | If unsuccessful: [See REST Error Responses](../../errors/errors) 728 |
729 | 736 |
737 |

Response

738 | If successful, returns: 739 |
740 | ```json 741 | { 742 | "completed": bool, 743 | "output": string 744 | } 745 | ``` 746 |
747 | If unsuccessful: [See REST Error Responses](../../errors/errors) 748 |
749 | 750 | 751 | 752 | 753 | -------------------------------------------------------------------------------- /docs/typescript/typescript.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: WiFi Pineapple TypeScript Documentation 4 | nav_order: 200 5 | has_children: false 6 | has_toc: true 7 | --- 8 | 9 | 10 | 11 | # WiFi Pineapple TypeScript Documentation 12 | {: .no_toc } 13 | 14 | Table of contents 15 | {: .text-delta } 16 | 1. TOC 17 | {:toc} 18 | --- 19 | 20 | ## Introduction 21 | WiFi Pineapple Mark VII Module front-ends include a service named `ApiService` that offers a variety of helper functions, such as wrappers for easier interfaction with the [REST API](https://hak5.github.io/mk7-docs/docs/rest/rest/) or to communicate with [Modules](x). It is instantiated in the module component as `API`, an should always be named the same in custom components written by the module developer. 22 | ```typescript 23 | @Component({ 24 | selector: 'lib-example-module', 25 | templateUrl: './example-module.component.html', 26 | styleUrls: ['./example-module.component.css'] 27 | }) 28 | export class ExampleModuleComponent implments OnInit { 29 | constructor(private API: ApiService) { } 30 | 31 | ngOnInit() { } 32 | } 33 | ``` 34 | 35 | ## Module Requests 36 | Module requests are made with the `this.API.request()` function. It takes two arguments: 37 | - `payload: any` 38 | - `callback: any` 39 | 40 | The `payload` must be a JSON structure that contains **at least** two properties: 41 | - `module: string` (The name of the module you're making a request to) 42 | - `action: string` (The action you are making a request for) 43 | 44 | The `callback` is a function that takes a `response: any` as an argument. Extra data can be added to the request structure, and will be passwd to the module back-end where it may be processed. 45 | 46 | ```typescript 47 | // A simple Module request 48 | this.API.request({ 49 | module: 'ExampleModule', 50 | action: 'some_action' 51 | }, (response) => { 52 | console.log(response); 53 | }); 54 | 55 | // A module request with extra supplied data and error handling 56 | this.API.request({ 57 | module: 'ExampleModule', 58 | action: 'another_action', 59 | my_data: 'My Own Data String', 60 | other_data: could_be_a_variable 61 | }, (response) { 62 | if (response.error) { 63 | // Handle Error 64 | console.log('An error happened'); 65 | } else { 66 | // Handle Success 67 | console.log('Success!'); 68 | } 69 | }); 70 | ``` 71 | 72 | ## REST API Requests 73 | Regular API requests should be made with the following functions, depending on the type of request as specified in the [REST API](https://hak5.github.io/mk7-docs/docs/rest/rest/) docs. 74 | 75 | 81 |
82 |

Example

83 | Unauthenticate the current session. 84 |
85 | ```typescript 86 | this.API.unauth(); 87 | ``` 88 |
89 |
90 | 91 | 97 |
98 |

Example

99 | Enables a spinner in the UI title bar to indicate a busy state. 100 |
101 | ```typescript 102 | this.API.setBusy(); 103 | ``` 104 |
105 |
106 | 107 | 113 |
114 |

Example

115 | Disables the UI title bar busy spinner. 116 |
117 | ```typescript 118 | this.API.setNotBusy(); 119 | ``` 120 |
121 |
122 | 123 | 129 |
130 | ```typescript 131 | this.API.APIGet(path: string, callback: (any)); 132 | ``` 133 | Make a GET Request to a GET endpoint described in the REST API documentation. 134 |

Example

135 |
136 | ```typescript 137 | this.API.APIGet('/api/status', (resp) => { 138 | console.log(resp); 139 | }); 140 | ``` 141 |
142 |
143 | 144 | 150 |
151 | ```typescript 152 | async this.API.APIGetAsync(path: string); 153 | ``` 154 | Asynchronously make a GET Request to a GET endpoint described in the REST API documentation. 155 |

Example

156 |
157 | ```typescript 158 | const resp: any = await this.API.APIGetAsync('/api/status'); 159 | console.log(resp); 160 | ``` 161 |
162 |
163 | 164 | 170 |
171 | ```typescript 172 | this.API.APIPut(path: string, callback: (any)); 173 | ``` 174 | Make a PUT Request to a PUT endpoint described in the REST API documentation. 175 |

Example

176 |
177 | ```typescript 178 | this.API.APIPut('/api/status', (resp) => { 179 | console.log(resp); 180 | }); 181 | ``` 182 |
183 |
184 | 185 | 191 |
192 | ```typescript 193 | async this.API.APIPutAsync(path: string); 194 | ``` 195 | Asynchronously make a PUT Request to a PUT endpoint described in the REST API documentation. 196 |

Example

197 |
198 | ```typescript 199 | const resp: any = await this.API.APIPutAsync('/api/status'); 200 | console.log(resp); 201 | ``` 202 |
203 |
204 | 205 | 211 |
212 | ```typescript 213 | this.API.APIPost(path: string, body: any, callback: (any)); 214 | ``` 215 | Make a POST Request to a POST endpoint described in the REST API documentation. 216 |

Example

217 |
218 | ```typescript 219 | this.API.APIPost('/api/status', { content: 'content'}, (resp) => { 220 | console.log(resp); 221 | }); 222 | ``` 223 |
224 |
225 | 226 | 232 |
233 | ```typescript 234 | async this.API.APIGetAsync(path: string, body: any); 235 | ``` 236 | Asynchronously make a POST Request to a POST endpoint described in the REST API documentation. 237 |

Example

238 |
239 | ```typescript 240 | const resp: any = await this.API.APIPostAsync('/api/status', { content: 'content'}); 241 | console.log(resp); 242 | ``` 243 |
244 |
245 | 246 | 252 |
253 | ```typescript 254 | this.API.APIDelete(path: string, callback: (any)); 255 | ``` 256 | Make a DELETE Request to a DELETE endpoint described in the REST API documentation. 257 |

Example

258 |
259 | ```typescript 260 | this.API.APIDelete('/api/status', (resp) => { 261 | console.log(resp); 262 | }); 263 | ``` 264 |
265 |
266 | 267 | 273 |
274 | ```typescript 275 | async this.API.APIDeleteAsync(path: string); 276 | ``` 277 | Asynchronously make a DELETE Request to a DELETE endpoint described in the REST API documentation. 278 |

Example

279 |
280 | ```typescript 281 | const resp: any = await this.API.APIDeleteAsync('/api/status'); 282 | console.log(resp); 283 | ``` 284 |
285 |
286 | 287 | 293 |
294 | ```typescript 295 | this.API.APIDownload(fullpath: string, filename: string); 296 | ``` 297 |

Example

298 |
299 | ```typescript 300 | this.API.APIDownload('/tmp/log.txt', 'log-output'); 301 | ``` 302 |
303 |
304 | 305 | 306 | 307 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Home 4 | nav_order: 1 5 | description: "Hak5 WiFi Pineapple Mark 7 Developer Documentation" 6 | permalink: / 7 | --- 8 | 9 | # WiFi Pineapple Mark 7 Developer Documentation 10 | This is the home for technical and developer documentation for the WiFi Pineapple Mark VII. 11 | 12 | 13 | --------------------------------------------------------------------------------