├── run.sh ├── source ├── javascripts │ ├── all.js │ ├── all_nosearch.js │ ├── app │ │ ├── _search.js │ │ ├── _toc.js │ │ └── _lang.js │ └── lib │ │ ├── _jquery.highlight.js │ │ ├── _energize.js │ │ └── _imagesloaded.min.js ├── fonts │ ├── slate.eot │ ├── slate.ttf │ ├── slate.woff │ ├── slate.woff2 │ └── slate.svg ├── images │ ├── alert.png │ ├── logo.png │ ├── alert1.png │ ├── favicon.ico │ ├── navbar.png │ ├── auth_json.png │ ├── juice-shop.png │ ├── zap_api_ui.png │ ├── code_example.png │ ├── header_rule.png │ ├── report_html.png │ ├── alerts_results.png │ ├── spider_advanced.png │ ├── spider_results.png │ ├── zap_desktop_api.png │ ├── auth_json_script.png │ ├── ajax_spider_advanced.png │ ├── ajax_spider_results.png │ ├── ascan_advanced_tech.png │ ├── auth_dvwa_token_html.png │ ├── auth_dvwa_zap_script.png │ ├── ascan_advanced_options.png │ ├── ascan_advanced_policy.png │ ├── auth_dvwa_context_auth.png │ ├── ascan_advanced_input_vectors.png │ ├── auth_bodgeit_form_settings.png │ └── ascan_advanced_input_vectors_code.png ├── index.html.md ├── stylesheets │ ├── _icon-font.scss │ ├── print.css.scss │ ├── _rtl.scss │ ├── _variables.scss │ ├── _normalize.css │ ├── _normalize.scss │ └── screen.css.scss ├── scripts │ ├── jwtScript.js │ └── auth-dvwa.js ├── includes │ ├── troubleshooting.md │ ├── advanced.md │ ├── results.md │ ├── contributing.md │ ├── attack.md │ ├── welcome.md │ └── explore.md └── layouts │ └── layout.erb ├── Rakefile ├── .github ├── dependabot.yml ├── actions │ └── update-website │ │ ├── package.json │ │ ├── action.yml │ │ ├── index.js │ │ └── package-lock.json └── workflows │ ├── ci.yml │ ├── update-website.yml │ └── check-dist-update-website.yml ├── package.json ├── .editorconfig ├── .gitignore ├── Gemfile ├── lib ├── multilang.rb ├── nesting_unique_head.rb ├── toc_data.rb └── unique_head.rb ├── widdershins └── templates │ ├── code_python.dot │ └── main.dot ├── config.rb ├── Vagrantfile ├── README.md ├── Gemfile.lock ├── font-selection.json ├── deploy.sh └── LICENSE /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | bundle exec middleman server -------------------------------------------------------------------------------- /source/javascripts/all.js: -------------------------------------------------------------------------------- 1 | //= require ./all_nosearch 2 | //= require ./app/_search 3 | -------------------------------------------------------------------------------- /source/fonts/slate.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/fonts/slate.eot -------------------------------------------------------------------------------- /source/fonts/slate.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/fonts/slate.ttf -------------------------------------------------------------------------------- /source/fonts/slate.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/fonts/slate.woff -------------------------------------------------------------------------------- /source/images/alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/alert.png -------------------------------------------------------------------------------- /source/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/logo.png -------------------------------------------------------------------------------- /source/fonts/slate.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/fonts/slate.woff2 -------------------------------------------------------------------------------- /source/images/alert1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/alert1.png -------------------------------------------------------------------------------- /source/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/favicon.ico -------------------------------------------------------------------------------- /source/images/navbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/navbar.png -------------------------------------------------------------------------------- /source/images/auth_json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/auth_json.png -------------------------------------------------------------------------------- /source/images/juice-shop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/juice-shop.png -------------------------------------------------------------------------------- /source/images/zap_api_ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/zap_api_ui.png -------------------------------------------------------------------------------- /source/images/code_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/code_example.png -------------------------------------------------------------------------------- /source/images/header_rule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/header_rule.png -------------------------------------------------------------------------------- /source/images/report_html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/report_html.png -------------------------------------------------------------------------------- /source/images/alerts_results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/alerts_results.png -------------------------------------------------------------------------------- /source/images/spider_advanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/spider_advanced.png -------------------------------------------------------------------------------- /source/images/spider_results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/spider_results.png -------------------------------------------------------------------------------- /source/images/zap_desktop_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/zap_desktop_api.png -------------------------------------------------------------------------------- /source/images/auth_json_script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/auth_json_script.png -------------------------------------------------------------------------------- /source/images/ajax_spider_advanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/ajax_spider_advanced.png -------------------------------------------------------------------------------- /source/images/ajax_spider_results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/ajax_spider_results.png -------------------------------------------------------------------------------- /source/images/ascan_advanced_tech.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/ascan_advanced_tech.png -------------------------------------------------------------------------------- /source/images/auth_dvwa_token_html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/auth_dvwa_token_html.png -------------------------------------------------------------------------------- /source/images/auth_dvwa_zap_script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/auth_dvwa_zap_script.png -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'middleman-gh-pages' 2 | require 'rake/clean' 3 | 4 | CLOBBER.include('build') 5 | 6 | task :default => [:build] 7 | -------------------------------------------------------------------------------- /source/images/ascan_advanced_options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/ascan_advanced_options.png -------------------------------------------------------------------------------- /source/images/ascan_advanced_policy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/ascan_advanced_policy.png -------------------------------------------------------------------------------- /source/images/auth_dvwa_context_auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/auth_dvwa_context_auth.png -------------------------------------------------------------------------------- /source/images/ascan_advanced_input_vectors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/ascan_advanced_input_vectors.png -------------------------------------------------------------------------------- /source/images/auth_bodgeit_form_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/auth_bodgeit_form_settings.png -------------------------------------------------------------------------------- /source/images/ascan_advanced_input_vectors_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zaproxy/zap-api-docs/HEAD/source/images/ascan_advanced_input_vectors_code.png -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | open-pull-requests-limit: 10 8 | - package-ecosystem: npm 9 | directory: "/" 10 | schedule: 11 | interval: weekly 12 | open-pull-requests-limit: 10 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zap-api-docs", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "build": "widdershins -u widdershins/templates/ --search false --language_tabs 'shell:Shell' 'java:Java' 'python:Python' --summary openapi.yaml source/includes/apis.md" 6 | }, 7 | "dependencies": { 8 | "widdershins": "^4.0.1" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # Top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 12 | trim_trailing_whitespace = true 13 | 14 | [*.rb] 15 | charset = utf-8 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /source/index.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: API Reference 3 | 4 | language_tabs: # must be one of https://git.io/vQNgJ 5 | - python 6 | - java 7 | - shell 8 | 9 | toc_footers: 10 | - ZAP Website 11 | 12 | includes: 13 | - welcome 14 | - explore 15 | - attack 16 | - results 17 | - auth 18 | - advanced 19 | - contributing 20 | - troubleshooting 21 | - apis 22 | 23 | search: true 24 | --- 25 | 26 | 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | coverage 6 | InstalledFiles 7 | lib/bundler/man 8 | pkg 9 | rdoc 10 | spec/reports 11 | test/tmp 12 | test/version_tmp 13 | tmp 14 | *.DS_STORE 15 | build/ 16 | .cache 17 | .vagrant 18 | .sass-cache 19 | 20 | # YARD artifacts 21 | .yardoc 22 | _yardoc 23 | doc/ 24 | .idea/ 25 | 26 | # Eclipse 27 | *.project 28 | *.settings 29 | 30 | # JS 31 | node_modules/ 32 | /dist/ 33 | 34 | # Generated 35 | /source/includes/apis.md 36 | -------------------------------------------------------------------------------- /source/javascripts/all_nosearch.js: -------------------------------------------------------------------------------- 1 | //= require ./lib/_energize 2 | //= require ./app/_toc 3 | //= require ./app/_lang 4 | 5 | $(function() { 6 | loadToc($('#toc'), '.toc-link', '.toc-list-h2', 10); 7 | setupLanguages($('body').data('languages')); 8 | $('.content').imagesLoaded( function() { 9 | window.recacheHeights(); 10 | window.refreshToc(); 11 | }); 12 | }); 13 | 14 | window.onpopstate = function() { 15 | activateLanguage(getLanguageFromQueryString()); 16 | }; 17 | -------------------------------------------------------------------------------- /.github/actions/update-website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "update-website", 3 | "version": "1.0.0", 4 | "description": "Update the website with a pull request", 5 | "license": "Apache-2.0", 6 | "main": "index.js", 7 | "scripts": { 8 | "package": "ncc build index.js -o dist" 9 | }, 10 | "dependencies": { 11 | "@actions/core": "^1.11.1", 12 | "@actions/exec": "^1.1.1", 13 | "@actions/github": "^6.0.1", 14 | "@actions/io": "^1.1.3" 15 | }, 16 | "devDependencies": { 17 | "@vercel/ncc": "^0.38.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.github/actions/update-website/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Update Website' 2 | description: 'Updates the website with a pull request' 3 | inputs: 4 | repo: 5 | description: 'the repo that contains the website' 6 | required: true 7 | branch: 8 | description: 'the base branch in the website repo' 9 | required: true 10 | user: 11 | description: 'the username of the pull request author' 12 | required: true 13 | email: 14 | description: 'the email of the pull request author' 15 | required: true 16 | runs: 17 | using: 'node24' 18 | main: 'dist/index.js' 19 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Ruby CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: '20' 18 | - uses: ruby/setup-ruby@v1 19 | with: 20 | ruby-version: 3.2 21 | bundler-cache: true 22 | - name: Generate apis.md 23 | run: | 24 | npm install 25 | npm run build 26 | - run: bundle exec middleman build 27 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | ruby '>=3.2' 2 | source 'https://rubygems.org' 3 | 4 | gem 'middleman', '4.5.1' 5 | gem 'middleman-syntax', '~> 3.4.0' 6 | gem 'middleman-autoprefixer', '~> 3.0.0' 7 | gem 'middleman-sprockets', '~> 4.1.1' 8 | gem 'rouge', '~> 3.30.0' 9 | gem 'redcarpet', '~> 3.6.1' 10 | gem 'nokogiri', '~> 1.18.10' 11 | 12 | # Lock haml https://github.com/middleman/middleman-syntax/issues/80 13 | gem 'haml', '>= 4.0.5', '< 6.0' 14 | # Lock sprockets/sass, newer version (4.0.0) uses sassc which leads to an error: 15 | # "Error: File to import not found or unreadable: normalize." 16 | gem 'sprockets', '3.7.5' 17 | gem 'sass', '3.7.4' -------------------------------------------------------------------------------- /lib/multilang.rb: -------------------------------------------------------------------------------- 1 | module Multilang 2 | def block_code(code, full_lang_name) 3 | if full_lang_name 4 | parts = full_lang_name.split('--') 5 | rouge_lang_name = (parts) ? parts[0] : "" # just parts[0] here causes null ref exception when no language specified 6 | super(code, rouge_lang_name).sub("highlight #{rouge_lang_name}") do |match| 7 | match + " tab-" + full_lang_name 8 | end 9 | else 10 | super(code, full_lang_name) 11 | end 12 | end 13 | end 14 | 15 | require 'middleman-core/renderers/redcarpet' 16 | Middleman::Renderers::MiddlemanRedcarpetHTML.send :include, Multilang 17 | -------------------------------------------------------------------------------- /widdershins/templates/code_python.dot: -------------------------------------------------------------------------------- 1 | import requests 2 | {{?data.allHeaders.length}}headers = { 3 | {{~data.allHeaders :p:index}} '{{=p.name}}': {{=p.exampleValues.json}}{{?index < data.allHeaders.length-1}},{{?}} 4 | {{~}}} 5 | {{?}} 6 | r = requests.{{=data.method.verb}}('{{=data.url}}'{{? data.requiredParameters.length}}, params={ 7 | {{~ data.requiredParameters :p:index}} '{{=p.name}}': {{=p.exampleValues.json}}{{? data.requiredParameters.length-1 != index }},{{?}}{{~}} 8 | }{{?}}{{?data.allHeaders.length}}, headers = headers{{?}}) 9 | 10 | {{?data.url.includes("/JSON/")}}print(r.json()){{?? true }}print(r.content){{?}} 11 | -------------------------------------------------------------------------------- /lib/nesting_unique_head.rb: -------------------------------------------------------------------------------- 1 | # Nested unique header generation 2 | require 'middleman-core/renderers/redcarpet' 3 | 4 | class NestingUniqueHeadCounter < Middleman::Renderers::MiddlemanRedcarpetHTML 5 | def initialize 6 | super 7 | @@headers_history = {} if !defined?(@@headers_history) 8 | end 9 | 10 | def header(text, header_level) 11 | friendly_text = text.gsub(/<[^>]*>/,"").parameterize 12 | @@headers_history[header_level] = text.parameterize 13 | 14 | if header_level > 1 15 | for i in (header_level - 1).downto(1) 16 | friendly_text.prepend("#{@@headers_history[i]}-") if @@headers_history.key?(i) 17 | end 18 | end 19 | 20 | return "#{text}" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/toc_data.rb: -------------------------------------------------------------------------------- 1 | require 'nokogiri' 2 | 3 | def toc_data(page_content) 4 | html_doc = Nokogiri::HTML::DocumentFragment.parse(page_content) 5 | 6 | # get a flat list of headers 7 | headers = [] 8 | html_doc.css('h1, h2, h3').each do |header| 9 | headers.push({ 10 | id: header.attribute('id').to_s, 11 | content: header.children, 12 | title: header.children.to_s.gsub(/<[^>]*>/, ''), 13 | level: header.name[1].to_i, 14 | children: [] 15 | }) 16 | end 17 | 18 | [3,2].each do |header_level| 19 | header_to_nest = nil 20 | headers = headers.reject do |header| 21 | if header[:level] == header_level 22 | header_to_nest[:children].push header if header_to_nest 23 | true 24 | else 25 | header_to_nest = header if header[:level] < header_level 26 | false 27 | end 28 | end 29 | end 30 | headers 31 | end 32 | -------------------------------------------------------------------------------- /lib/unique_head.rb: -------------------------------------------------------------------------------- 1 | # Unique header generation 2 | require 'middleman-core/renderers/redcarpet' 3 | require 'digest' 4 | class UniqueHeadCounter < Middleman::Renderers::MiddlemanRedcarpetHTML 5 | def initialize 6 | super 7 | @head_count = {} 8 | end 9 | def header(text, header_level) 10 | friendly_text = text.gsub(/<[^>]*>/,"").parameterize 11 | if friendly_text.strip.length == 0 12 | # Looks like parameterize removed the whole thing! It removes many unicode 13 | # characters like Chinese and Russian. To get a unique URL, let's just 14 | # URI escape the whole header 15 | friendly_text = Digest::SHA1.hexdigest(text)[0,10] 16 | end 17 | @head_count[friendly_text] ||= 0 18 | @head_count[friendly_text] += 1 19 | if @head_count[friendly_text] > 1 20 | friendly_text += "-#{@head_count[friendly_text]}" 21 | end 22 | return "#{text}" 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /source/stylesheets/_icon-font.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'slate'; 3 | src:font-url('slate.eot?-syv14m'); 4 | src:font-url('slate.eot?#iefix-syv14m') format('embedded-opentype'), 5 | font-url('slate.woff2?-syv14m') format('woff2'), 6 | font-url('slate.woff?-syv14m') format('woff'), 7 | font-url('slate.ttf?-syv14m') format('truetype'), 8 | font-url('slate.svg?-syv14m#slate') format('svg'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | 13 | %icon { 14 | font-family: 'slate'; 15 | speak: none; 16 | font-style: normal; 17 | font-weight: normal; 18 | font-variant: normal; 19 | text-transform: none; 20 | line-height: 1; 21 | } 22 | 23 | %icon-exclamation-sign { 24 | @extend %icon; 25 | content: "\e600"; 26 | } 27 | %icon-info-sign { 28 | @extend %icon; 29 | content: "\e602"; 30 | } 31 | %icon-ok-sign { 32 | @extend %icon; 33 | content: "\e606"; 34 | } 35 | %icon-search { 36 | @extend %icon; 37 | content: "\e607"; 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/update-website.yml: -------------------------------------------------------------------------------- 1 | name: Update Website 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | update-website: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: ruby/setup-ruby@v1 13 | with: 14 | ruby-version: 3.2 15 | - uses: actions/setup-node@v4 16 | with: 17 | node-version: '20' 18 | - uses: actions/checkout@v4 19 | with: 20 | path: zap-api-docs 21 | - name: Checkout zaproxy.github.io 22 | uses: actions/checkout@v4 23 | with: 24 | repository: zaproxy/zaproxy.github.io 25 | persist-credentials: false 26 | path: zaproxy.github.io 27 | fetch-depth: 0 28 | - name: Generate apis.md 29 | run: | 30 | cd zap-api-docs 31 | npm install 32 | npm run build 33 | - name: Build API docs 34 | run: | 35 | cd zap-api-docs 36 | gem install bundler 37 | bundle install 38 | bundle exec middleman build --clean 39 | - name: Update Website 40 | uses: ./zap-api-docs/.github/actions/update-website 41 | with: 42 | repo: 'zaproxy.github.io' 43 | branch: 'master' 44 | user: 'zapbot' 45 | email: '12745184+zapbot@users.noreply.github.com' 46 | env: 47 | AUTH_TOKEN: ${{ secrets.ZAPBOT_TOKEN }} 48 | -------------------------------------------------------------------------------- /.github/workflows/check-dist-update-website.yml: -------------------------------------------------------------------------------- 1 | name: Check update-website dist 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - '.github/actions/update-website/**' 9 | pull_request: 10 | paths: 11 | - '.github/actions/update-website/**' 12 | workflow_dispatch: 13 | 14 | jobs: 15 | check-dist: 16 | runs-on: ubuntu-latest 17 | defaults: 18 | run: 19 | working-directory: .github/actions/update-website/ 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - name: Set Node.js 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: 20.x 28 | 29 | - name: Install dependencies 30 | run: npm ci 31 | 32 | - name: Rebuild the dist directory 33 | run: npm run package 34 | 35 | - name: Compare dist directories 36 | run: | 37 | if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then 38 | echo "Detected uncommitted changes after build. See status below:" 39 | git diff 40 | exit 1 41 | fi 42 | id: diff 43 | 44 | - uses: actions/upload-artifact@v4 45 | if: ${{ failure() && steps.diff.conclusion == 'failure' }} 46 | with: 47 | name: dist 48 | path: .github/actions/update-website/dist/ 49 | -------------------------------------------------------------------------------- /config.rb: -------------------------------------------------------------------------------- 1 | # Unique header generation 2 | require './lib/unique_head.rb' 3 | 4 | # Markdown 5 | set :markdown_engine, :redcarpet 6 | set :markdown, 7 | fenced_code_blocks: true, 8 | smartypants: true, 9 | disable_indented_code_blocks: true, 10 | prettify: true, 11 | strikethrough: true, 12 | tables: true, 13 | with_toc_data: true, 14 | no_intra_emphasis: true, 15 | renderer: UniqueHeadCounter 16 | 17 | # Assets 18 | set :css_dir, 'stylesheets' 19 | set :js_dir, 'javascripts' 20 | set :images_dir, 'images' 21 | set :fonts_dir, 'fonts' 22 | 23 | # Activate the syntax highlighter 24 | activate :syntax 25 | ready do 26 | require './lib/multilang.rb' 27 | end 28 | 29 | activate :sprockets 30 | 31 | activate :autoprefixer do |config| 32 | config.browsers = ['last 2 version', 'Firefox ESR'] 33 | config.cascade = false 34 | config.inline = true 35 | end 36 | 37 | # Github pages require relative links 38 | activate :relative_assets 39 | set :relative_links, true 40 | 41 | # Build Configuration 42 | configure :build do 43 | # If you're having trouble with Middleman hanging, commenting 44 | # out the following two lines has been known to help 45 | activate :minify_css 46 | activate :minify_javascript 47 | # activate :relative_assets 48 | # activate :asset_hash 49 | # activate :gzip 50 | end 51 | 52 | # Deploy Configuration 53 | # If you want Middleman to listen on a different port, you can set that below 54 | set :port, 4567 55 | 56 | helpers do 57 | require './lib/toc_data.rb' 58 | end 59 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure(2) do |config| 2 | config.vm.box = "ubuntu/trusty64" 3 | config.vm.network :forwarded_port, guest: 4567, host: 4567 4 | config.vm.provider "virtualbox" do |vb| 5 | vb.memory = "2048" 6 | end 7 | 8 | config.vm.provision "bootstrap", 9 | type: "shell", 10 | inline: <<-SHELL 11 | sudo apt-add-repository ppa:brightbox/ruby-ng 12 | sudo apt-get update 13 | sudo apt-get install -yq ruby2.4 ruby2.4-dev 14 | sudo apt-get install -yq pkg-config build-essential nodejs git libxml2-dev libxslt-dev 15 | sudo apt-get autoremove -yq 16 | gem2.4 install --no-ri --no-rdoc bundler 17 | SHELL 18 | 19 | # add the local user git config to the vm 20 | config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig" 21 | 22 | config.vm.provision "install", 23 | type: "shell", 24 | privileged: false, 25 | inline: <<-SHELL 26 | echo "==============================================" 27 | echo "Installing app dependencies" 28 | cd /vagrant 29 | bundle config build.nokogiri --use-system-libraries 30 | bundle install 31 | SHELL 32 | 33 | config.vm.provision "run", 34 | type: "shell", 35 | privileged: false, 36 | run: "always", 37 | inline: <<-SHELL 38 | echo "==============================================" 39 | echo "Starting up middleman at http://localhost:4567" 40 | echo "If it does not come up, check the ~/middleman.log file for any error messages" 41 | cd /vagrant 42 | bundle exec middleman server --watcher-force-polling --watcher-latency=1 &> ~/middleman.log & 43 | SHELL 44 | end 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Introduction 3 | 4 | Welcome to ZAP API Documentation! The [Zed Attack Proxy](https://www.zaproxy.org/) (**ZAP**) 5 | is one of the world's most popular free security tools which lets you automatically find security vulnerabilities in your 6 | applications. ZAP also has an extremely powerful API that allows you to do nearly everything that possible via the desktop interface. 7 | This allows the developers to automate pentesting and security regression testing of the application in the CI/CD pipeline. 8 | This repository provides example guides & API definitions for ZAP APIs. 9 | 10 | The live documentation can be viewed in the following [URL](https://zaproxy.org/docs/api/). 11 | 12 | # Contributing to the Documentation 13 | 14 | The guidelines for contribution is available in the [following page](https://zaproxy.org/docs/api/#contributions-welcome). 15 | ZAP documentation is built using [Slate](https://github.com/slatedocs/slate). The source files for the documentation are available 16 | in the `source/includes` directory. View the contribution guide on how to start contributing to the document. 17 | 18 | # Deploying 19 | 20 | To deploy to https://zaproxy.org/ 21 | 22 | 1. Run `bundle install` if you have not done so before 23 | 1. Run `bundle exec middleman build --clean` 24 | 1. Copy the build directory to your own clone of https://github.com/zaproxy/zaproxy.github.io e.g. 25 | - `cp -R build/* ../zaproxy.github.io/docs/api/` 26 | 1. Commit and push any changes then open a PR on https://github.com/zaproxy/zaproxy.github.io 27 | 28 | # Generate OpenAPI Markdown 29 | 30 | The OpenAPI markdown is auto generated from the `openapi.yaml` file and the [Widdershins](https://github.com/Mermade/widdershins) 31 | node package is used to auto generate the markdown file. 32 | 33 | ```bash 34 | # To install the widdershins package 35 | npm install 36 | # To generate the markdown file 37 | npm run build 38 | ``` 39 | -------------------------------------------------------------------------------- /source/scripts/jwtScript.js: -------------------------------------------------------------------------------- 1 | // Logging with the script name is super helpful! 2 | function logger() { 3 | print('[' + this['zap.script.name'] + '] ' + arguments[0]); 4 | } 5 | 6 | var HttpSender = Java.type('org.parosproxy.paros.network.HttpSender'); 7 | var ScriptVars = Java.type('org.zaproxy.zap.extension.script.ScriptVars'); 8 | var HtmlParameter = Java.type('org.parosproxy.paros.network.HtmlParameter') 9 | var COOKIE_TYPE = org.parosproxy.paros.network.HtmlParameter.Type.cookie; 10 | 11 | function sendingRequest(msg, initiator, helper) { 12 | if (initiator === HttpSender.AUTHENTICATION_INITIATOR) { 13 | logger("Trying to auth") 14 | return msg; 15 | } 16 | 17 | var token = ScriptVars.getGlobalVar("jwt-token") 18 | if (!token) {return;} 19 | var headers = msg.getRequestHeader(); 20 | var cookie = new HtmlParameter(COOKIE_TYPE, "token", token); 21 | msg.getRequestHeader().getCookieParams().add(cookie); 22 | // For all non-authentication requests we want to include the authorization header 23 | logger("Added authorization token " + token.slice(0, 20) + " ... ") 24 | msg.getRequestHeader().setHeader('Authorization', 'Bearer ' + token); 25 | return msg; 26 | } 27 | 28 | function responseReceived(msg, initiator, helper) { 29 | var resbody = msg.getResponseBody().toString() 30 | var resheaders = msg.getResponseHeader() 31 | 32 | if (initiator !== HttpSender.AUTHENTICATION_INITIATOR) { 33 | var token = ScriptVars.getGlobalVar("jwt-token"); 34 | if (!token) {return;} 35 | 36 | var headers = msg.getRequestHeader(); 37 | var cookies = headers.getCookieParams(); 38 | var cookie = new HtmlParameter(COOKIE_TYPE, "token", token); 39 | 40 | if (cookies.contains(cookie)) {return;} 41 | msg.getResponseHeader().setHeader('Set-Cookie', 'token=' + token + '; Path=/;'); 42 | return; 43 | } 44 | 45 | logger("Handling auth response") 46 | if (resheaders.getStatusCode() > 299) { 47 | logger("Auth failed") 48 | return; 49 | } 50 | 51 | // Is response JSON? @todo check content-type 52 | if (resbody[0] !== '{') {return;} 53 | try { 54 | var data = JSON.parse(resbody); 55 | } catch (e) { 56 | return; 57 | } 58 | 59 | // If auth request was not succesful move on 60 | if (!data['authentication']) {return;} 61 | 62 | // @todo abstract away to be configureable 63 | var token = data["authentication"]["token"] 64 | logger("Capturing token for JWT\n" + token) 65 | ScriptVars.setGlobalVar("jwt-token", token) 66 | msg.getResponseHeader().setHeader('Set-Cookie', 'token=' + token + '; Path=/;'); 67 | } 68 | -------------------------------------------------------------------------------- /source/javascripts/app/_search.js: -------------------------------------------------------------------------------- 1 | //= require ../lib/_lunr 2 | //= require ../lib/_jquery 3 | //= require ../lib/_jquery.highlight 4 | ;(function () { 5 | 'use strict'; 6 | 7 | var content, searchResults; 8 | var highlightOpts = { element: 'span', className: 'search-highlight' }; 9 | var searchDelay = 0; 10 | var timeoutHandle = 0; 11 | 12 | var index = new lunr.Index(); 13 | 14 | index.ref('id'); 15 | index.field('title', { boost: 10 }); 16 | index.field('body'); 17 | index.pipeline.add(lunr.trimmer, lunr.stopWordFilter); 18 | 19 | $(populate); 20 | $(bind); 21 | 22 | function populate() { 23 | $('h1, h2').each(function() { 24 | var title = $(this); 25 | var body = title.nextUntil('h1, h2'); 26 | index.add({ 27 | id: title.prop('id'), 28 | title: title.text(), 29 | body: body.text() 30 | }); 31 | }); 32 | 33 | determineSearchDelay(); 34 | } 35 | function determineSearchDelay() { 36 | if(index.tokenStore.length>5000) { 37 | searchDelay = 300; 38 | } 39 | } 40 | 41 | function bind() { 42 | content = $('.content'); 43 | searchResults = $('.search-results'); 44 | 45 | $('#input-search').on('keyup',function(e) { 46 | var wait = function() { 47 | return function(executingFunction, waitTime){ 48 | clearTimeout(timeoutHandle); 49 | timeoutHandle = setTimeout(executingFunction, waitTime); 50 | }; 51 | }(); 52 | wait(function(){ 53 | search(e); 54 | }, searchDelay ); 55 | }); 56 | } 57 | 58 | function search(event) { 59 | 60 | var searchInput = $('#input-search')[0]; 61 | 62 | unhighlight(); 63 | searchResults.addClass('visible'); 64 | 65 | // ESC clears the field 66 | if (event.keyCode === 27) searchInput.value = ''; 67 | 68 | if (searchInput.value) { 69 | var results = index.search(searchInput.value).filter(function(r) { 70 | return r.score > 0.0001; 71 | }); 72 | 73 | if (results.length) { 74 | searchResults.empty(); 75 | $.each(results, function (index, result) { 76 | var elem = document.getElementById(result.ref); 77 | searchResults.append("
  • " + $(elem).text() + "
  • "); 78 | }); 79 | highlight.call(searchInput); 80 | } else { 81 | searchResults.html('
  • '); 82 | $('.search-results li').text('No Results Found for "' + searchInput.value + '"'); 83 | } 84 | } else { 85 | unhighlight(); 86 | searchResults.removeClass('visible'); 87 | } 88 | } 89 | 90 | function highlight() { 91 | if (this.value) content.highlight(this.value, highlightOpts); 92 | } 93 | 94 | function unhighlight() { 95 | content.unhighlight(highlightOpts); 96 | } 97 | })(); 98 | 99 | -------------------------------------------------------------------------------- /source/fonts/slate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /source/scripts/auth-dvwa.js: -------------------------------------------------------------------------------- 1 | // The authenticate function is called whenever ZAP requires to authenticate, for a Context for which this script 2 | // was selected as the Authentication Method. The function should send any messages that are required to do the authentication 3 | // and should return a message with an authenticated response so the calling method. 4 | // 5 | // NOTE: Any message sent in the function should be obtained using the 'helper.prepareMessage()' method. 6 | // 7 | // Parameters: 8 | // helper - a helper class providing useful methods: prepareMessage(), sendAndReceive(msg) 9 | // paramsValues - the values of the parameters configured in the Session Properties -> Authentication panel. 10 | // The paramsValues is a map, having as keys the parameters names (as returned by the getRequiredParamsNames() 11 | // and getOptionalParamsNames() functions below) 12 | // credentials - an object containing the credentials values, as configured in the Session Properties -> Users panel. 13 | // The credential values can be obtained via calls to the getParam(paramName) method. The param names are the ones 14 | // returned by the getCredentialsParamsNames() below 15 | function authenticate(helper, paramsValues, credentials) { 16 | var loginUrl = paramsValues.get("Login_URL"); 17 | var csrfTokenName = paramsValues.get("CSRF_Field"); 18 | var csrfTokenValue = extractInputFieldValue(getPageContent(helper, loginUrl), csrfTokenName); 19 | var postData = paramsValues.get("POST_Data"); 20 | 21 | postData = postData.replace('{%username%}', encodeURIComponent(credentials.getParam("Username"))); 22 | postData = postData.replace('{%password%}', encodeURIComponent(credentials.getParam("Password"))); 23 | postData = postData.replace('{%' + csrfTokenName + '%}', encodeURIComponent(csrfTokenValue)); 24 | 25 | var msg = sendAndReceive(helper, loginUrl, postData); 26 | return msg; 27 | } 28 | 29 | function getRequiredParamsNames() { 30 | return [ "Login_URL", "CSRF_Field", "POST_Data" ]; 31 | } 32 | 33 | function getOptionalParamsNames() { 34 | return []; 35 | } 36 | 37 | function getCredentialsParamsNames() { 38 | return [ "Username", "Password" ]; 39 | } 40 | 41 | function getPageContent(helper, url) { 42 | var msg = sendAndReceive(helper, url); 43 | return msg.getResponseBody().toString(); 44 | } 45 | 46 | function sendAndReceive(helper, url, postData) { 47 | var msg = helper.prepareMessage(); 48 | 49 | var method = "GET"; 50 | if (postData) { 51 | method = "POST"; 52 | msg.setRequestBody(postData); 53 | } 54 | 55 | var requestUri = new org.apache.commons.httpclient.URI(url, true); 56 | var requestHeader = new org.parosproxy.paros.network.HttpRequestHeader(method, requestUri, "HTTP/1.0"); 57 | msg.setRequestHeader(requestHeader); 58 | 59 | helper.sendAndReceive(msg); 60 | 61 | return msg; 62 | } 63 | 64 | function extractInputFieldValue(page, fieldName) { 65 | // Rhino: 66 | //var src = new net.htmlparser.jericho.Source(page); 67 | // Nashorn: 68 | var Source = Java.type("net.htmlparser.jericho.Source"); 69 | var src = new Source(page); 70 | 71 | var it = src.getAllElements('input').iterator(); 72 | 73 | while (it.hasNext()) { 74 | var element = it.next(); 75 | if (element.getAttributeValue('name') == fieldName) { 76 | return element.getAttributeValue('value'); 77 | } 78 | } 79 | return ''; 80 | } 81 | -------------------------------------------------------------------------------- /source/stylesheets/print.css.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | @import 'normalize'; 3 | @import 'variables'; 4 | @import 'icon-font'; 5 | 6 | /* 7 | Copyright 2008-2013 Concur Technologies, Inc. 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); you may 10 | not use this file except in compliance with the License. You may obtain 11 | a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 17 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 18 | License for the specific language governing permissions and limitations 19 | under the License. 20 | */ 21 | 22 | $print-color: #999; 23 | $print-color-light: #ccc; 24 | $print-font-size: 12px; 25 | 26 | body { 27 | @extend %default-font; 28 | } 29 | 30 | .tocify, .toc-footer, .lang-selector, .search, #nav-button { 31 | display: none; 32 | } 33 | 34 | .tocify-wrapper>img { 35 | margin: 0 auto; 36 | display: block; 37 | } 38 | 39 | .content { 40 | font-size: 12px; 41 | 42 | pre, code { 43 | @extend %code-font; 44 | @extend %break-words; 45 | border: 1px solid $print-color; 46 | border-radius: 5px; 47 | font-size: 0.8em; 48 | } 49 | 50 | pre { 51 | code { 52 | border: 0; 53 | } 54 | } 55 | 56 | pre { 57 | padding: 1.3em; 58 | } 59 | 60 | code { 61 | padding: 0.2em; 62 | } 63 | 64 | table { 65 | border: 1px solid $print-color; 66 | tr { 67 | border-bottom: 1px solid $print-color; 68 | } 69 | td,th { 70 | padding: 0.7em; 71 | } 72 | } 73 | 74 | p { 75 | line-height: 1.5; 76 | } 77 | 78 | a { 79 | text-decoration: none; 80 | color: #000; 81 | } 82 | 83 | h1 { 84 | @extend %header-font; 85 | font-size: 2.5em; 86 | padding-top: 0.5em; 87 | padding-bottom: 0.5em; 88 | margin-top: 1em; 89 | margin-bottom: $h1-margin-bottom; 90 | border: 2px solid $print-color-light; 91 | border-width: 2px 0; 92 | text-align: center; 93 | } 94 | 95 | h2 { 96 | @extend %header-font; 97 | font-size: 1.8em; 98 | margin-top: 2em; 99 | border-top: 2px solid $print-color-light; 100 | padding-top: 0.8em; 101 | } 102 | 103 | h1+h2, h1+div+h2 { 104 | border-top: none; 105 | padding-top: 0; 106 | margin-top: 0; 107 | } 108 | 109 | h3, h4 { 110 | @extend %header-font; 111 | font-size: 0.8em; 112 | margin-top: 1.5em; 113 | margin-bottom: 0.8em; 114 | text-transform: uppercase; 115 | } 116 | 117 | h5, h6 { 118 | text-transform: uppercase; 119 | } 120 | 121 | aside { 122 | padding: 1em; 123 | border: 1px solid $print-color-light; 124 | border-radius: 5px; 125 | margin-top: 1.5em; 126 | margin-bottom: 1.5em; 127 | line-height: 1.6; 128 | } 129 | 130 | aside:before { 131 | vertical-align: middle; 132 | padding-right: 0.5em; 133 | font-size: 14px; 134 | } 135 | 136 | aside.notice:before { 137 | @extend %icon-info-sign; 138 | } 139 | 140 | aside.warning:before { 141 | @extend %icon-exclamation-sign; 142 | } 143 | 144 | aside.success:before { 145 | @extend %icon-ok-sign; 146 | } 147 | } -------------------------------------------------------------------------------- /source/includes/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | This section explains how to troubleshoot issues that might occur when interacting with the ZAP API. 4 | 5 | ## Enable Useful Dev Options 6 | 7 | While developing scripts/programs that interact with ZAP API it's recommended that the following [ZAP API options](https://www.zaproxy.org/docs/desktop/ui/dialogs/options/api/) 8 | are enabled, to have more information about possible errors: 9 | 10 | - `Report permission errors via API` 11 | - `Report error details via API` 12 | 13 | The API response will then contain the details about why the API request was rejected or was not successful. 14 | 17 | 18 | ## Common Errors 19 | 20 | ### Wrong API Key or Address Not Allowed 21 | 22 | ```java 23 | //org.zaproxy.clientapi.core.ClientApiException: java.net.SocketException: Unexpected end of file from server 24 | // at org.zaproxy.clientapi.core.ClientApi.callApiDom(ClientApi.java:366) 25 | // at org.zaproxy.clientapi.core.ClientApi.callApi(ClientApi.java:350) 26 | // at org.zaproxy.clientapi.gen.Spider.scan(Spider.java:242) 27 | ``` 28 | 29 | ```python 30 | requests.exceptions.ProxyError: HTTPConnectionPool(host='127.0.0.1', port=8080): Max retries exceeded with 31 | url: http://zap/JSON/spider/action/scan/?apikey=changeMe&url=https%3A%2F%2Fexample.com 32 | (Caused by ProxyError('Cannot connect to proxy.', RemoteDisconnected('Remote end closed connection without response'))) 33 | ``` 34 | 35 | By default, ZAP will close the connection without a response if an API request is not from an allowed address or the API key is wrong. 36 | If you get exceptions similar to the following ensure that the API client is using the correct API key and that the address is allowed. 37 | 38 | ### No Connection to ZAP 39 | 40 | ```java 41 | //org.zaproxy.clientapi.core.ClientApiException: java.net.ConnectException: Connection refused: connect 42 | // at org.zaproxy.clientapi.core.ClientApi.callApiDom(ClientApi.java:366) 43 | // at org.zaproxy.clientapi.core.ClientApi.callApi(ClientApi.java:350) 44 | // at org.zaproxy.clientapi.gen.Spider.scan(Spider.java:242) 45 | // at ZAP_tests.Spider.main(Spider.java:25) 46 | ``` 47 | 48 | ```python 49 | requests.exceptions.ProxyError: HTTPConnectionPool(host='127.0.0.1', port=8080): Max retries exceeded with 50 | url: http://zap/JSON/spider/action/scan/?apikey=changeMe&url=https%3A%2F%2Fexample.com 51 | (Caused by ProxyError('Cannot connect to proxy.', NewConnectionError(': Failed to establish a new connection: [Errno 61] Connection refused'))) 53 | ``` 54 | 55 | There are several reasons that the API client might not be able to connect to ZAP: 56 | 57 | - ZAP is not yet started, some clients might have methods to wait for ZAP; 58 | - ZAP is not listening on the address, for example, if the API client is connecting from an external machine then ZAP will have to listen on the external address (or all addresses `0.0.0.0`) 59 | - The API client is not configured with correct address/port; 60 | 61 | ### Error: No Implementor 62 | 63 | If you come across the `No Implementor Error` while invoking the APIs: Check the necessary add-on or component is installed and enabled. 64 | (For example if you receive "no_implementor" in relation to Ajax Spider calls, perhaps the Ajax Spider add-on isn't installed.) 65 | 66 | -------------------------------------------------------------------------------- /source/stylesheets/_rtl.scss: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // RTL Styles Variables 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | $default: auto; 6 | 7 | //////////////////////////////////////////////////////////////////////////////// 8 | // TABLE OF CONTENTS 9 | //////////////////////////////////////////////////////////////////////////////// 10 | 11 | #toc>ul>li>a>span { 12 | float: left; 13 | } 14 | 15 | .toc-wrapper { 16 | transition: right 0.3s ease-in-out !important; 17 | left: $default !important; 18 | #{right}: 0; 19 | } 20 | 21 | .toc-h2 { 22 | padding-#{right}: $nav-padding + $nav-indent; 23 | } 24 | 25 | #nav-button { 26 | #{right}: 0; 27 | transition: right 0.3s ease-in-out; 28 | &.open { 29 | right: $nav-width 30 | } 31 | } 32 | 33 | //////////////////////////////////////////////////////////////////////////////// 34 | // PAGE LAYOUT AND CODE SAMPLE BACKGROUND 35 | //////////////////////////////////////////////////////////////////////////////// 36 | .page-wrapper { 37 | margin-#{left}: $default !important; 38 | margin-#{right}: $nav-width; 39 | .dark-box { 40 | #{right}: $default; 41 | #{left}: 0; 42 | } 43 | } 44 | 45 | .lang-selector { 46 | width: $default !important; 47 | a { 48 | float: right; 49 | } 50 | } 51 | 52 | //////////////////////////////////////////////////////////////////////////////// 53 | // CODE SAMPLE STYLES 54 | //////////////////////////////////////////////////////////////////////////////// 55 | .content { 56 | &>h1, 57 | &>h2, 58 | &>h3, 59 | &>h4, 60 | &>h5, 61 | &>h6, 62 | &>p, 63 | &>table, 64 | &>ul, 65 | &>ol, 66 | &>aside, 67 | &>dl { 68 | margin-#{left}: $examples-width; 69 | margin-#{right}: $default !important; 70 | } 71 | &>ul, 72 | &>ol { 73 | padding-#{right}: $main-padding + 15px; 74 | } 75 | table { 76 | th, 77 | td { 78 | text-align: right; 79 | } 80 | } 81 | dd { 82 | margin-#{right}: 15px; 83 | } 84 | aside { 85 | aside:before { 86 | padding-#{left}: 0.5em; 87 | } 88 | .search-highlight { 89 | background: linear-gradient(to top right, #F7E633 0%, #F1D32F 100%); 90 | } 91 | } 92 | pre, 93 | blockquote { 94 | float: left !important; 95 | clear: left !important; 96 | } 97 | } 98 | 99 | //////////////////////////////////////////////////////////////////////////////// 100 | // TYPOGRAPHY 101 | //////////////////////////////////////////////////////////////////////////////// 102 | h1, 103 | h2, 104 | h3, 105 | h4, 106 | h5, 107 | h6, 108 | p, 109 | aside { 110 | text-align: right; 111 | direction: rtl; 112 | } 113 | 114 | .toc-wrapper { 115 | text-align: right; 116 | direction: rtl; 117 | font-weight: 100 !important; 118 | } 119 | 120 | 121 | //////////////////////////////////////////////////////////////////////////////// 122 | // RESPONSIVE DESIGN 123 | //////////////////////////////////////////////////////////////////////////////// 124 | @media (max-width: $tablet-width) { 125 | .toc-wrapper { 126 | #{right}: -$nav-width; 127 | &.open { 128 | #{right}: 0; 129 | } 130 | } 131 | .page-wrapper { 132 | margin-#{right}: 0; 133 | } 134 | } 135 | 136 | @media (max-width: $phone-width) { 137 | %left-col { 138 | margin-#{left}: 0; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /.github/actions/update-website/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const core = require('@actions/core') 3 | const exec = require('@actions/exec') 4 | const { getOctokit, context } = require('@actions/github') 5 | const io = require('@actions/io') 6 | 7 | async function git(dir, args, options) { 8 | return exec.exec('git', args, { ...{ cwd: dir }, ...options }) 9 | } 10 | 11 | function getRequiredInput(name) { 12 | return core.getInput(name, { required: true }) 13 | } 14 | 15 | async function run() { 16 | try { 17 | const owner = context.repo.owner 18 | const repo = context.repo.repo 19 | 20 | const docsDir = path.resolve(process.env.GITHUB_WORKSPACE, repo) 21 | const websiteRepoName = getRequiredInput('repo') 22 | const websiteDir = path.resolve(process.env.GITHUB_WORKSPACE, websiteRepoName) 23 | const websiteDocsDir = path.resolve(websiteDir, 'docs/api') 24 | 25 | await io.rmRF(websiteDocsDir) 26 | await io.cp(path.resolve(docsDir, 'build'), websiteDocsDir, { recursive: true }) 27 | 28 | core.info('Checking for changes...') 29 | await git(websiteDir, ['add', '.']) 30 | const code = await git(websiteDir, ['diff-index', '--cached', '--quiet', 'HEAD', '--'], { ignoreReturnCode: true }) 31 | core.info(`Result: ${code}`) 32 | if (code === 0) { 33 | return 34 | } 35 | 36 | const user = getRequiredInput('user') 37 | const email = getRequiredInput('email') 38 | const authToken = process.env.AUTH_TOKEN 39 | const branch = 'update-api-docs' 40 | const pullRequestParams = { 41 | owner: owner, 42 | repo: websiteRepoName, 43 | base: getRequiredInput('branch'), 44 | head: `${user}:${branch}` 45 | } 46 | 47 | const octokit = getOctokit(authToken).rest; 48 | 49 | const pulls = await octokit.pulls.list({ 50 | ...pullRequestParams, 51 | state: 'open' 52 | }) 53 | 54 | let pullNumber 55 | if (pulls.data.length !== 0) { 56 | pullNumber = pulls.data[0].number 57 | core.info(`Found existing pull request: #${pullNumber}`) 58 | } 59 | 60 | const gitHubBaseUrl = 'https://github.com' 61 | const title = 'Update API docs' 62 | const body = `From:\n${owner}/${repo}@${context.sha}` 63 | const commitMessage = `${title}\n\n${body}` 64 | 65 | core.info('Setting user configs...') 66 | await git(websiteDir, ['config', '--local', 'user.name', user]) 67 | await git(websiteDir, ['config', '--local', 'user.email', email]) 68 | const authHeader = `Authorization: Basic ${Buffer.from(`${user}:${authToken}`).toString('base64')}` 69 | await git(websiteDir, ['config', '--local', `http.${gitHubBaseUrl}/.extraheader`, authHeader], { silent: true }) 70 | core.info('Changing remote...') 71 | await git(websiteDir, ['remote', 'set-url', 'origin', `${gitHubBaseUrl}/${user}/${websiteRepoName}`]) 72 | core.info('Checking out branch...') 73 | await git(websiteDir, ['checkout', '-b', branch]) 74 | core.info('Committing...') 75 | await git(websiteDir, ['commit', '-sm', commitMessage]) 76 | core.info('Pushing...') 77 | await git(websiteDir, ['push', '-f', 'origin', branch]) 78 | 79 | if (pullNumber) { 80 | core.info('Updating pull request...') 81 | await octokit.pulls.update({ 82 | owner: owner, 83 | repo: websiteRepoName, 84 | pull_number: pullNumber, 85 | body: body, 86 | }) 87 | } else { 88 | core.info('Creating pull request...') 89 | await octokit.pulls.create({ 90 | ...pullRequestParams, 91 | title: title, 92 | body: body, 93 | }) 94 | } 95 | 96 | } 97 | catch (error) { 98 | core.setFailed(error.message) 99 | } 100 | 101 | } 102 | 103 | run() 104 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (7.0.8) 5 | concurrent-ruby (~> 1.0, >= 1.0.2) 6 | i18n (>= 1.6, < 2) 7 | minitest (>= 5.1) 8 | tzinfo (~> 2.0) 9 | addressable (2.8.6) 10 | public_suffix (>= 2.0.2, < 6.0) 11 | autoprefixer-rails (10.4.13.0) 12 | execjs (~> 2) 13 | backports (3.24.1) 14 | base64 (0.2.0) 15 | coffee-script (2.4.1) 16 | coffee-script-source 17 | execjs 18 | coffee-script-source (1.12.2) 19 | concurrent-ruby (1.3.4) 20 | contracts (0.16.1) 21 | dotenv (2.8.1) 22 | erubis (2.7.0) 23 | execjs (2.9.1) 24 | fast_blank (1.0.1) 25 | fastimage (2.3.0) 26 | ffi (1.16.3) 27 | haml (5.2.2) 28 | temple (>= 0.8.0) 29 | tilt 30 | hamster (3.0.0) 31 | concurrent-ruby (~> 1.0) 32 | hashie (3.6.0) 33 | i18n (1.6.0) 34 | concurrent-ruby (~> 1.0) 35 | kramdown (2.4.0) 36 | rexml 37 | listen (3.8.0) 38 | rb-fsevent (~> 0.10, >= 0.10.3) 39 | rb-inotify (~> 0.9, >= 0.9.10) 40 | memoist (0.16.2) 41 | middleman (4.5.1) 42 | coffee-script (~> 2.2) 43 | haml (>= 4.0.5) 44 | kramdown (>= 2.3.0) 45 | middleman-cli (= 4.5.1) 46 | middleman-core (= 4.5.1) 47 | middleman-autoprefixer (3.0.0) 48 | autoprefixer-rails (~> 10.0) 49 | middleman-core (>= 4.0.0) 50 | middleman-cli (4.5.1) 51 | thor (>= 0.17.0, < 1.3.0) 52 | middleman-core (4.5.1) 53 | activesupport (>= 6.1, < 7.1) 54 | addressable (~> 2.4) 55 | backports (~> 3.6) 56 | bundler (~> 2.0) 57 | contracts (~> 0.13, < 0.17) 58 | dotenv 59 | erubis 60 | execjs (~> 2.0) 61 | fast_blank 62 | fastimage (~> 2.0) 63 | hamster (~> 3.0) 64 | hashie (~> 3.4) 65 | i18n (~> 1.6.0) 66 | listen (~> 3.0) 67 | memoist (~> 0.14) 68 | padrino-helpers (~> 0.15.0) 69 | parallel 70 | rack (>= 1.4.5, < 3) 71 | sassc (~> 2.0) 72 | servolux 73 | tilt (~> 2.0.9) 74 | toml 75 | uglifier (~> 3.0) 76 | webrick 77 | middleman-sprockets (4.1.1) 78 | middleman-core (~> 4.0) 79 | sprockets (>= 3.0) 80 | middleman-syntax (3.4.0) 81 | middleman-core (>= 3.2) 82 | rouge (~> 3.2) 83 | minitest (5.21.2) 84 | nokogiri (1.18.10-x86_64-linux-gnu) 85 | racc (~> 1.4) 86 | padrino-helpers (0.15.3) 87 | i18n (>= 0.6.7, < 2) 88 | padrino-support (= 0.15.3) 89 | tilt (>= 1.4.1, < 3) 90 | padrino-support (0.15.3) 91 | parallel (1.24.0) 92 | parslet (2.0.0) 93 | public_suffix (5.0.4) 94 | racc (1.8.1) 95 | rack (2.2.20) 96 | rb-fsevent (0.11.2) 97 | rb-inotify (0.10.1) 98 | ffi (~> 1.0) 99 | redcarpet (3.6.1) 100 | rexml (3.4.2) 101 | rouge (3.30.0) 102 | sass (3.7.4) 103 | sass-listen (~> 4.0.0) 104 | sass-listen (4.0.0) 105 | rb-fsevent (~> 0.9, >= 0.9.4) 106 | rb-inotify (~> 0.9, >= 0.9.7) 107 | sassc (2.4.0) 108 | ffi (~> 1.9) 109 | servolux (0.13.0) 110 | sprockets (3.7.5) 111 | base64 112 | concurrent-ruby (~> 1.0) 113 | rack (> 1, < 3) 114 | temple (0.10.3) 115 | thor (1.2.2) 116 | tilt (2.0.11) 117 | toml (0.3.0) 118 | parslet (>= 1.8.0, < 3.0.0) 119 | tzinfo (2.0.6) 120 | concurrent-ruby (~> 1.0) 121 | uglifier (3.2.0) 122 | execjs (>= 0.3.0, < 3) 123 | webrick (1.8.2) 124 | 125 | PLATFORMS 126 | x86_64-linux 127 | 128 | DEPENDENCIES 129 | haml (>= 4.0.5, < 6.0) 130 | middleman (= 4.5.1) 131 | middleman-autoprefixer (~> 3.0.0) 132 | middleman-sprockets (~> 4.1.1) 133 | middleman-syntax (~> 3.4.0) 134 | nokogiri (~> 1.18.10) 135 | redcarpet (~> 3.6.1) 136 | rouge (~> 3.30.0) 137 | sass (= 3.7.4) 138 | sprockets (= 3.7.5) 139 | 140 | RUBY VERSION 141 | ruby 3.2.2p53 142 | 143 | BUNDLED WITH 144 | 2.4.10 145 | -------------------------------------------------------------------------------- /source/stylesheets/_variables.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2008-2013 Concur Technologies, Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | not use this file except in compliance with the License. You may obtain 6 | a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | License for the specific language governing permissions and limitations 14 | under the License. 15 | */ 16 | 17 | 18 | //////////////////////////////////////////////////////////////////////////////// 19 | // CUSTOMIZE SLATE 20 | //////////////////////////////////////////////////////////////////////////////// 21 | // Use these settings to help adjust the appearance of Slate 22 | 23 | // BACKGROUND COLORS 24 | //////////////////// 25 | $nav-bg: #EAEAEA !default; 26 | $examples-bg: #393939 !default; 27 | $code-bg: #292929 !default; 28 | $code-annotation-bg: #1c1c1c !default; 29 | $nav-subitem-bg: #EAEAEA !default; 30 | $nav-active-bg: #fbfcff !default; 31 | $nav-active-parent-bg: #EAEAEA !default; // parent links of the current section 32 | $lang-select-border: #000000 !default; 33 | $lang-select-bg: #242424 !default; 34 | $lang-select-active-bg: $examples-bg !default; // feel free to change this to blue or something 35 | $lang-select-pressed-bg: #111 !default; // color of language tab bg when mouse is pressed 36 | $main-bg: #ffffff !default; 37 | $aside-notice-bg: #d6e9f1 !default; 38 | $aside-warning-bg: rgba(255, 46, 70, 0.42) !default; 39 | $aside-success-bg: rgba(106, 193, 116, 0.79) !default; 40 | $search-notice-bg: #b56d70 !default; 41 | 42 | 43 | // TEXT COLORS 44 | //////////////////// 45 | $main-text: #2b2b2b !default; // main content text color 46 | $nav-text: #4f6772 !default; 47 | $nav-active-text: #53a8cc !default; 48 | $nav-active-parent-text: #4f6772 !default; // parent links of the current section 49 | $lang-select-text: #fff !default; // color of unselected language tab text 50 | $lang-select-active-text: #fff !default; // color of selected language tab text 51 | $lang-select-pressed-text: #fff !default; // color of language tab text when mouse is pressed 52 | 53 | 54 | // SIZES 55 | //////////////////// 56 | $nav-width: 230px !default; // width of the navbar 57 | $examples-width: 40% !default; // portion of the screen taken up by code examples 58 | $logo-margin: 20px !default; // margin below logo 59 | $main-padding: 28px !default; // padding to left and right of content & examples 60 | $nav-padding: 10px !default; // padding to left and right of navbar 61 | $nav-v-padding: 10px !default; // padding used vertically around search boxes and results 62 | $nav-indent: 10px !default; // extra padding for ToC subitems 63 | $code-annotation-padding: 13px !default; // padding inside code annotations 64 | $h1-margin-bottom: 21px !default; // padding under the largest header tags 65 | $tablet-width: 930px !default; // min width before reverting to tablet size 66 | $phone-width: $tablet-width - $nav-width !default; // min width before reverting to mobile size 67 | 68 | 69 | // FONTS 70 | //////////////////// 71 | %default-font { 72 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 73 | font-size: 13px; 74 | } 75 | 76 | %header-font { 77 | @extend %default-font; 78 | font-weight: bold; 79 | } 80 | 81 | %code-font { 82 | font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace, serif; 83 | font-size: 12px; 84 | line-height: 1.5; 85 | } 86 | 87 | 88 | // OTHER 89 | //////////////////// 90 | $nav-footer-border-color: #666 !default; 91 | $search-box-border-color: #008aca !default; 92 | 93 | 94 | //////////////////////////////////////////////////////////////////////////////// 95 | // INTERNAL 96 | //////////////////////////////////////////////////////////////////////////////// 97 | // These settings are probably best left alone. 98 | 99 | %break-words { 100 | word-break: break-all; 101 | hyphens: auto; 102 | } 103 | 104 | 105 | -------------------------------------------------------------------------------- /source/javascripts/app/_toc.js: -------------------------------------------------------------------------------- 1 | //= require ../lib/_jquery 2 | //= require ../lib/_imagesloaded.min 3 | ;(function () { 4 | 'use strict'; 5 | 6 | var htmlPattern = /<[^>]*>/g; 7 | var loaded = false; 8 | 9 | var debounce = function(func, waitTime) { 10 | var timeout = false; 11 | return function() { 12 | if (timeout === false) { 13 | setTimeout(function() { 14 | func(); 15 | timeout = false; 16 | }, waitTime); 17 | timeout = true; 18 | } 19 | }; 20 | }; 21 | 22 | var closeToc = function() { 23 | $(".toc-wrapper").removeClass('open'); 24 | $("#nav-button").removeClass('open'); 25 | }; 26 | 27 | function loadToc($toc, tocLinkSelector, tocListSelector, scrollOffset) { 28 | var headerHeights = {}; 29 | var pageHeight = 0; 30 | var windowHeight = 0; 31 | var originalTitle = document.title; 32 | 33 | var recacheHeights = function() { 34 | headerHeights = {}; 35 | pageHeight = $(document).height(); 36 | windowHeight = $(window).height(); 37 | 38 | $toc.find(tocLinkSelector).each(function() { 39 | var targetId = $(this).attr('href'); 40 | if (targetId[0] === "#") { 41 | headerHeights[targetId] = $(targetId).offset().top; 42 | } 43 | }); 44 | }; 45 | 46 | var refreshToc = function() { 47 | var currentTop = $(document).scrollTop() + scrollOffset; 48 | 49 | if (currentTop + windowHeight >= pageHeight) { 50 | // at bottom of page, so just select last header by making currentTop very large 51 | // this fixes the problem where the last header won't ever show as active if its content 52 | // is shorter than the window height 53 | currentTop = pageHeight + 1000; 54 | } 55 | 56 | var best = null; 57 | for (var name in headerHeights) { 58 | if ((headerHeights[name] < currentTop && headerHeights[name] > headerHeights[best]) || best === null) { 59 | best = name; 60 | } 61 | } 62 | 63 | // Catch the initial load case 64 | if (currentTop == scrollOffset && !loaded) { 65 | best = window.location.hash; 66 | loaded = true; 67 | } 68 | 69 | var $best = $toc.find("[href='" + best + "']").first(); 70 | if (!$best.hasClass("active")) { 71 | // .active is applied to the ToC link we're currently on, and its parent