├── .ci_scripts ├── lint_workflows.sh └── test_workflows.sh ├── .github └── workflows │ └── test.yml ├── LICENSE ├── README.md ├── cookiecutter.json ├── hooks ├── post_gen_project.sh └── pre_gen_project.sh └── {{ cookiecutter.project_slug }} ├── .ci_scripts ├── install_zeek.sh ├── install_zkg.sh └── test.sh ├── .github ├── ISSUE_TEMPLATE.md └── workflows │ └── btest.yml ├── CMakeLists.txt ├── COPYING ├── Makefile ├── README ├── README.rst ├── VERSION ├── configure ├── configure.plugin ├── contributors ├── docs ├── CODE_OF_CONDUCT.md ├── Makefile ├── README.rst ├── _static │ └── theme_overrides.css ├── _templates │ ├── breadcrumbs.html │ └── layout.html ├── conf.py ├── generate_docs.sh ├── genindex.rst ├── index.rst └── sphinxcontrib │ ├── __init__.py │ ├── zeek.py │ └── zeekygen.py ├── scripts ├── __load__.bro ├── __load__.zeek ├── __preload__.zeek ├── types.zeek └── {{ cookiecutter.project_namespace }} │ └── {{ cookiecutter.project_slug }} │ ├── README │ ├── __load__.bro │ ├── __load__.zeek │ ├── dpd.sig │ └── main.zeek ├── src ├── .clang-format ├── Plugin.cc ├── Plugin.h ├── events.bif ├── {{ cookiecutter.protocol_name }}.cc ├── {{ cookiecutter.protocol_name }}.h ├── {{ cookiecutter.protocol_name|lower }}-analyzer.pac ├── {{ cookiecutter.protocol_name|lower }}-protocol.pac └── {{ cookiecutter.protocol_name|lower }}.pac ├── tests ├── .gitignore ├── Makefile ├── Scripts │ ├── combine-profiler-files │ ├── coverage │ ├── coverage-calc │ ├── coverage-to-gcov │ ├── diff-remove-timestamps │ ├── finalizer │ ├── get-zeek-env │ └── mock_functions.zeek ├── Traces │ └── .gitkeep ├── btest.cfg ├── random.seed └── {{ cookiecutter.project_slug }} │ ├── run_all_mode.zeek │ ├── run_all_mode_parse_only.zeek │ ├── run_bare_mode.zeek │ ├── run_bare_mode_parse_only.zeek │ ├── run_default_mode.zeek │ ├── run_default_mode_parse_only.zeek │ └── run_plugin_activation.zeek ├── zeek_v3.0 └── src │ ├── Plugin.cc │ ├── Plugin.h │ ├── {{ cookiecutter.protocol_name }}.cc │ ├── {{ cookiecutter.protocol_name }}.h │ └── {{ cookiecutter.protocol_name|lower }}-analyzer.pac ├── zeek_v3.1 ├── zeek_v3.2 └── src │ ├── Plugin.cc │ ├── {{ cookiecutter.protocol_name }}.cc │ └── {{ cookiecutter.protocol_name }}.h └── zkg.meta /.ci_scripts/lint_workflows.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | curl -o /tmp/github-workflow.json https://raw.githubusercontent.com/Logerfo/schemastore/master/src/schemas/json/github-workflow.json 6 | 7 | for i in *_analyzer/.github/workflows/*.yml 8 | do 9 | name=$(basename "$i") 10 | cat $i | python3 -c 'import json, sys; from ruamel import yaml ; y=yaml.safe_load(sys.stdin.read()) ; json.dump(y, sys.stdout)' > /tmp/workflow-lint-$name.json 11 | echo "Validating GitHub workflow $i" 12 | jsonschema -i /tmp/workflow-lint-$name.json /tmp/github-workflow.json 13 | done 14 | -------------------------------------------------------------------------------- /.ci_scripts/test_workflows.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ZEEK_VER=$1 6 | 7 | env 8 | 9 | for i in /tmp/workflow-lint-*.json 10 | do 11 | jq -c '.jobs.test.steps[].run' "$i" | egrep . | egrep -v "^null$" | while read -r cmd 12 | do 13 | cd *_analyzer 14 | cmd=$(echo "$cmd" | sed -e 's/$GITHUB_WORKSPACE/$PWD/g') 15 | cmd=$(echo "$cmd" | sed -e 's/${{ matrix.zeek }}/'$ZEEK_VER'/g') 16 | cmd=$(echo "$cmd" | sed -e 's/^"//' | sed -e 's/"$//') 17 | echo "Running command: $cmd" 18 | eval "$(printf "$cmd")" 19 | cd - 20 | done 21 | done 22 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will generate a package, and then make sure that package's tests work. 2 | 3 | name: tests 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | on: 7 | push: 8 | pull_request: 9 | 10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 11 | jobs: 12 | test: 13 | name: tests on Linux with ${{ matrix.zeek }} 14 | runs-on: ubuntu-18.04 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | zeek: ["zeek", "zeek-lts", "zeek-nightly"] 20 | 21 | # Steps represent a sequence of tasks that will be executed as part of the job 22 | steps: 23 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 24 | - uses: actions/checkout@v2 25 | 26 | - name: Configure git 27 | run: | 28 | git config --global user.email "you@example.com" 29 | git config --global user.name "Your Name" 30 | 31 | - name: Install Zeek ${{ matrix.zeek }} RPM 32 | run: sudo $GITHUB_WORKSPACE/\{\{\ cookiecutter.project_slug\ \}\}/.ci_scripts/install_zeek.sh "${{ matrix.zeek }}" 33 | 34 | - name: Install cookiecutter and schema checker 35 | run: | 36 | sudo apt install jq python3-setuptools 37 | sudo pip3 install cookiecutter jsonschema ruamel-yaml 38 | 39 | - name: Run cookiecutter 40 | run: PATH="/opt/zeek/bin:/opt/zeek-nightly/bin:$PATH" cookiecutter --no-input $GITHUB_WORKSPACE 41 | 42 | - name: Lint resulting workflow 43 | run: $GITHUB_WORKSPACE/.ci_scripts/lint_workflows.sh 44 | 45 | - name: Test resulting workflow 46 | run: $GITHUB_WORKSPACE/.ci_scripts/test_workflows.sh ${{ matrix.zeek }} 47 | 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019, The Regents of the University of California 2 | through the Lawrence Berkeley National Laboratory. All rights 3 | reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | (1) Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | (2) Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | (3) Neither the name of the University of California, Lawrence Berkeley 16 | National Laboratory, U.S. Dept. of Energy, International Computer 17 | Science Institute, nor the names of contributors may be used to endorse 18 | or promote products derived from this software without specific prior 19 | written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. 32 | 33 | Note that some files in the distribution may carry their own copyright 34 | notices. 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cookiecutter Zeek BinPAC Package 2 | -------------------------------- 3 | 4 | Cookiecutter template for a Zeek package implementing a protocol analyzer written in BinPAC. 5 | 6 | * GitHub repo: https://github.com/grigorescu/binpac_quickstart 7 | * Free software: BSD license 8 | 9 | Features 10 | -------- 11 | 12 | * Script testing with ``btest`` 13 | * GitHub integration: Actions for testing and building documentation as GitHub Pages 14 | * GitLab CI support 15 | * Code coverage analysis 16 | 17 | Quickstart 18 | ---------- 19 | 20 | Install the latest Cookiecutter if you haven't installed it yet (this requires 21 | Cookiecutter 1.4.0 or higher): 22 | 23 | pip install -U cookiecutter 24 | 25 | Generate a Zeek package project: 26 | 27 | cookiecutter https://github.com/grigorescu/binpac_quickstart.git 28 | 29 | Answer some questions, and it will create a new directory for you, initialized as a git repo. 30 | 31 | If you'd like free Zeek script coverage reports via [Coveralls](https://coveralls.io), login and sync your repositories. 32 | 33 | Configuration 34 | ------------- 35 | 36 | Some of the questions that that cookiecutter will prompt you for will likely be the same across many different packages. You can create a [cookiecutter configuration file](https://cookiecutter.readthedocs.io/en/1.7.2/advanced/user_config.html) as `~/.cookiecutterrc`, which will be read as the defaults: 37 | 38 | ``` yaml 39 | default_context: 40 | github_username: "grigorescu" 41 | project_credits: "Vlad Grigorescu " 42 | project_namespace: "ESnet" 43 | copyright_owner: "Energy Sciences Network" 44 | open_source_license: "BSD license" 45 | ``` 46 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "protocol_name": "AIM", 3 | "L4_protocol": ["TCP", "UDP", "None (Warning: might not work)"], 4 | 5 | "project_name": "{{ cookiecutter.protocol_name }} Protocol Analyzer", 6 | "project_slug": "{{ cookiecutter.protocol_name.lower().replace(' ', '_').replace('-', '_') }}_analyzer", 7 | "project_short_description": "{{ cookiecutter.project_name }} for Zeek", 8 | "project_credits": "Alice , Bob ", 9 | "project_namespace": "Local", 10 | 11 | "zeek_version": "Automatically determined from the installed version, or '3.2' if zeek-config not found", 12 | 13 | "metadata_tags": "network, evil, rfc3514", 14 | 15 | "github_username": "alice", 16 | 17 | "copyright_owner": "Veridian Technologies", 18 | "open_source_license": ["BSD license", "MIT license", "ISC license", "Apache Software License 2.0", "GNU General Public License v3", "Not open source"], 19 | 20 | "_copy_without_render": [ 21 | "docs/_templates/*.html", 22 | ".github/workflows/*.yml" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /hooks/post_gen_project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -d "zeek_v{{ cookiecutter.zeek_version }}" ] 4 | then 5 | SOURCE="zeek_v{{ cookiecutter.zeek_version }}" 6 | else 7 | VERSION=$(zeek-config --version || echo "3.2") 8 | VERSION=$(echo "$VERSION" | cut -f -2 -d.) 9 | SOURCE="zeek_v$VERSION" 10 | fi 11 | 12 | cp -Rv "$SOURCE"/* . 13 | rm -Rf zeek_v* 14 | 15 | git init 16 | git add . 17 | git commit -m "Initial commit [cookiecutter-zeekpackage]" 18 | 19 | -------------------------------------------------------------------------------- /hooks/pre_gen_project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/.ci_scripts/install_zeek.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo 'deb http://download.opensuse.org/repositories/security:/zeek/xUbuntu_18.04/ /' | sudo tee /etc/apt/sources.list.d/security:zeek.list 4 | curl -fsSL https://download.opensuse.org/repositories/security:zeek/xUbuntu_18.04/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/security:zeek.gpg > /dev/null 5 | apt update 6 | apt install python3-setuptools python3-sphinx python3-wheel $1 7 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/.ci_scripts/install_zkg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export PATH="/opt/zeek/bin:/opt/zeek-nightly/bin:$PATH" 4 | 5 | pip3 install -U sphinx_rtd_theme zkg 6 | zkg autoconfig 7 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/.ci_scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | function debug_and_die { 6 | OUTPUT_PATH=$HOME/.zkg/testing/{{ cookiecutter.project_slug }}/clones/{{ cookiecutter.project_slug }} 7 | if [ -s $OUTPUT_PATH/zkg.test_command.stdout ]; then 8 | echo "zkg test command stdout" 9 | echo "-----------------------" 10 | cat $OUTPUT_PATH/zkg.test_command.stdout 11 | fi 12 | 13 | if [ -s $OUTPUT_PATH/zkg.test_command.stderr ]; then 14 | echo "zkg test command stderr" 15 | echo "-----------------------" 16 | cat $OUTPUT_PATH/zkg.test_command.stderr 17 | fi 18 | 19 | if [ -s $HOME/.zkg/logs/{{ cookiecutter.project_slug }}-build.log ]; then 20 | echo "zkg build command output" 21 | echo "-----------------------" 22 | cat $HOME/.zkg/logs/{{ cookiecutter.project_slug }}-build.log 23 | fi 24 | 25 | exit 1 26 | } 27 | 28 | export PATH="/opt/zeek/bin:/opt/zeek-nightly/bin:$PATH" 29 | 30 | echo "Running zkg test..." 31 | zkg test "$PWD" || debug_and_die 32 | echo "Tests succeeded. Running zkg install..." 33 | zkg install --force --skiptests "$PWD" || debug_and_die 34 | echo "Install succeeded." 35 | 36 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * {{ cookiecutter.project_name }} version: 2 | * Zeek version: 3 | * Operating System: 4 | 5 | ### Description 6 | 7 | Describe what you were trying to get done. 8 | Tell us what happened, what went wrong, and what you expected to happen. 9 | If there's specific network traffic that triggers this issue, please attach it as a separate file. 10 | 11 | ### What I Did 12 | 13 | ``` 14 | Paste any configuration of the package that you had here. 15 | If there was a crash, please include the traceback here. 16 | ``` 17 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/.github/workflows/btest.yml: -------------------------------------------------------------------------------- 1 | # This workflow will execute the accompanying btests 2 | 3 | name: btest CI 4 | 5 | # Controls when the action will run. Triggers the workflow on push or pull request 6 | on: 7 | push: 8 | pull_request: 9 | 10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 11 | jobs: 12 | # This workflow contains a single job called "build" 13 | test: 14 | name: btests on Linux with ${{ matrix.zeek }} 15 | runs-on: ubuntu-18.04 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | zeek: ["zeek", "zeek-lts", "zeek-nightly"] 21 | 22 | # Steps represent a sequence of tasks that will be executed as part of the job 23 | steps: 24 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 25 | - uses: actions/checkout@v2 26 | 27 | - name: Install Zeek ${{ matrix.zeek }} RPM 28 | run: sudo $GITHUB_WORKSPACE/.ci_scripts/install_zeek.sh "${{ matrix.zeek }}" 29 | 30 | - name: Ensure Zeek was correctly installed 31 | run: PATH="/opt/zeek/bin:/opt/zeek-nightly/bin:$PATH" zeek --version 32 | 33 | - name: Install zkg and other pip packages 34 | run: sudo $GITHUB_WORKSPACE/.ci_scripts/install_zkg.sh 35 | 36 | - name: Run test and zkg install 37 | run: sudo $GITHUB_WORKSPACE/.ci_scripts/test.sh 38 | 39 | - name: Generate docs 40 | run: PATH="/opt/zeek/bin:/opt/zeek-nightly/bin:$PATH" make -C $GITHUB_WORKSPACE/docs 41 | if: matrix.zeek == 'zeek' 42 | 43 | - name: Deploy docs 44 | if: matrix.zeek == 'zeek' 45 | uses: JamesIves/github-pages-deploy-action@3.5.9 46 | with: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | BRANCH: gh-pages 49 | FOLDER: $GITHUB_WORKSPACE/docs/html 50 | 51 | - name: Coveralls 52 | if: matrix.zeek == 'zeek' 53 | uses: coverallsapp/github-action@master 54 | with: 55 | github-token: ${{ secrets.GITHUB_TOKEN }} 56 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Generated by binpac_quickstart 2 | cmake_minimum_required(VERSION 3.0 FATAL_ERROR) 3 | 4 | project(ZeekPlugin{{ cookiecutter.protocol_name|upper }}) 5 | 6 | include(ZeekPlugin) 7 | 8 | zeek_plugin_begin({{ cookiecutter.project_namespace }} {{ cookiecutter.protocol_name }}) 9 | zeek_plugin_cc(src/Plugin.cc) 10 | zeek_plugin_cc(src/{{ cookiecutter.protocol_name }}.cc) 11 | zeek_plugin_bif(src/events.bif) 12 | zeek_plugin_dist_files(README.rst COPYING contributors VERSION) 13 | zeek_plugin_pac(src/{{ cookiecutter.protocol_name|lower }}.pac src/{{ cookiecutter.protocol_name|lower }}-analyzer.pac src/{{ cookiecutter.protocol_name|lower }}-protocol.pac) 14 | zeek_plugin_end() 15 | 16 | file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" VERSION LIMIT_COUNT 1) 17 | 18 | if ("${PROJECT_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") 19 | # Allows building rpm/deb packages via "make package" in build dir. 20 | include(ConfigurePackaging) 21 | ConfigurePackaging(${VERSION}) 22 | endif () 23 | 24 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/COPYING: -------------------------------------------------------------------------------- 1 | {% if cookiecutter.open_source_license == 'MIT license' -%} 2 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.copyright_owner }} 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | {% elif cookiecutter.open_source_license == 'BSD license' -%} 22 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.copyright_owner }} 23 | All rights reserved. 24 | 25 | Redistribution and use in source and binary forms, with or without modification, 26 | are permitted provided that the following conditions are met: 27 | 28 | * Redistributions of source code must retain the above copyright notice, this 29 | list of conditions and the following disclaimer. 30 | 31 | * Redistributions in binary form must reproduce the above copyright notice, this 32 | list of conditions and the following disclaimer in the documentation and/or 33 | other materials provided with the distribution. 34 | 35 | * Neither the name of the copyright holder nor the names of its 36 | contributors may be used to endorse or promote products derived from this 37 | software without specific prior written permission. 38 | 39 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 40 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 41 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 42 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 43 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 44 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 45 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 46 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 47 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 48 | OF THE POSSIBILITY OF SUCH DAMAGE. 49 | {% elif cookiecutter.open_source_license == 'ISC license' -%} 50 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.copyright_owner }} 51 | 52 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 53 | 54 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 55 | {% elif cookiecutter.open_source_license == 'Apache Software License 2.0' -%} 56 | Copyright (c) {% now 'local', '%Y' %}, {{ cookiecutter.copyright_owner }} 57 | 58 | Licensed under the Apache License, Version 2.0 (the "License"); 59 | you may not use this file except in compliance with the License. 60 | You may obtain a copy of the License at 61 | 62 | http://www.apache.org/licenses/LICENSE-2.0 63 | 64 | Unless required by applicable law or agreed to in writing, software 65 | distributed under the License is distributed on an "AS IS" BASIS, 66 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 67 | See the License for the specific language governing permissions and 68 | limitations under the License. 69 | {% elif cookiecutter.open_source_license == 'GNU General Public License v3' -%} 70 | GNU GENERAL PUBLIC LICENSE 71 | Version 3, 29 June 2007 72 | 73 | {{ cookiecutter.project_short_description }} 74 | Copyright (C) {% now 'local', '%Y' %} {{ cookiecutter.copyright_owner }} 75 | 76 | This program is free software: you can redistribute it and/or modify 77 | it under the terms of the GNU General Public License as published by 78 | the Free Software Foundation, either version 3 of the License, or 79 | (at your option) any later version. 80 | 81 | This program is distributed in the hope that it will be useful, 82 | but WITHOUT ANY WARRANTY; without even the implied warranty of 83 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 84 | GNU General Public License for more details. 85 | 86 | You should have received a copy of the GNU General Public License 87 | along with this program. If not, see . 88 | 89 | Also add information on how to contact you by electronic and paper mail. 90 | 91 | You should also get your employer (if you work as a programmer) or school, 92 | if any, to sign a "copyright disclaimer" for the program, if necessary. 93 | For more information on this, and how to apply and follow the GNU GPL, see 94 | . 95 | 96 | The GNU General Public License does not permit incorporating your program 97 | into proprietary programs. If your program is a subroutine library, you 98 | may consider it more useful to permit linking proprietary applications with 99 | the library. If this is what you want to do, use the GNU Lesser General 100 | Public License instead of this License. But first, please read 101 | . 102 | {% endif %} 103 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Convenience Makefile providing a few common top-level targets. 3 | # 4 | 5 | cmake_build_dir=build 6 | arch=`uname -s | tr A-Z a-z`-`uname -m` 7 | 8 | all: build-it 9 | 10 | build-it: 11 | @test -e $(cmake_build_dir)/config.status || ./configure 12 | -@test -e $(cmake_build_dir)/CMakeCache.txt && \ 13 | test $(cmake_build_dir)/CMakeCache.txt -ot `cat $(cmake_build_dir)/CMakeCache.txt | grep ZEEK_DIST | cut -d '=' -f 2`/build/CMakeCache.txt && \ 14 | echo Updating stale CMake cache && \ 15 | touch $(cmake_build_dir)/CMakeCache.txt 16 | 17 | ( cd $(cmake_build_dir) && make ) 18 | 19 | install: 20 | ( cd $(cmake_build_dir) && make install ) 21 | 22 | clean: 23 | ( cd $(cmake_build_dir) && make clean ) 24 | 25 | distclean: 26 | rm -rf $(cmake_build_dir) 27 | 28 | test: 29 | make -C tests 30 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/README: -------------------------------------------------------------------------------- 1 | README.rst -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/README.rst: -------------------------------------------------------------------------------- 1 | {% set is_open_source = cookiecutter.open_source_license != 'Not open source' -%} 2 | 3 | Zeek Package for {{ cookiecutter.project_name }} 4 | ================================================ 5 | 6 | .. image:: https://travis-ci.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}.svg?branch=master 7 | :target: https://travis-ci.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }} 8 | :alt: Build Status 9 | 10 | .. image:: https://coveralls.io/repos/github/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}/badge.svg?branch=master 11 | :target: https://coveralls.io/repos/github/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}?branch=master 12 | :alt: Coverage Status 13 | 14 | {% if is_open_source %} 15 | .. image:: https://img.shields.io/github/license/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }}) 16 | :target: :doc:`COPYING <./COPYING>` 17 | :alt: {{ cookiecutter.open_source_license }} 18 | {% endif %} 19 | 20 | ![GitHub]( 21 | 22 | {{ cookiecutter.project_short_description }} 23 | 24 | Getting Started 25 | --------------- 26 | 27 | These instructions will get you a copy of the package up and running on your Zeek cluster. See development for notes on how to install the package in order to hack on or contribute to it. 28 | 29 | Prerequisites 30 | ------------- 31 | 32 | This is a package designed to run with the [Zeek Network Security Monitor](https://zeek.org). First, [get Zeek](https://zeek.org/get-zeek/). We strive to support both the current feature and LTS releases. 33 | 34 | The recommended installation method is via the [Zeek package manager, zkg](https://docs.zeek.org/projects/package-manager/en/stable/). On any recent system, run `pip install zkg`. After installation, run `zkg autoconfig`. For more information, see the [zkg documentation](https://docs.zeek.org/projects/package-manager/en/stable/quickstart.html). 35 | 36 | Installing 37 | ---------- 38 | 39 | To install the package, run: 40 | 41 | ``` 42 | zkg install https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.project_slug }} 43 | ``` 44 | 45 | If this is being installed on a cluster, install the package on the manager, then deploy it via: 46 | 47 | ``` 48 | zeekctl deploy 49 | ``` 50 | 51 | Running the tests 52 | ----------------- 53 | 54 | `zkg` will run the test suite before installing. To manually run the tests, go into the `tests` directory, and run `make`. 55 | 56 | Contributing 57 | ------------ 58 | 59 | Please read [CONTRIBUTING.md](./docs/CONTRIBUTING.md) for details on how to contribute. 60 | 61 | Versioning 62 | ---------- 63 | 64 | We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](../../tags). 65 | 66 | Credits 67 | ------- 68 | 69 | {% for credit in cookiecutter.project_credits.split(', ') %} 70 | * {{ credit }} 71 | {% endfor %} 72 | 73 | See also the list of [contributors](contributors) who participated in this project. 74 | 75 | License 76 | ------- 77 | 78 | {% if is_open_source %}This project is licensed under the {{ cookiecutter.open_source_license }}.{% endif %} See the [COPYING](COPYING) file for details. 79 | 80 | Acknowledgments 81 | --------------- 82 | 83 | * ESnet team for Zeek Package Cookie Cutter 84 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/VERSION: -------------------------------------------------------------------------------- 1 | 0.1.0 -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Wrapper for viewing/setting options that the plugin's CMake 4 | # scripts will recognize. 5 | # 6 | # Don't edit this. Edit configure.plugin to add plugin-specific options. 7 | # 8 | 9 | set -e 10 | command="$0 $*" 11 | 12 | if [ -e `dirname $0`/configure.plugin ]; then 13 | # Include custom additions. 14 | . `dirname $0`/configure.plugin 15 | fi 16 | 17 | usage() { 18 | 19 | cat 1>&2 </dev/null 2>&1; then 34 | plugin_usage 1>&2 35 | fi 36 | 37 | echo 38 | 39 | exit 1 40 | } 41 | 42 | # Function to append a CMake cache entry definition to the 43 | # CMakeCacheEntries variable 44 | # $1 is the cache entry variable name 45 | # $2 is the cache entry variable type 46 | # $3 is the cache entry variable value 47 | append_cache_entry () { 48 | CMakeCacheEntries="$CMakeCacheEntries -D $1:$2=$3" 49 | } 50 | 51 | # set defaults 52 | builddir=build 53 | zeekdist="" 54 | installroot="default" 55 | CMakeCacheEntries="" 56 | 57 | while [ $# -ne 0 ]; do 58 | case "$1" in 59 | -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; 60 | *) optarg= ;; 61 | esac 62 | 63 | case "$1" in 64 | --help|-h) 65 | usage 66 | ;; 67 | 68 | --cmake=*) 69 | CMakeCommand=$optarg 70 | ;; 71 | 72 | --zeek-dist=*) 73 | zeekdist=`cd $optarg && pwd` 74 | ;; 75 | 76 | --install-root=*) 77 | installroot=$optarg 78 | ;; 79 | 80 | --with-binpac=*) 81 | append_cache_entry BinPAC_ROOT_DIR PATH $optarg 82 | binpac_root=$optarg 83 | ;; 84 | 85 | --with-broker=*) 86 | append_cache_entry BROKER_ROOT_DIR PATH $optarg 87 | broker_root=$optarg 88 | ;; 89 | 90 | --with-caf=*) 91 | append_cache_entry CAF_ROOT_DIR PATH $optarg 92 | caf_root=$optarg 93 | ;; 94 | 95 | --with-bifcl=*) 96 | append_cache_entry BifCl_EXE PATH $optarg 97 | ;; 98 | 99 | --enable-debug) 100 | append_cache_entry BRO_PLUGIN_ENABLE_DEBUG BOOL true 101 | ;; 102 | 103 | *) 104 | if type plugin_option >/dev/null 2>&1; then 105 | plugin_option $1 && shift && continue; 106 | fi 107 | 108 | echo "Invalid option '$1'. Try $0 --help to see available options." 109 | exit 1 110 | ;; 111 | esac 112 | shift 113 | done 114 | 115 | if [ -z "$CMakeCommand" ]; then 116 | # prefer cmake3 over "regular" cmake (cmake == cmake2 on RHEL) 117 | if command -v cmake3 >/dev/null 2>&1 ; then 118 | CMakeCommand="cmake3" 119 | elif command -v cmake >/dev/null 2>&1 ; then 120 | CMakeCommand="cmake" 121 | else 122 | echo "This package requires CMake, please install it first." 123 | echo "Then you may use this script to configure the CMake build." 124 | echo "Note: pass --cmake=PATH to use cmake in non-standard locations." 125 | exit 1; 126 | fi 127 | fi 128 | 129 | if [ -z "$zeekdist" ]; then 130 | if type zeek-config >/dev/null 2>&1; then 131 | zeek_config="zeek-config" 132 | zeekdist=`${zeek_config} --zeek_dist` 133 | else 134 | echo "Either 'zeek-config' must be in PATH or '--zeek-dist=' used" 135 | exit 1 136 | fi 137 | 138 | append_cache_entry BRO_CONFIG_PREFIX PATH `${zeek_config} --prefix` 139 | append_cache_entry BRO_CONFIG_INCLUDE_DIR PATH `${zeek_config} --include_dir` 140 | append_cache_entry BRO_CONFIG_PLUGIN_DIR PATH `${zeek_config} --plugin_dir` 141 | append_cache_entry BRO_CONFIG_CMAKE_DIR PATH `${zeek_config} --cmake_dir` 142 | append_cache_entry CMAKE_MODULE_PATH PATH `${zeek_config} --cmake_dir` 143 | 144 | build_type=`${zeek_config} --build_type` 145 | 146 | if [ "$build_type" = "debug" ]; then 147 | append_cache_entry BRO_PLUGIN_ENABLE_DEBUG BOOL true 148 | fi 149 | 150 | if [ -z "$binpac_root" ]; then 151 | append_cache_entry BinPAC_ROOT_DIR PATH `${zeek_config} --binpac_root` 152 | fi 153 | 154 | if [ -z "$broker_root" ]; then 155 | append_cache_entry BROKER_ROOT_DIR PATH `${zeek_config} --broker_root` 156 | fi 157 | 158 | if [ -z "$caf_root" ]; then 159 | append_cache_entry CAF_ROOT_DIR PATH `${zeek_config} --caf_root` 160 | fi 161 | else 162 | if [ ! -e "$zeekdist/zeek-path-dev.in" ]; then 163 | echo "$zeekdist does not appear to be a valid Zeek source tree." 164 | exit 1 165 | fi 166 | 167 | # BRO_DIST is the canonical/historical name used by plugin CMake scripts 168 | # ZEEK_DIST doesn't serve a function at the moment, but set/provided anyway 169 | append_cache_entry BRO_DIST PATH $zeekdist 170 | append_cache_entry ZEEK_DIST PATH $zeekdist 171 | append_cache_entry CMAKE_MODULE_PATH PATH $zeekdist/cmake 172 | fi 173 | 174 | if [ "$installroot" != "default" ]; then 175 | mkdir -p $installroot 176 | append_cache_entry BRO_PLUGIN_INSTALL_ROOT PATH $installroot 177 | fi 178 | 179 | echo "Build Directory : $builddir" 180 | echo "Zeek Source Directory : $zeekdist" 181 | 182 | mkdir -p $builddir 183 | cd $builddir 184 | 185 | "$CMakeCommand" $CMakeCacheEntries .. 186 | 187 | echo "# This is the command used to configure this build" > config.status 188 | echo $command >> config.status 189 | chmod u+x config.status 190 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/configure.plugin: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Hooks to add custom options to the configure script. 4 | # 5 | 6 | plugin_usage() 7 | { 8 | : # Do nothing 9 | # cat < 5 | {% if pagename != "search" %} 6 | {% if display_github %} 7 | {% if github_version == "master" %} 8 | {{ _('Edit on GitHub') }} 9 | {% endif %} 10 | {% elif show_source and has_source and sourcename %} 11 | {{ _('View page source') }} 12 | {% endif %} 13 | {% endif %} 14 | 15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/docs/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "!layout.html" %} 2 | 3 | {% if READTHEDOCS and current_version %} 4 | {% if current_version == "latest" or current_version == "stable" %} 5 | {% set current_version = current_version ~ " (" ~ version ~ ")" %} 6 | {% endif %} 7 | {% endif %} 8 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | import os 4 | import sys 5 | 6 | sys.path.insert(0, os.path.abspath('sphinxcontrib')) 7 | 8 | extensions = ['zeek', 'sphinx.ext.githubpages', 'sphinx.ext.intersphinx'] 9 | 10 | intersphinx_mapping = { 11 | 'zeek': ('https://docs.zeek.org/en/current', None), 12 | } 13 | 14 | # Add any paths that contain templates here, relative to this directory. 15 | templates_path = ['_templates'] 16 | 17 | # General information about the project. 18 | project = u'External Use of Sensitive Protocols' 19 | copyright = u'2020, ESnet' 20 | 21 | version = u"source" 22 | 23 | try: 24 | # Use the actual version if available 25 | with open('../VERSION', 'r') as f: 26 | version = f.readline().strip() 27 | except: 28 | try: 29 | import git 30 | 31 | repo = git.Repo(os.path.abspath('.')) 32 | version = u"git/master" 33 | tag = [str(t) for t in repo.tags if t.commit == repo.head.commit] 34 | 35 | if tag: 36 | version = tag[0] 37 | 38 | except: 39 | pass 40 | 41 | # The full version, including alpha/beta/rc tags. 42 | release = version 43 | 44 | # If true, sectionauthor and moduleauthor directives will be shown in the 45 | # output. They are ignored by default. 46 | show_authors = True 47 | 48 | highlight_language = 'zeek' 49 | 50 | # -- Options for HTML output --------------------------------------------------- 51 | 52 | import sphinx_rtd_theme 53 | html_theme = 'sphinx_rtd_theme' 54 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 55 | 56 | # Theme options are theme-specific and customize the look and feel of a theme 57 | # further. For a list of options available for each theme, see the 58 | # documentation. 59 | html_theme_options = { 60 | 'collapse_navigation': False, 61 | 'display_version': True, 62 | } 63 | 64 | html_static_path = ['_static'] 65 | 66 | def setup(app): 67 | app.add_css_file("theme_overrides.css") 68 | from sphinx.highlighting import lexers 69 | from pygments.lexers.dsls import ZeekLexer 70 | lexers['zeek'] = ZeekLexer() 71 | 72 | # Output file base name for HTML help builder. 73 | htmlhelp_basename = 'zeek-pkg-docs' 74 | 75 | # -- Options for todo plugin -------------------------------------------- 76 | todo_include_todos=True 77 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/docs/generate_docs.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | set -e 4 | 5 | # Our directory structure 6 | dir=$( cd "$( dirname "$0" )" && pwd ) 7 | source_dir=$( cd "$dir"/.. && pwd ) 8 | output_dir=$source_dir/docs 9 | build_dir=$source_dir/build 10 | 11 | conf_file=$build_dir/zeekygen-test.conf 12 | zeek_error_file=$build_dir/zeekygen-test-stderr.txt 13 | 14 | scripts_output_dir=$output_dir/scripts 15 | 16 | mkdir -p "$build_dir" 17 | rm -rf "$scripts_output_dir" 18 | mkdir -p "$scripts_output_dir" 19 | 20 | # Configure Zeek 21 | unset ZEEK_DISABLE_ZEEKYGEN; 22 | 23 | export ZEEK_ALLOW_INIT_ERRORS=1 24 | DEFAULT_ZEEKPATH=$(zeek-config --zeekpath) 25 | export ZEEKPATH="$source_dir/scripts:$DEFAULT_ZEEKPATH" 26 | 27 | # Run Zeek 28 | function run_zeek 29 | { 30 | if ! zeek -b -B zeekygen -X "$conf_file" "$1" >/dev/null 2>"$zeek_error_file" 31 | then 32 | echo "Failed running zeek with zeekygen config file $conf_file" 33 | echo "See stderr in $zeek_error_file" 34 | exit 1 35 | fi 36 | } 37 | 38 | # For each module (Namespace::Module), run it through zeek, and generate package documentation 39 | cd "$source_dir"/scripts 40 | find * -maxdepth 1 -mindepth 1 -type d -print0 | while IFS="" read -r -d $'\0' namespace 41 | do 42 | (cat < "$conf_file" 47 | run_zeek "$namespace" 48 | done 49 | 50 | # Finally, we fix the links from the upstream Zeek docs 51 | find . -name '*.rst' -exec sed -i '' -e 's% ` always gets loaded 41 | The preload mechanism is primarily for loading types, defined in :doc:`types.zeek ` which the rest of the scripts depend on. 42 | 4. :doc:`__load__.zeek ` always gets loaded 43 | 44 | If the plugin is explicitly loaded (:code:`@load {{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}`), the load process continues: 45 | 46 | 5. :doc:`{{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}/__load__.zeek ` then loads the rest of the scripts. 47 | 48 | Reference 49 | ========= 50 | 51 | .. toctree:: 52 | 53 | genindex 54 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/docs/sphinxcontrib/__init__.py: -------------------------------------------------------------------------------- 1 | __import__('pkg_resources').declare_namespace(__name__) 2 | 3 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/docs/sphinxcontrib/zeek.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Zeek domain for Sphinx. 3 | """ 4 | 5 | from sphinx import addnodes 6 | from sphinx.domains import Domain, ObjType, Index 7 | from sphinx.directives import ObjectDescription 8 | from sphinx.errors import SphinxError 9 | from sphinx.locale import _ 10 | from sphinx.roles import XRefRole 11 | from sphinx.util.nodes import make_refnode 12 | from sphinx import version_info 13 | 14 | from sphinx.util import logging 15 | logger = logging.getLogger(__name__) 16 | 17 | from docutils import nodes 18 | from docutils.parsers.rst import Directive, directives 19 | 20 | from optparse import Values 21 | 22 | import zeekygen 23 | 24 | def setup(Sphinx): 25 | Sphinx.add_domain(ZeekDomain) 26 | Sphinx.add_node(see) 27 | Sphinx.add_directive_to_domain('zeek', 'see', SeeDirective) 28 | Sphinx.connect('doctree-resolved', process_see_nodes) 29 | 30 | zeekygen.setup(Sphinx) 31 | 32 | class see(nodes.General, nodes.Element): 33 | refs = [] 34 | 35 | class SeeDirective(Directive): 36 | has_content = True 37 | 38 | def run(self): 39 | n = see('') 40 | n.refs = " ".join(self.content).split() 41 | return [n] 42 | 43 | # Wrapper for creating a tuple for index nodes, staying backwards 44 | # compatible to Sphinx < 1.4: 45 | def make_index_tuple(indextype, indexentry, targetname, targetname2): 46 | if version_info >= (1, 4, 0, '', 0): 47 | return (indextype, indexentry, targetname, targetname2, None) 48 | else: 49 | return (indextype, indexentry, targetname, targetname2) 50 | 51 | def process_see_nodes(app, doctree, fromdocname): 52 | for node in doctree.traverse(see): 53 | content = [] 54 | para = nodes.paragraph() 55 | para += nodes.Text("See also:", "See also:") 56 | for name in node.refs: 57 | join_str = " " 58 | if name != node.refs[0]: 59 | join_str = ", " 60 | link_txt = join_str + name; 61 | 62 | if name not in app.env.domaindata['zeek']['idtypes']: 63 | # Just create the text and issue warning 64 | logger.warning('%s: unknown target for ".. zeek:see:: %s"', fromdocname, name, location=node) 65 | para += nodes.Text(link_txt, link_txt) 66 | else: 67 | # Create a reference 68 | typ = app.env.domaindata['zeek']['idtypes'][name] 69 | todocname = app.env.domaindata['zeek']['objects'][(typ, name)] 70 | 71 | newnode = nodes.reference('', '') 72 | innernode = nodes.literal(_(name), _(name)) 73 | newnode['refdocname'] = todocname 74 | newnode['refuri'] = app.builder.get_relative_uri( 75 | fromdocname, todocname) 76 | newnode['refuri'] += '#' + typ + '-' + name 77 | newnode.append(innernode) 78 | para += nodes.Text(join_str, join_str) 79 | para += newnode 80 | 81 | content.append(para) 82 | node.replace_self(content) 83 | 84 | class ZeekGeneric(ObjectDescription): 85 | def update_type_map(self, idname): 86 | if 'idtypes' not in self.env.domaindata['zeek']: 87 | self.env.domaindata['zeek']['idtypes'] = {} 88 | self.env.domaindata['zeek']['idtypes'][idname] = self.objtype 89 | 90 | def after_content(self): 91 | ObjectDescription.after_content(self) 92 | 93 | if not self.content: 94 | return 95 | 96 | node = nodes.paragraph() 97 | self.state.nested_parse(self.content, 0, node) 98 | 99 | # node 100 | # node[0] 101 | # node[0][0] 102 | # node[0][0][0] 103 | # node[0][0][0][0] Type 104 | # node[0][0][1] 105 | # node[0][0][1][0] 106 | # node[0][0][1][0][0] 107 | # node[0][0][1][0][0][0] 108 | # node[0][0][1][0][0][0][0] function 109 | 110 | sig_done = False 111 | sig = f"function {self.names[0]}" 112 | try: 113 | func_def = node.children[0][0][1][0] 114 | node_type = func_def.children[0][0][0] 115 | if node_type == "function": 116 | for a in func_def.children[1:-2:2]: 117 | sig += a.title().strip() 118 | sig_done = True 119 | except Exception: 120 | pass 121 | 122 | if sig_done: 123 | sig += ")" 124 | print(sig) 125 | if not node.children or node.children[-1].tagname != "paragraph": 126 | logger.error(f"{self.names[0]} is undocumented") 127 | 128 | def add_target_and_index(self, name, sig, signode): 129 | targetname = self.objtype + '-' + name 130 | if targetname not in self.state.document.ids: 131 | signode['names'].append(targetname) 132 | signode['ids'].append(targetname) 133 | signode['first'] = (not self.names) 134 | self.state.document.note_explicit_target(signode) 135 | 136 | objects = self.env.domaindata['zeek']['objects'] 137 | key = (self.objtype, name) 138 | if ( key in objects and self.objtype != "id" and 139 | self.objtype != "type" ): 140 | logger.warning('%s: duplicate description of %s %s, ' % 141 | (self.env.docname, self.objtype, name) + 142 | 'other instance in ' + 143 | self.env.doc2path(objects[key]), 144 | self.lineno) 145 | objects[key] = self.env.docname 146 | self.update_type_map(name) 147 | 148 | indextext = self.get_index_text(self.objtype, name) 149 | if indextext: 150 | self.indexnode['entries'].append(make_index_tuple('single', 151 | indextext, targetname, 152 | targetname)) 153 | 154 | def get_index_text(self, objectname, name): 155 | return _('%s (%s)') % (name, self.objtype) 156 | 157 | def handle_signature(self, sig, signode): 158 | signode += addnodes.desc_name("", sig) 159 | return sig 160 | 161 | class ZeekNamespace(ZeekGeneric): 162 | def add_target_and_index(self, name, sig, signode): 163 | targetname = self.objtype + '-' + name 164 | if targetname not in self.state.document.ids: 165 | signode['names'].append(targetname) 166 | signode['ids'].append(targetname) 167 | signode['first'] = (not self.names) 168 | self.state.document.note_explicit_target(signode) 169 | 170 | objects = self.env.domaindata['zeek']['objects'] 171 | key = (self.objtype, name) 172 | objects[key] = self.env.docname 173 | self.update_type_map(name) 174 | 175 | indextext = self.get_index_text(self.objtype, name) 176 | self.indexnode['entries'].append(make_index_tuple('single', indextext, 177 | targetname, targetname)) 178 | self.indexnode['entries'].append(make_index_tuple('single', 179 | "namespaces; %s" % (sig), 180 | targetname, targetname)) 181 | 182 | def get_index_text(self, objectname, name): 183 | return _('%s (namespace); %s') % (name, self.env.docname) 184 | 185 | def handle_signature(self, sig, signode): 186 | signode += addnodes.desc_name("", sig) 187 | return sig 188 | 189 | class ZeekEnum(ZeekGeneric): 190 | def add_target_and_index(self, name, sig, signode): 191 | targetname = self.objtype + '-' + name 192 | if targetname not in self.state.document.ids: 193 | signode['names'].append(targetname) 194 | signode['ids'].append(targetname) 195 | signode['first'] = (not self.names) 196 | self.state.document.note_explicit_target(signode) 197 | 198 | objects = self.env.domaindata['zeek']['objects'] 199 | key = (self.objtype, name) 200 | objects[key] = self.env.docname 201 | self.update_type_map(name) 202 | 203 | indextext = self.get_index_text(self.objtype, name) 204 | #self.indexnode['entries'].append(make_index_tuple('single', indextext, 205 | # targetname, targetname)) 206 | m = sig.split() 207 | 208 | if len(m) < 2: 209 | logger.warning("%s: zeek:enum directive missing argument(s)", self.env.docname) 210 | return 211 | 212 | if m[1] == "Notice::Type": 213 | if 'notices' not in self.env.domaindata['zeek']: 214 | self.env.domaindata['zeek']['notices'] = [] 215 | self.env.domaindata['zeek']['notices'].append( 216 | (m[0], self.env.docname, targetname)) 217 | 218 | self.indexnode['entries'].append(make_index_tuple('single', 219 | "%s (enum values); %s" % (m[1], m[0]), 220 | targetname, targetname)) 221 | 222 | def handle_signature(self, sig, signode): 223 | m = sig.split() 224 | name = m[0] 225 | signode += addnodes.desc_name("", name) 226 | return name 227 | 228 | class ZeekIdentifier(ZeekGeneric): 229 | def get_index_text(self, objectname, name): 230 | return name 231 | 232 | class ZeekKeyword(ZeekGeneric): 233 | def get_index_text(self, objectname, name): 234 | return name 235 | 236 | class ZeekAttribute(ZeekGeneric): 237 | def get_index_text(self, objectname, name): 238 | return _('%s (attribute)') % (name) 239 | 240 | class ZeekNotices(Index): 241 | """ 242 | Index subclass to provide the Zeek notices index. 243 | """ 244 | 245 | name = 'noticeindex' 246 | localname = _('Zeek Notice Index') 247 | shortname = _('notices') 248 | 249 | def generate(self, docnames=None): 250 | content = {} 251 | 252 | if 'notices' not in self.domain.env.domaindata['zeek']: 253 | return content, False 254 | 255 | for n in self.domain.env.domaindata['zeek']['notices']: 256 | modname = n[0].split("::")[0] 257 | entries = content.setdefault(modname, []) 258 | entries.append([n[0], 0, n[1], n[2], '', '', '']) 259 | 260 | content = sorted(content.items()) 261 | 262 | return content, False 263 | 264 | class ZeekDomain(Domain): 265 | """Zeek domain.""" 266 | name = 'zeek' 267 | label = 'Zeek' 268 | 269 | object_types = { 270 | 'type': ObjType(_('type'), 'type'), 271 | 'namespace': ObjType(_('namespace'), 'namespace'), 272 | 'id': ObjType(_('id'), 'id'), 273 | 'keyword': ObjType(_('keyword'), 'keyword'), 274 | 'enum': ObjType(_('enum'), 'enum'), 275 | 'attr': ObjType(_('attr'), 'attr'), 276 | } 277 | 278 | directives = { 279 | 'type': ZeekGeneric, 280 | 'namespace': ZeekNamespace, 281 | 'id': ZeekIdentifier, 282 | 'keyword': ZeekKeyword, 283 | 'enum': ZeekEnum, 284 | 'attr': ZeekAttribute, 285 | } 286 | 287 | roles = { 288 | 'type': XRefRole(), 289 | 'namespace': XRefRole(), 290 | 'id': XRefRole(), 291 | 'keyword': XRefRole(), 292 | 'enum': XRefRole(), 293 | 'attr': XRefRole(), 294 | 'see': XRefRole(), 295 | } 296 | 297 | indices = [ 298 | ZeekNotices, 299 | ] 300 | 301 | initial_data = { 302 | 'objects': {}, # fullname -> docname, objtype 303 | } 304 | 305 | def clear_doc(self, docname): 306 | to_delete = [] 307 | 308 | for (typ, name), doc in self.data['objects'].items(): 309 | if doc == docname: 310 | to_delete.append((typ, name)) 311 | 312 | for (typ, name) in to_delete: 313 | del self.data['objects'][typ, name] 314 | 315 | def resolve_xref(self, env, fromdocname, builder, typ, target, node, 316 | contnode): 317 | objects = self.data['objects'] 318 | if typ == "see": 319 | if target not in self.data['idtypes']: 320 | logger.warning('%s: unknown target for ":zeek:see:`%s`"', fromdocname, target) 321 | return [] 322 | objtype = self.data['idtypes'][target] 323 | return make_refnode(builder, fromdocname, 324 | objects[objtype, target], 325 | objtype + '-' + target, 326 | contnode, target + ' ' + objtype) 327 | else: 328 | objtypes = self.objtypes_for_role(typ) 329 | for objtype in objtypes: 330 | if (objtype, target) in objects: 331 | return make_refnode(builder, fromdocname, 332 | objects[objtype, target], 333 | objtype + '-' + target, 334 | contnode, target + ' ' + objtype) 335 | 336 | def get_objects(self): 337 | for (typ, name), docname in self.data['objects'].items(): 338 | yield name, name, typ, docname, typ + '-' + name, 1 339 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/docs/sphinxcontrib/zeekygen.py: -------------------------------------------------------------------------------- 1 | from sphinx.errors import SphinxError 2 | 3 | from sphinx.util import logging 4 | logger = logging.getLogger(__name__) 5 | 6 | import os 7 | import shutil 8 | import subprocess 9 | import tempfile 10 | 11 | cwd = os.path.abspath(os.getcwd()) 12 | pkg_script_dir = os.path.abspath(os.path.join(cwd, "..", "scripts")) 13 | rst_script_dir = os.path.join(cwd, "scripts") 14 | rst_output_dir = os.path.join(cwd, "build") 15 | rst_packages_dir = os.path.join(rst_output_dir, "packages") 16 | temp_script_index = os.path.join(rst_script_dir, "tempindex") 17 | script_index = os.path.join(rst_script_dir, "index.rst") 18 | package_index = os.path.join(rst_packages_dir, "package_index.rst") 19 | 20 | try: 21 | shutil.rmtree(rst_script_dir) 22 | except FileNotFoundError: 23 | pass 24 | 25 | os.makedirs(rst_script_dir, exist_ok=True) 26 | os.makedirs(rst_packages_dir, exist_ok=True) 27 | 28 | def run_zeek_on_files(path, filenames): 29 | for f in filenames: 30 | if not f.endswith('.zeek'): 31 | continue 32 | path = path.replace(pkg_script_dir + "/", "") 33 | path = path.replace(pkg_script_dir, "") 34 | run_zeek_script(os.path.join(path, f)) 35 | 36 | def run_zeek_script(script): 37 | """Runs zeek for a single script""" 38 | output = os.path.join(rst_script_dir, script + ".rst") 39 | conf = "\t".join(["script", script, output]) + "\n" 40 | conf += "\t".join(["script_index", script, temp_script_index]) + "\n" 41 | run_zeek(script, conf) 42 | 43 | if not os.path.exists(script_index): 44 | shutil.copy(temp_script_index, script_index) 45 | else: 46 | with open(temp_script_index) as temp_index: 47 | data = temp_index.readlines() 48 | with open(script_index, 'a') as index: 49 | # Chomp the headers 50 | index.writelines(data[3:]) 51 | os.remove(temp_script_index) 52 | 53 | def run_zeek_package(packages): 54 | """Runs zeek for a package""" 55 | 56 | conf = "" 57 | for p in packages: 58 | conf += "\t".join(["package", p, rst_script_dir + "/" + p + "/index.rst"]) + "\n" 59 | run_zeek(" ".join(packages), conf) 60 | 61 | def run_zeek(name, config): 62 | """Runs zeek with a given Zeekygen config""" 63 | 64 | logger.info("Running Zeekygen on %s", name) 65 | with tempfile.TemporaryDirectory() as tmp: 66 | path = os.path.join(tmp, 'zeekygen.cfg') 67 | with open(path, 'w') as f: 68 | f.write(config) 69 | result = subprocess.run(["zeek", "-b", "-X", path, name]) 70 | if result.stderr: 71 | logger.error("Zeek returned: %s", result.stderr) 72 | if result.returncode != 0: 73 | raise SphinxError("Zeekygen failed") 74 | 75 | def run_cmd(cmd): 76 | """Runs a command and returns stdout""" 77 | return subprocess.run(cmd, stdout=subprocess.PIPE).stdout.decode('utf-8') 78 | 79 | def setup(app): 80 | """Zeek will run and generate the ReST files, which Sphinx will then convert to HTML.""" 81 | 82 | if 'ZEEK_DISABLE_ZEEKYGEN' in os.environ: 83 | del os.environ['ZEEK_DISABLE_ZEEKYGEN'] 84 | 85 | os.environ['ZEEK_ALLOW_INIT_ERRORS'] = "1" 86 | zeekpath = pkg_script_dir 87 | zeekpath += ":" + run_cmd(['zeek-config', '--zeekpath']) 88 | os.environ['ZEEKPATH'] = zeekpath 89 | 90 | walker = os.walk(pkg_script_dir) 91 | 92 | packages = [] 93 | 94 | path, namespaces, filenames = next(walker) 95 | run_zeek_on_files(path, filenames) 96 | for n in namespaces: 97 | path, modules, filenames = next(os.walk(os.path.join(pkg_script_dir, n))) 98 | 99 | for m in modules: 100 | packages.append(f"{n}/{m}") 101 | 102 | for path, dirs, filenames in walker: 103 | if filenames: 104 | for special_name in ['__load__.zeek', '__preload__.zeek']: 105 | if special_name in filenames: 106 | filenames.remove(special_name) 107 | filenames.insert(0, special_name) 108 | run_zeek_on_files(path, filenames) 109 | 110 | run_zeek_package(packages) 111 | 112 | # Now we need to fix up some references to the remote docs 113 | for path, dirs, files in os.walk(rst_script_dir): 114 | for rst_file in files: 115 | if not rst_file.endswith('.rst'): 116 | continue 117 | with open(os.path.join(path, rst_file), 'r+') as f: 118 | orig_data = f.read() 119 | data = orig_data.replace(" ") 131 | f.write("\n") 132 | 133 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/scripts/__load__.bro: -------------------------------------------------------------------------------- 1 | ##! For compatibility, loads ./__load__.zeek 2 | @load ./__load__.zeek 3 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/scripts/__load__.zeek: -------------------------------------------------------------------------------- 1 | ##! This is loaded automatically at Zeek startup once the plugin gets activated 2 | ##! and its BiF elements have become available. 3 | # 4 | ##! File load order, always happens 5 | ##! 1. Zeek startup 6 | ##! 2. Plugin activation 7 | ##! 3. __preload__.zeek always gets loaded 8 | ##! 4. __load__.zeek always gets loaded <-- YOU ARE HERE 9 | ##! 10 | ##! ONLY IF the plugin gets loaded via `@load {{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}`, this continues: 11 | ##! 5. @load {{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}/__load__.zeek 12 | ##! 13 | # Include code here that should always execute unconditionally when your plugin gets activated. 14 | # 15 | # Note that often you may want your plugin's accompanying scripts not here, but 16 | # in scripts/{{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}/__load__.zeek. 17 | # That's processed only on explicit `@load {{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}`. 18 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/scripts/__preload__.zeek: -------------------------------------------------------------------------------- 1 | ##! This is loaded automatically at Zeek startup once the plugin gets activated, 2 | ##! but before any of the BiFs that the plugin defines become available. 3 | ##! 4 | ##! File load order, always happens 5 | ##! 1. Zeek startup 6 | ##! 2. Plugin activation 7 | ##! 3. __preload__.zeek always gets loaded <-- YOU ARE HERE 8 | ##! 4. __load__.zeek always gets loaded 9 | ##! 10 | ##! ONLY IF the plugin gets loaded via `@load {{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}`, this continues: 11 | ##! 5. @load {{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}/__load__.zeek 12 | ##! 13 | ##! This is primarily for defining types that BiFs already depend on. If you 14 | ##! need to do any other unconditional initialization, that should go into 15 | ##! __load__.zeek instead. 16 | 17 | @load ./types 18 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/scripts/types.zeek: -------------------------------------------------------------------------------- 1 | ##! Types used by the {{ cookiecutter.project_slug }} plugin. 2 | 3 | module {{ cookiecutter.project_namespace }}; 4 | 5 | # export { 6 | # 7 | # } 8 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/scripts/{{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}/README: -------------------------------------------------------------------------------- 1 | ../../../README.rst -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/scripts/{{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}/__load__.bro: -------------------------------------------------------------------------------- 1 | ##! For compatibility, loads ./__load__.zeek 2 | @load ./__load__.zeek 3 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/scripts/{{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}/__load__.zeek: -------------------------------------------------------------------------------- 1 | ##! This script is auto-loaded by Zeek when loading the directory. 2 | ##! 3 | ##! Best practice is to load all other .zeek files in here, in the right order if needed. 4 | 5 | # Generated by binpac_quickstart 6 | 7 | @load ./main 8 | @load-sigs ./dpd.sig 9 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/scripts/{{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}/dpd.sig: -------------------------------------------------------------------------------- 1 | {% set tcp = cookiecutter.L4_protocol == "TCP" -%} 2 | {% set udp = cookiecutter.L4_protocol == "UDP" -%} 3 | # Generated by binpac_quickstart 4 | 5 | signature dpd_{{ cookiecutter.protocol_name|lower }} { 6 | {% if tcp %} 7 | ip-proto == tcp 8 | {% elif udp %} 9 | ip-proto == udp 10 | {% endif %} 11 | 12 | # ## TODO: Define the payload. When Bro sees this regex, on 13 | # ## any port, it will enable your analyzer on that 14 | # ## connection. 15 | # ## payload /^{{ cookiecutter.protocol_name|upper }}/ 16 | 17 | enable "{{ cookiecutter.protocol_name|lower }}" 18 | } 19 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/scripts/{{ cookiecutter.project_namespace }}/{{ cookiecutter.project_slug }}/main.zeek: -------------------------------------------------------------------------------- 1 | {% set tcp = cookiecutter.L4_protocol == "TCP" -%} 2 | {% set udp = cookiecutter.L4_protocol == "UDP" -%} 3 | ##! Implements base functionality for {{ cookiecutter.protocol_name }} analysis. 4 | ##! Generates the {{ cookiecutter.protocol_name|title }}.log file. 5 | 6 | # Generated by binpac_quickstart 7 | 8 | module {{ cookiecutter.protocol_name|title }}; 9 | 10 | export { 11 | redef enum Log::ID += { LOG }; 12 | 13 | type Info: record { 14 | ## Timestamp for when the event happened. 15 | ts: time &log; 16 | ## Unique ID for the connection. 17 | uid: string &log; 18 | ## The connection's 4-tuple of endpoint addresses/ports. 19 | id: conn_id &log; 20 | 21 | # ## TODO: Add other fields here that you'd like to log. 22 | }; 23 | 24 | ## Event that can be handled to access the {{ cookiecutter.protocol_name }} record as it is sent on 25 | ## to the loggin framework. 26 | global log_{{ cookiecutter.protocol_name|lower }}: event(rec: Info); 27 | } 28 | 29 | # TODO: The recommended method to do dynamic protocol detection 30 | # (DPD) is with the signatures in dpd.sig. If you can't come up 31 | # with any signatures, then you can do port-based detection by 32 | # uncommenting the following and specifying the port(s): 33 | {% if tcp %} 34 | # const ports = { 1234/tcp, 5678/tcp }; 35 | {% elif udp %} 36 | # const ports = { 1234/udp, 5678/udp }; 37 | {% endif %} 38 | 39 | # redef likely_server_ports += { ports }; 40 | 41 | event zeek_init() &priority=5 42 | { 43 | Log::create_stream({{ cookiecutter.protocol_name|title }}::LOG, [$columns=Info, $ev=log_{{ cookiecutter.protocol_name|lower }}, $path="{{ cookiecutter.protocol_name|lower }}"]); 44 | 45 | # TODO: If you're using port-based DPD, uncomment this. 46 | # Analyzer::register_for_ports(Analyzer::ANALYZER_{{ cookiecutter.protocol_name|upper }}, ports); 47 | } 48 | 49 | event {{ cookiecutter.protocol_name|lower }}_event(c: connection) 50 | { 51 | local info: Info; 52 | info$ts = network_time(); 53 | info$uid = c$uid; 54 | info$id = c$id; 55 | 56 | Log::write({{ cookiecutter.protocol_name|title }}::LOG, info); 57 | } 58 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/src/.clang-format: -------------------------------------------------------------------------------- 1 | # Clang-format configuration for Zeek. 2 | 3 | Language: Cpp 4 | Standard: c++17 5 | 6 | BreakBeforeBraces: Whitesmiths 7 | 8 | BraceWrapping: 9 | AfterCaseLabel: true 10 | AfterClass: true 11 | AfterControlStatement: Always 12 | AfterEnum: true 13 | AfterFunction: true 14 | AfterNamespace: true 15 | AfterStruct: true 16 | AfterExternBlock: false 17 | BeforeCatch: true 18 | BeforeElse: true 19 | IndentBraces: true 20 | SplitEmptyFunction: false 21 | SplitEmptyRecord: false 22 | 23 | AccessModifierOffset: -4 24 | AlignAfterOpenBracket: true 25 | AllowShortBlocksOnASingleLine: Empty 26 | AllowShortFunctionsOnASingleLine: Inline 27 | AllowShortIfStatementsOnASingleLine: false 28 | AllowShortLambdasOnASingleLine: Empty 29 | AllowShortLoopsOnASingleLine: false 30 | BreakConstructorInitializers: BeforeColon 31 | BreakInheritanceList: BeforeColon 32 | ColumnLimit: 0 33 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 34 | FixNamespaceComments: false 35 | IndentCaseLabels: true 36 | IndentPPDirectives: None 37 | IndentWidth: 4 38 | NamespaceIndentation: None 39 | PointerAlignment: Left 40 | SpaceAfterLogicalNot: true 41 | SpaceBeforeAssignmentOperators: true 42 | SpaceBeforeCpp11BracedList: true 43 | SpaceBeforeCtorInitializerColon: true 44 | SpaceBeforeInheritanceColon: true 45 | SpaceBeforeParens: ControlStatements 46 | SpaceBeforeRangeBasedForLoopColon: true 47 | SpaceBeforeSquareBrackets: false 48 | SpaceInEmptyBlock: true 49 | SpaceInEmptyParentheses: false 50 | SpacesInAngles: false 51 | SpacesInConditionalStatement: true 52 | SpacesInContainerLiterals: false 53 | TabWidth: 4 54 | 55 | # With clang-format 11, we can get "AlignWithSpaces" here, but for 56 | # now this will have to do. 57 | UseTab: Always -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/src/Plugin.cc: -------------------------------------------------------------------------------- 1 | // Generated by binpac_quickstart 2 | 3 | #include "Plugin.h" 4 | #include "analyzer/Component.h" 5 | 6 | #include "{{ cookiecutter.protocol_name }}.h" 7 | 8 | namespace plugin { namespace {{ cookiecutter.project_namespace }}_{{ cookiecutter.protocol_name }} { Plugin plugin; } } 9 | 10 | using namespace plugin::{{ cookiecutter.project_namespace }}_{{ cookiecutter.protocol_name }}; 11 | 12 | zeek::plugin::Configuration Plugin::Configure() 13 | { 14 | AddComponent(new ::zeek::analyzer::Component("{{ cookiecutter.protocol_name }}", 15 | ::analyzer::{{ cookiecutter.project_namespace }}_{{ cookiecutter.protocol_name }}::{{ cookiecutter.protocol_name }}_Analyzer::InstantiateAnalyzer)); 16 | 17 | zeek::plugin::Configuration config; 18 | 19 | config.name = "{{ cookiecutter.project_namespace }}::{{ cookiecutter.protocol_name }}"; 20 | config.description = "{{ cookiecutter.project_name }}"; 21 | 22 | config.version.major = 0; 23 | config.version.minor = 1; 24 | config.version.patch = 0; 25 | 26 | return config; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/src/Plugin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace plugin { 6 | namespace {{ cookiecutter.project_namespace }}_{{ cookiecutter.protocol_name }} { 7 | 8 | class Plugin : public zeek::plugin::Plugin 9 | { 10 | protected: 11 | // Overridden from zeek::plugin::Plugin. 12 | zeek::plugin::Configuration Configure() override; 13 | }; 14 | 15 | extern Plugin plugin; 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/src/events.bif: -------------------------------------------------------------------------------- 1 | # Generated by binpac_quickstart 2 | 3 | # In this file, you'll define the events that your analyzer will 4 | # generate. A sample event is included. 5 | 6 | # ## TODO: Edit the sample event, and add more events. 7 | 8 | ## Generated for {{ cookiecutter.protocol_name }} connections 9 | ## 10 | ## See `Google `__ for more information about {{ cookiecutter.protocol_name }} 11 | ## 12 | ## c: The connection 13 | ## 14 | event {{ cookiecutter.protocol_name|lower }}_event%(c: connection%); 15 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/src/{{ cookiecutter.protocol_name }}.cc: -------------------------------------------------------------------------------- 1 | {% set tcp = cookiecutter.L4_protocol == "TCP" -%} 2 | {% set udp = cookiecutter.L4_protocol == "UDP" -%} 3 | // Generated by binpac_quickstart 4 | 5 | #include "{{ cookiecutter.protocol_name }}.h" 6 | {% if tcp %} 7 | #include "analyzer/protocol/tcp/TCP_Reassembler.h" 8 | {% endif %} 9 | #include "Reporter.h" 10 | 11 | #include "events.bif.h" 12 | 13 | using namespace analyzer::{{ cookiecutter.project_namespace }}_{{ cookiecutter.protocol_name }}; 14 | 15 | {{ cookiecutter.protocol_name }}_Analyzer::{{ cookiecutter.protocol_name }}_Analyzer(zeek::Connection* c) 16 | {% if tcp %} 17 | : tcp::TCP_ApplicationAnalyzer("{{ cookiecutter.protocol_name }}", c) 18 | {% elif udp %} 19 | : analyzer::Analyzer("{{ cookiecutter.protocol_name }}", c) 20 | {% endif %} 21 | { 22 | interp = new binpac::{{ cookiecutter.protocol_name }}::{{ cookiecutter.protocol_name }}_Conn(this); 23 | {% if tcp %} 24 | had_gap = false; 25 | {% endif %} 26 | } 27 | 28 | {{ cookiecutter.protocol_name }}_Analyzer::~{{ cookiecutter.protocol_name }}_Analyzer() 29 | { 30 | delete interp; 31 | } 32 | 33 | void {{ cookiecutter.protocol_name }}_Analyzer::Done() 34 | { 35 | {% if tcp %} 36 | tcp::TCP_ApplicationAnalyzer::Done(); 37 | 38 | interp->FlowEOF(true); 39 | interp->FlowEOF(false); 40 | {% elif udp %} 41 | Analyzer::Done(); 42 | {% endif %} 43 | } 44 | {% if tcp %} 45 | void {{ cookiecutter.protocol_name }}_Analyzer::EndpointEOF(bool is_orig) 46 | { 47 | tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig); 48 | interp->FlowEOF(is_orig); 49 | } 50 | 51 | void {{ cookiecutter.protocol_name }}_Analyzer::DeliverStream(int len, const u_char* data, bool orig) 52 | { 53 | tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig); 54 | 55 | assert(TCP()); 56 | if ( TCP()->IsPartial() ) 57 | return; 58 | 59 | if ( had_gap ) 60 | // If only one side had a content gap, we could still try to 61 | // deliver data to the other side if the script layer can handle this. 62 | return; 63 | 64 | try 65 | { 66 | interp->NewData(orig, data, data + len); 67 | } 68 | catch ( const binpac::Exception& e ) 69 | { 70 | ProtocolViolation(fmt("Binpac exception: %s", e.c_msg())); 71 | } 72 | } 73 | 74 | void {{ cookiecutter.protocol_name }}_Analyzer::Undelivered(uint64_t seq, int len, bool orig) 75 | { 76 | tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig); 77 | had_gap = true; 78 | interp->NewGap(orig, len); 79 | } 80 | {% elif udp %} 81 | void {{ cookiecutter.protocol_name }}_Analyzer::DeliverPacket(int len, const u_char* data, 82 | bool orig, uint64_t seq, const IP_Hdr* ip, int caplen) 83 | { 84 | Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen); 85 | 86 | try 87 | { 88 | interp->NewData(orig, data, data + len); 89 | } 90 | catch ( const binpac::Exception& e ) 91 | { 92 | ProtocolViolation(fmt("Binpac exception: %s", e.c_msg())); 93 | } 94 | } 95 | {% endif %} 96 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/src/{{ cookiecutter.protocol_name }}.h: -------------------------------------------------------------------------------- 1 | {% set tcp = cookiecutter.L4_protocol == "TCP" -%} 2 | {% set udp = cookiecutter.L4_protocol == "UDP" -%} 3 | // Generated by binpac_quickstart 4 | 5 | #pragma once 6 | 7 | #include "events.bif.h" 8 | 9 | {% if tcp %} 10 | #include "analyzer/protocol/tcp/TCP.h" 11 | {% elif udp %} 12 | #include "analyzer/protocol/udp/UDP.h" 13 | {% endif %} 14 | #include "{{ cookiecutter.protocol_name|lower }}_pac.h" 15 | 16 | namespace analyzer { namespace {{ cookiecutter.project_namespace }}_{{ cookiecutter.protocol_name }} { 17 | 18 | class {{ cookiecutter.protocol_name }}_Analyzer{% if tcp -%}: public tcp::TCP_ApplicationAnalyzer{% elif udp -%}: public ::zeek::analyzer::Analyzer{% endif %} 19 | { 20 | public: 21 | {{ cookiecutter.protocol_name }}_Analyzer(zeek::Connection* conn); 22 | virtual ~{{ cookiecutter.protocol_name }}_Analyzer(); 23 | 24 | // Overriden from Analyzer. 25 | virtual void Done(); 26 | {% if tcp %} 27 | virtual void DeliverStream(int len, const u_char* data, bool orig); 28 | virtual void Undelivered(uint64_t seq, int len, bool orig); 29 | 30 | // Overriden from tcp::TCP_ApplicationAnalyzer. 31 | virtual void EndpointEOF(bool is_orig); 32 | {% elif udp %} 33 | virtual void DeliverPacket(int len, const u_char* data, bool orig, 34 | uint64_t seq, const IP_Hdr* ip, int caplen); 35 | {% endif %} 36 | 37 | static ::zeek::analyzer::Analyzer* InstantiateAnalyzer(zeek::Connection* conn) 38 | { return new {{ cookiecutter.protocol_name }}_Analyzer(conn); } 39 | 40 | protected: 41 | binpac::{{ cookiecutter.protocol_name }}::{{ cookiecutter.protocol_name }}_Conn* interp; 42 | {% if tcp -%} 43 | bool had_gap; 44 | {% endif -%} 45 | }; 46 | 47 | } } // namespace analyzer::* 48 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/src/{{ cookiecutter.protocol_name|lower }}-analyzer.pac: -------------------------------------------------------------------------------- 1 | # Generated by binpac_quickstart 2 | 3 | refine flow {{ cookiecutter.protocol_name }}_Flow += { 4 | function proc_{{ cookiecutter.protocol_name|lower }}_message(msg: {{ cookiecutter.protocol_name }}_PDU): bool 5 | %{ 6 | zeek::BifEvent::enqueue_{{ cookiecutter.protocol_name|lower }}_event(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn()); 7 | return true; 8 | %} 9 | }; 10 | 11 | refine typeattr {{ cookiecutter.protocol_name }}_PDU += &let { 12 | proc: bool = $context.flow.proc_{{ cookiecutter.protocol_name|lower }}_message(this); 13 | }; 14 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/src/{{ cookiecutter.protocol_name|lower }}-protocol.pac: -------------------------------------------------------------------------------- 1 | # Generated by binpac_quickstart 2 | 3 | # ## TODO: Add your protocol structures in here. 4 | # ## some examples: 5 | 6 | # Types are your basic building blocks. 7 | # There are some builtins, or you can define your own. 8 | # Here's a definition for a regular expression: 9 | # type {{ cookiecutter.protocol_name|upper }}_WHITESPACE = RE/[ \t]*/; 10 | 11 | # A record is a collection of types. 12 | # Here's one with the built-in types 13 | # type example = record { 14 | # 15 | # }; 16 | 17 | type {{ cookiecutter.protocol_name }}_PDU(is_orig: bool) = record { 18 | data: bytestring &restofdata; 19 | } &byteorder=bigendian; 20 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/src/{{ cookiecutter.protocol_name|lower }}.pac: -------------------------------------------------------------------------------- 1 | # Generated by binpac_quickstart 2 | 3 | # {{ cookiecutter.project_short_description }} 4 | # - {{ cookiecutter.protocol_name|lower }}-protocol.pac: describes the {{ cookiecutter.protocol_name }} protocol messages 5 | # - {{ cookiecutter.protocol_name|lower }}-analyzer.pac: describes the {{ cookiecutter.protocol_name }} analyzer code 6 | 7 | %include binpac.pac 8 | %include bro.pac 9 | 10 | %extern{ 11 | #include "events.bif.h" 12 | %} 13 | 14 | analyzer {{ cookiecutter.protocol_name }} withcontext { 15 | connection: {{ cookiecutter.protocol_name }}_Conn; 16 | flow: {{ cookiecutter.protocol_name }}_Flow; 17 | }; 18 | 19 | # Our connection consists of two flows, one in each direction. 20 | connection {{ cookiecutter.protocol_name }}_Conn(bro_analyzer: BroAnalyzer) { 21 | upflow = {{ cookiecutter.protocol_name }}_Flow(true); 22 | downflow = {{ cookiecutter.protocol_name }}_Flow(false); 23 | }; 24 | 25 | %include {{ cookiecutter.protocol_name|lower }}-protocol.pac 26 | 27 | # Now we define the flow: 28 | flow {{ cookiecutter.protocol_name }}_Flow(is_orig: bool) { 29 | 30 | # ## TODO: Determine if you want flowunit or datagram parsing: 31 | 32 | # Using flowunit will cause the anlayzer to buffer incremental input. 33 | # This is needed for &oneline and &length. If you don't need this, you'll 34 | # get better performance with datagram. 35 | 36 | # flowunit = {{ cookiecutter.protocol_name }}_PDU(is_orig) withcontext(connection, this); 37 | datagram = {{ cookiecutter.protocol_name }}_PDU(is_orig) withcontext(connection, this); 38 | 39 | }; 40 | 41 | %include {{ cookiecutter.protocol_name|lower }}-analyzer.pac 42 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/tests/.gitignore: -------------------------------------------------------------------------------- 1 | .btest.failed.dat 2 | .tmp 3 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/tests/Makefile: -------------------------------------------------------------------------------- 1 | DIAG=diag.log 2 | JUNIT=.btest_output.xml 3 | 4 | all: cleanup btest-verbose coverage 5 | 6 | # Showing all tests. 7 | btest-verbose: 8 | @btest -j -f $(DIAG) -x $(JUNIT) || cat $(DIAG) 9 | 10 | brief: cleanup btest-brief 11 | 12 | # Brief output showing only failed tests. 13 | btest-brief: 14 | @btest -j -b -f $(DIAG) -x $(JUNIT) || cat $(DIAG) 15 | 16 | coverage: 17 | @./Scripts/coverage 18 | 19 | cleanup: 20 | @rm -f $(DIAG) $(JUNIT) 21 | @rm -f .tmp/script-coverage/* 22 | @find ../../ -name "*.gcov" -exec rm {} \; 23 | 24 | distclean: cleanup 25 | @rm -rf .btest.failed.dat \ 26 | .tmp/ 27 | 28 | .PHONY: all btest-verbose brief btest-brief cleanup 29 | 30 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/tests/Scripts/combine-profiler-files: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Adds up execution counts in .tmp/script-coverage/XXXXXX 4 | 5 | import glob 6 | from pathlib import Path, PurePath 7 | 8 | # We are pkg/tests/Scripts/coverage-to-gcov 9 | # .parent is pkg/tests/Scripts 10 | # .parent.parent is pkg/tests 11 | # .parent.parent.parent is pkg 12 | test_dir = Path(__file__).resolve().parent.parent 13 | 14 | def find_files(): 15 | profiler_glob = str(PurePath.joinpath(test_dir, ".tmp", "script-coverage", "btest_profile-*")) 16 | return glob.glob(profiler_glob) 17 | 18 | 19 | def is_package_file(path): 20 | file_path = Path(path).resolve() 21 | 22 | # We skip files from the btests themselves 23 | if '.tmp' in str(file_path) or 'tests/Scripts' in str(file_path): 24 | return False 25 | 26 | return str(file_path.parent).startswith(str(test_dir.parent)) 27 | 28 | 29 | def parse_line(line): 30 | count, location, source = line.split('\t', 2) 31 | 32 | loc_file, loc_line = location.split(', ', 1) 33 | 34 | if 'lines ' in loc_line: 35 | start, end = loc_line.split('lines ', 1)[1].split('-') 36 | lines = range(int(start), int(end) + 1) 37 | else: 38 | lines = [int(loc_line.split('line ', 1)[1])] 39 | 40 | return (int(count.strip()), loc_file, lines) 41 | 42 | 43 | def main(): 44 | line_counts = {} 45 | 46 | for f in find_files(): 47 | with open(f, 'r') as profile_file: 48 | for l in profile_file.readlines(): 49 | try: 50 | count, loc_file, lines = parse_line(l) 51 | except ValueError: 52 | continue 53 | 54 | if not is_package_file(loc_file): 55 | continue 56 | 57 | if loc_file not in line_counts: 58 | line_counts[loc_file] = {} 59 | for line in lines: 60 | if line not in line_counts[loc_file]: 61 | line_counts[loc_file][line] = 0 62 | line_counts[loc_file][line] += count 63 | 64 | 65 | with open(PurePath.joinpath(test_dir, ".tmp", "script-coverage", "coverage.stats"), 'w') as f: 66 | for filename, counts in line_counts.items(): 67 | for line, count in counts.items(): 68 | f.write(f"{count}\t{line}\t{filename}\n") 69 | 70 | if __name__ == "__main__": 71 | main() 72 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/tests/Scripts/coverage: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 6 | 7 | "$SCRIPT_DIR/combine-profiler-files" 8 | 9 | if [ ! -s "$SCRIPT_DIR/../.tmp/script-coverage/coverage.stats" ] 10 | then 11 | echo "No coverage statistics found, skipping coverage analysis." 12 | exit 13 | fi 14 | 15 | # Convert the Brofiler stats to gcov 16 | "$SCRIPT_DIR/coverage-to-gcov" 17 | 18 | if [ "$CI" == "true" ] 19 | then 20 | if [ "$TRAVIS" == "true" ] 21 | then 22 | # Send to coveralls.io 23 | cd "$SCRIPT_DIR/../.."; coveralls -i scripts --gcov-options '\-lp' 24 | elif [ "$GITLAB_CI" == "true" ] 25 | then 26 | cp "$SCRIPT_DIR/../.btest_output.xml" "$CI_PROJECT_DIR/.btest_output.xml" 27 | cd "$SCRIPT_DIR/../.."; gcovr -g -r scripts --xml -o "$CI_PROJECT_DIR/.coverage.cobertura.xml" 28 | fi 29 | fi 30 | -------------------------------------------------------------------------------- /{{ cookiecutter.project_slug }}/tests/Scripts/coverage-calc: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # This script aggregates many files containing Zeek script coverage information 4 | # into a single file and reports the overall coverage information. Usage: 5 | # 6 | # coverage-calc