├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE ├── .gitignore ├── .ruby-version ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE.TXT ├── README.md ├── VERSION.txt ├── attachments └── test.txt ├── config.json ├── config.json.defaults ├── db └── blank ├── docker-compose.yml ├── docker ├── dev.dockerfile └── docker.sh ├── docs └── docker.md ├── helpers ├── asciidoc_exporter.rb ├── helper.rb ├── image.rb ├── odle_import.rb ├── plugin_listener.rb ├── plugin_notifier.rb ├── sinatra_ssl.rb ├── vuln_importer.rb └── xslt_generation.rb ├── log └── blank ├── model └── master.rb ├── public ├── bootstrap-4.3.1 │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map ├── codemirror │ ├── .editorconfig │ ├── .gitattributes │ ├── .npmignore │ ├── .travis.yml │ ├── AUTHORS │ ├── CHANGELOG.md │ ├── CONTRIBUTING.md │ ├── LICENSE │ ├── README.md │ ├── addon │ │ ├── comment │ │ │ ├── comment.js │ │ │ └── continuecomment.js │ │ ├── dialog │ │ │ ├── dialog.css │ │ │ └── dialog.js │ │ ├── display │ │ │ ├── autorefresh.js │ │ │ ├── fullscreen.css │ │ │ ├── fullscreen.js │ │ │ ├── panel.js │ │ │ ├── placeholder.js │ │ │ └── rulers.js │ │ ├── edit │ │ │ ├── closebrackets.js │ │ │ ├── closetag.js │ │ │ ├── continuelist.js │ │ │ ├── matchbrackets.js │ │ │ ├── matchtags.js │ │ │ └── trailingspace.js │ │ ├── fold │ │ │ ├── brace-fold.js │ │ │ ├── comment-fold.js │ │ │ ├── foldcode.js │ │ │ ├── foldgutter.css │ │ │ ├── foldgutter.js │ │ │ ├── indent-fold.js │ │ │ ├── markdown-fold.js │ │ │ └── xml-fold.js │ │ ├── hint │ │ │ ├── anyword-hint.js │ │ │ ├── css-hint.js │ │ │ ├── html-hint.js │ │ │ ├── javascript-hint.js │ │ │ ├── show-hint.css │ │ │ ├── show-hint.js │ │ │ ├── sql-hint.js │ │ │ └── xml-hint.js │ │ ├── lint │ │ │ ├── coffeescript-lint.js │ │ │ ├── css-lint.js │ │ │ ├── html-lint.js │ │ │ ├── javascript-lint.js │ │ │ ├── json-lint.js │ │ │ ├── lint.css │ │ │ ├── lint.js │ │ │ └── yaml-lint.js │ │ ├── merge │ │ │ ├── merge.css │ │ │ └── merge.js │ │ ├── mode │ │ │ ├── loadmode.js │ │ │ ├── multiplex.js │ │ │ ├── multiplex_test.js │ │ │ ├── overlay.js │ │ │ └── simple.js │ │ ├── runmode │ │ │ ├── colorize.js │ │ │ ├── runmode-standalone.js │ │ │ ├── runmode.js │ │ │ └── runmode.node.js │ │ ├── scroll │ │ │ ├── annotatescrollbar.js │ │ │ ├── scrollpastend.js │ │ │ ├── simplescrollbars.css │ │ │ └── simplescrollbars.js │ │ ├── search │ │ │ ├── jump-to-line.js │ │ │ ├── match-highlighter.js │ │ │ ├── matchesonscrollbar.css │ │ │ ├── matchesonscrollbar.js │ │ │ ├── search.js │ │ │ └── searchcursor.js │ │ ├── selection │ │ │ ├── active-line.js │ │ │ ├── mark-selection.js │ │ │ └── selection-pointer.js │ │ ├── tern │ │ │ ├── tern.css │ │ │ ├── tern.js │ │ │ └── worker.js │ │ └── wrap │ │ │ └── hardwrap.js │ ├── bin │ │ ├── authors.sh │ │ ├── lint │ │ ├── release │ │ ├── source-highlight │ │ └── upload-release.js │ ├── doc │ │ ├── activebookmark.js │ │ ├── docs.css │ │ ├── internals.html │ │ ├── logo.png │ │ ├── logo.svg │ │ ├── manual.html │ │ ├── realworld.html │ │ ├── releases.html │ │ ├── reporting.html │ │ ├── upgrade_v2.2.html │ │ ├── upgrade_v3.html │ │ ├── upgrade_v4.html │ │ └── yinyang.png │ ├── index.html │ ├── keymap │ │ ├── emacs.js │ │ ├── sublime.js │ │ └── vim.js │ ├── lib │ │ ├── codemirror.css │ │ └── codemirror.js │ ├── mode │ │ ├── Serpico │ │ │ ├── Serpico.css │ │ │ └── Serpico.js │ │ ├── index.html │ │ └── meta.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── codemirror.js │ │ ├── display │ │ │ ├── Display.js │ │ │ ├── focus.js │ │ │ ├── gutters.js │ │ │ ├── highlight_worker.js │ │ │ ├── line_numbers.js │ │ │ ├── mode_state.js │ │ │ ├── operations.js │ │ │ ├── scroll_events.js │ │ │ ├── scrollbars.js │ │ │ ├── scrolling.js │ │ │ ├── selection.js │ │ │ ├── update_display.js │ │ │ ├── update_line.js │ │ │ ├── update_lines.js │ │ │ └── view_tracking.js │ │ ├── edit │ │ │ ├── CodeMirror.js │ │ │ ├── commands.js │ │ │ ├── deleteNearSelection.js │ │ │ ├── drop_events.js │ │ │ ├── fromTextArea.js │ │ │ ├── global_events.js │ │ │ ├── key_events.js │ │ │ ├── legacy.js │ │ │ ├── main.js │ │ │ ├── methods.js │ │ │ ├── mouse_events.js │ │ │ ├── options.js │ │ │ └── utils.js │ │ ├── input │ │ │ ├── ContentEditableInput.js │ │ │ ├── TextareaInput.js │ │ │ ├── indent.js │ │ │ ├── input.js │ │ │ ├── keymap.js │ │ │ ├── keynames.js │ │ │ └── movement.js │ │ ├── line │ │ │ ├── highlight.js │ │ │ ├── line_data.js │ │ │ ├── pos.js │ │ │ ├── saw_special_spans.js │ │ │ ├── spans.js │ │ │ └── utils_line.js │ │ ├── measurement │ │ │ ├── position_measurement.js │ │ │ └── widgets.js │ │ ├── model │ │ │ ├── Doc.js │ │ │ ├── change_measurement.js │ │ │ ├── changes.js │ │ │ ├── chunk.js │ │ │ ├── document_data.js │ │ │ ├── history.js │ │ │ ├── line_widget.js │ │ │ ├── mark_text.js │ │ │ ├── selection.js │ │ │ └── selection_updates.js │ │ ├── modes.js │ │ └── util │ │ │ ├── StringStream.js │ │ │ ├── bidi.js │ │ │ ├── browser.js │ │ │ ├── dom.js │ │ │ ├── event.js │ │ │ ├── feature_detection.js │ │ │ ├── misc.js │ │ │ └── operation_group.js │ └── theme │ │ └── cobalt.css ├── css │ ├── bootstrap-suggest.css │ ├── cvss.css │ ├── docs.css │ ├── main.css │ └── signin.css ├── favicon.ico ├── fontawesome-5.7.2 │ ├── css │ │ ├── all.css │ │ ├── all.min.css │ │ ├── brands.css │ │ ├── brands.min.css │ │ ├── fontawesome.css │ │ ├── fontawesome.min.css │ │ ├── regular.css │ │ ├── regular.min.css │ │ ├── solid.css │ │ ├── solid.min.css │ │ ├── svg-with-js.css │ │ ├── svg-with-js.min.css │ │ ├── v4-shims.css │ │ └── v4-shims.min.css │ └── webfonts │ │ ├── fa-brands-400.eot │ │ ├── fa-brands-400.svg │ │ ├── fa-brands-400.ttf │ │ ├── fa-brands-400.woff │ │ ├── fa-brands-400.woff2 │ │ ├── fa-regular-400.eot │ │ ├── fa-regular-400.svg │ │ ├── fa-regular-400.ttf │ │ ├── fa-regular-400.woff │ │ ├── fa-regular-400.woff2 │ │ ├── fa-solid-900.eot │ │ ├── fa-solid-900.svg │ │ ├── fa-solid-900.ttf │ │ ├── fa-solid-900.woff │ │ └── fa-solid-900.woff2 ├── img │ ├── cvssicons.png │ ├── glyphicons-halflings-white.png │ ├── logo.jpg │ ├── logo.png │ └── logo_1.svg └── js │ ├── bootstrap-suggest.min.js │ ├── cvss.js │ ├── cvss3.js │ ├── cvsscalc20.js │ ├── cvsscalc30.js │ ├── d3.js │ ├── format_shortcut.js │ ├── helpers.js │ ├── jquery-3.3.1.min.js │ └── jquery-migrate-3.0.0.min.js ├── routes ├── admin.rb ├── api.rb ├── basic.rb ├── mapping.rb ├── master.rb └── report.rb ├── scripts ├── Serpico_init_template ├── alert_unapproved_findings.rb ├── create_user.rb ├── docker.sh ├── export_reports.rb ├── export_template_findings.rb ├── first_time.rb ├── give_plugin_access.rb ├── lf.sed ├── make_export.sh ├── manage_users.rb ├── reset_pw.rb ├── serpico.service ├── serpicoInstall.sh └── update_templates.rb ├── serpico.rb ├── server.rb ├── templates ├── CVSS_Template.docx ├── Default CVSS 3 Report.docx ├── Default NIST800 Report.docx ├── Default Status.docx ├── Default Template.docx ├── Serpico - Finding.docx ├── Serpico - GenericRiskScoring.docx ├── Serpico - No DREAD.docx ├── Serpico - Report.docx ├── Serpico - Risk Finding.docx ├── Serpico - Status.docx └── template_findings.json ├── test └── main_test.rb ├── tmp └── tmp.txt └── views ├── add_template.haml ├── add_user.haml ├── add_user_report.haml ├── additional_features.haml ├── admin.haml ├── config.haml ├── create_finding.haml ├── dbhosts.haml ├── dbservices.haml ├── dbvulns.haml ├── edit_template.haml ├── enabled_plugins.haml ├── findings_add.haml ├── findings_edit.haml ├── findings_list.haml ├── footer.haml ├── import_burp.haml ├── import_nessus.haml ├── import_report.haml ├── import_scan_data.haml ├── import_templates.haml ├── index.haml ├── info.haml ├── layout.haml ├── list_attachments.haml ├── list_user.haml ├── msfsettings.haml ├── new_report.haml ├── plugins.haml ├── presentation.haml ├── report_edit.haml ├── reports_list.haml ├── reset.haml ├── restore_attachments.haml ├── template_error.haml ├── template_list.haml ├── template_tree.haml ├── test.haml ├── text_status.haml ├── udo_template_edit.haml ├── upload_attachments.haml ├── user_defined_object_create.haml ├── user_defined_object_edit.haml ├── user_defined_object_manage.haml ├── user_defined_object_templates.haml └── user_defined_variable.haml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: 4 | patreon: 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | # Please fill out the Bug Form or Feature Request Below 2 | 3 | ------------------- 4 | ## Bug 5 | Describe the issue and steps to reproduce 6 | 7 | 1. ... 8 | 2. ... 9 | 10 | 11 | ## Feature Request 12 | 13 | Describe the feature and give an example use case 14 | 15 | ### Example Use Case 16 | 17 | "As a user ..." -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | db/master.db 2 | cert.pem 3 | key.pem 4 | tmp/* 5 | templates/* 6 | log/*.log 7 | attachments/* 8 | config.json 9 | plugins/* 10 | log/ 11 | 12 | # For the Jetbrains squad 13 | .idea 14 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.6.3 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.6.3 2 | MAINTAINER Serpico 3 | 4 | ENV SRP_ROOT /Serpico 5 | WORKDIR $SRP_ROOT 6 | COPY . $SRP_ROOT 7 | COPY ./docker/docker.sh scripts/docker.sh 8 | RUN bundle install 9 | 10 | # Allow DB to be on a shared volume 11 | VOLUME ["$SRP_ROOT/db", "$SRP_ROOT/templates", "$SRP_ROOT/attachments", "$SRP_ROOT/tmp"] 12 | EXPOSE 8443 13 | 14 | CMD bash scripts/docker.sh 15 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby "2.6.3" 4 | 5 | gem 'sinatra' 6 | gem 'haml' 7 | gem 'rubyzip' 8 | gem 'net-ldap', '~> 0.11' 9 | gem 'json' 10 | gem 'nokogiri' 11 | gem 'do_sqlite3', '0.10.17' 12 | gem 'data_mapper', '1.2.0' 13 | gem 'dm-sqlite-adapter', '1.2.0' 14 | gem 'msfrpc-client', '1.1.1' 15 | gem 'odle' 16 | gem 'rack' 17 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2017, Willis Vandevanter 2 | All rights reserved. 3 | 4 | License: BSD-3-clause 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 3. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /VERSION.txt: -------------------------------------------------------------------------------- 1 | 1.3.1.2 2 | -------------------------------------------------------------------------------- /attachments/test.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/attachments/test.txt -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "port":"8443", 3 | "use_ssl":true, 4 | "bind_address":"0.0.0.0", 5 | "ssl_certificate":"./cert.pem", 6 | "ssl_key":"key.pem", 7 | "ldap":"false", 8 | "ldap_domain":"", 9 | "ldap_dc":"", 10 | "dread":false, //DREAD risk scoring not used by default 11 | "cvss":false, 12 | "nessusmap":false, //maps nessus findings to serpico findings 13 | "burpmap":false, //maps burp findings to serpico findings 14 | "vulnmap":false, //maps vuln ids from MSF vuln db 15 | "finding_types": ["Web Application","Business Logic","Network Services", "Best Practice", "Compliance", "Database", "Network Internal", "Router Configuration","Social Engineering", "Physical", "Wireless", "Network Security", "System Security", "Logging and Auditing", "Imported"], 16 | "logo":"/img/logo_1.svg", 17 | "auto_import":false, //Experimental, will automatically create new findings on import 18 | "chart":true, //Enabled or disable support, 19 | "user_defined_variables":[], //set your global UDV's here; ["client_start_date","client_abbreviated_name"], 20 | "threshold":"2", // must be a string; "0","1","2"... 21 | "log_file":"./log/serpico.log" // set as "" output to the console or /var/log/serpico.log 22 | } 23 | -------------------------------------------------------------------------------- /config.json.defaults: -------------------------------------------------------------------------------- 1 | { 2 | "port": "8443", 3 | "use_ssl": true, 4 | "bind_address": "0.0.0.0", 5 | "ssl_certificate": "./cert.pem", 6 | "ssl_key": "key.pem", 7 | "ldap": false, 8 | "ldap_domain": "", 9 | "ldap_dc": "", 10 | "dread": false, 11 | "cvss": false, 12 | "nessusmap": false, 13 | "burpmap": false, 14 | "vulnmap": false, 15 | "finding_types": [ 16 | "Web Application", 17 | "Business Logic", 18 | "Network Services", 19 | "Best Practice", 20 | "Compliance", 21 | "Database", 22 | "Network Internal", 23 | "Router Configuration", 24 | "Social Engineering", 25 | "Physical", 26 | "Wireless", 27 | "Network Security", 28 | "System Security", 29 | "Logging and Auditing", 30 | "Imported" 31 | ], 32 | "finding_states": [ 33 | "Draft", 34 | "Under Review", 35 | "Completed" 36 | ], 37 | "logo": "/img/logo_1.svg", 38 | "auto_import": false, 39 | "chart": true, 40 | "effort":["Quick","Planned","Involved"], 41 | "user_defined_variables": [ 42 | 43 | ], 44 | "report_assessment_types": ["Network Internal","External","Web application","Physical","Social engineering","Configuration audit"], 45 | "findings_assessment_types": ["External", "Internal", "Internal/External", "Wireless", "Web Application", "DoS"], 46 | "threshold": "2", 47 | "log_file": "./log/serpico.log", 48 | "show_exceptions": false, 49 | "cvssv2_scoring_override": false, 50 | "image_align":"center", 51 | "ssl_ciphers":["ECDHE-RSA-AES128-GCM-SHA256","ECDHE-RSA-AES256-GCM-SHA384", 52 | "ECDHE-RSA-AES128-CBC-SHA","ECDHE-RSA-AES256-CBC-SHA", 53 | "AES128-GCM-SHA256","AES256-GCM-SHA384","AES128-SHA256", 54 | "AES256-SHA256","AES128-SHA","AES256-SHA"], 55 | "languages":["English"] 56 | } 57 | -------------------------------------------------------------------------------- /db/blank: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/db/blank -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | serpico: 4 | environment: 5 | - SRP_ADMIN=administrator 6 | - SRP_FINDINGS=yes 7 | build: 8 | dockerfile: docker/dev.dockerfile 9 | context: . 10 | ports: 11 | - "8443:8443" 12 | volumes: 13 | - ./:/Serpico 14 | -------------------------------------------------------------------------------- /docker/dev.dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.6.3 2 | MAINTAINER Serpico 3 | ENV SRP_ROOT /Serpico 4 | WORKDIR $SRP_ROOT 5 | # No volume: It will be mounted by docker-compose. 6 | COPY Gemfile $SRP_ROOT/ 7 | RUN bundle install 8 | EXPOSE 8443 9 | CMD ["bash", "docker/docker.sh"] 10 | -------------------------------------------------------------------------------- /docker/docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # This script is used as the entry point for the docker image. 3 | # It will initialize the database if it isn't already present. 4 | 5 | if [ ! -f "$SRP_ROOT/db/master.db" ]; then 6 | echo "First run detected. Initializing database..." 7 | ruby "$SRP_ROOT/scripts/first_time.rb" 8 | fi 9 | 10 | # CMD ["ruby", "serpico.rb"] 11 | ruby serpico.rb 12 | 13 | -------------------------------------------------------------------------------- /docs/docker.md: -------------------------------------------------------------------------------- 1 | # Running Inside Docker 2 | 3 | The included `Dockerfile` allows to run Serpico inside docker from any system 4 | that supports containers. 5 | 6 | By default, Serpico listens on 8443, you can expose it as `443` if you would 7 | like by using `docker run -p 443:8443 ...` 8 | 9 | The image needs to first be built. 10 | 11 | 1. Build the image 12 | 2. Map the database location in docker-compose or at `docker run` time. 13 | 3. If the database doesn't exist, it will be created with defaults 14 | 15 | ## Creating the image 16 | 17 | This will create a container with the current state of your repository. 18 | The database is not created at this point, and this image is safe for 19 | distribution. 20 | 21 | ``` 22 | docker build -t serpico . 23 | ``` 24 | 25 | The Dockerfile exposes a `VOLUME` at `/Serpico/db` to allow mounting an 26 | external database through `docker-compose` or `docker run -v`. 27 | 28 | 29 | ## Running with `docker run` 30 | 31 | ``` 32 | # Create a new container called "serpico" and run it in the foreground 33 | docker run --name serpico -p 8443:8443 -v"$(pwd)":/Serpico/db -it serpico 34 | 35 | # Stop the container when you no longer need it 36 | docker stop serpico 37 | 38 | # Start it again when you need it. It will keep its state. 39 | docker start serpico 40 | ``` 41 | 42 | This will store the database locally at `$PWD/master.db` Please note that the 43 | path to the database on the host [must be absolute][1]. 44 | 45 | [1]: https://docs.docker.com/engine/reference/run/#volume-shared-filesystems 46 | 47 | ## docker-compose 48 | 49 | The `docker-compose.yml` in the repository is aimed at development use. It will 50 | provision the ruby environment inside the container and mount the repository as 51 | the docker application, allowing for reloading the source code by simply 52 | restarting the container. The dockerfile `docker/dev.dockerfile` is used by 53 | compose. 54 | 55 | ## Caveats 56 | 57 | This is a work in progress, so a few things are currently not supported. 58 | 59 | - Running a new container with an existing `master.db` will not work because 60 | `first_time.rb` will not run, and there won't be any certificates for SSL. 61 | - `config.json` is not exposed to the host so customization requires rebuilding 62 | the image or accessing it with `docker exec bash`. 63 | - `docker-compose up` will not automatically reload the backend when `.rb` 64 | files are changed. This is a possible improvement. 65 | 66 | -------------------------------------------------------------------------------- /helpers/asciidoc_exporter.rb: -------------------------------------------------------------------------------- 1 | def parse_input(text) 2 | if text.nil? || (text == '') 3 | return " None \n" 4 | else 5 | # replace paragragh 6 | text = text.gsub('', '').gsub('', "\n\n") 7 | 8 | # replace h4 9 | text = text.gsub('

', '==== ').gsub('

', '') 10 | 11 | # not sure asciidoc equivalent for indent 12 | text = text.gsub('', '').gsub('', '') 13 | 14 | # replace italics 15 | text = text.gsub('', '_').gsub('', '_') 16 | 17 | # replace bullet, doesn't take into account nesting 18 | text = text.gsub('', "\n* ").gsub('', '') 19 | 20 | # replace code 21 | text = text.gsub('', '....').gsub('', '....') 22 | end 23 | text 24 | end 25 | 26 | # takes in a serpico finding and returns asciidoc version 27 | def gen_asciidoc(finding, score) 28 | asciidoc = '' 29 | 30 | asciidoc << "== #{finding.title} \n\n" 31 | 32 | if score == 'dread' 33 | asciidoc << "|====== \n" 34 | asciidoc << "|Damage|Reproducibility|Exploitability|Affected Users|Discoverability|Remediation Effort\n" 35 | asciidoc << "|#{finding.damage} \n" 36 | asciidoc << "|#{finding.reproducability} \n" 37 | asciidoc << "|#{finding.exploitability} \n" 38 | asciidoc << "|#{finding.affected_users} \n" 39 | asciidoc << "|#{finding.discoverability} \n" 40 | asciidoc << "|#{finding.effort} \n" 41 | asciidoc << "|======\n\n" 42 | elsif (score == 'cvss') || (score == 'cvssv3') 43 | asciidoc << "|====== \n" 44 | asciidoc << '|CVSS Score Total:' 45 | asciidoc << "|#{finding.cvss_total} \n" 46 | asciidoc << "|======\n\n" 47 | else 48 | risk = %w[Informational Low Moderate High Critical] 49 | asciidoc << "|===\n" 50 | asciidoc << "|Risk |Remediation Effort\n" 51 | asciidoc << "|#{risk[finding.risk]} \n" 52 | asciidoc << "|#{finding.effort} \n" 53 | asciidoc << "|===\n\n" 54 | end 55 | 56 | asciidoc << "=== Overview \n" 57 | asciidoc << parse_input(finding.overview) + "\n" 58 | asciidoc << "=== Affected Hosts \n" 59 | asciidoc << parse_input(finding.affected_hosts) + "\n" 60 | asciidoc << "=== Proof of Concept \n" 61 | asciidoc << parse_input(finding.poc) + "\n" 62 | asciidoc << "=== Remediation \n" 63 | asciidoc << parse_input(finding.remediation) + "\n" 64 | asciidoc << "=== References \n" 65 | asciidoc << parse_input(finding.references) + "\n\n" 66 | asciidoc << "<<< \n\n" 67 | asciidoc 68 | end 69 | -------------------------------------------------------------------------------- /helpers/image.rb: -------------------------------------------------------------------------------- 1 | class JPEG 2 | attr_reader :width, :height 3 | def initialize(file) 4 | if file.kind_of? IO 5 | examine(file) 6 | else 7 | File.open(file, 'rb') { |io| examine(io) } 8 | end 9 | end 10 | private 11 | def examine(io) 12 | class << io 13 | def getc; super.bytes.first; end 14 | def readchar; super.bytes.first; end 15 | def readint; (readchar << 8) + readchar; end 16 | def readframe; read(readint - 2); end 17 | def readsof; [readint, readchar, readint, readint, readchar]; end 18 | def next 19 | c = readchar while c != 0xFF 20 | c = readchar while c == 0xFF 21 | c 22 | end 23 | end 24 | raise 'malformed JPEG' unless io.getc == 0xFF && io.getc == 0xD8 # SOI 25 | while marker = io.next 26 | case marker 27 | when 0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF # SOF markers 28 | length, @bits, @height, @width, components = io.readsof 29 | raise 'malformed JPEG' unless length == 8 + components * 3 30 | when 0xD9, 0xDA then break # EOI, SOS 31 | when 0xFE then @comment = io.readframe # COM 32 | when 0xE1 then io.readframe # APP1, contains EXIF tag 33 | else io.readframe # ignore frame 34 | end 35 | end 36 | end 37 | end 38 | 39 | def jpeg?(data) 40 | return data[0,4]=="\xff\xd8\xff\xe0".force_encoding(Encoding::ASCII_8BIT) 41 | end 42 | 43 | def png?(data) 44 | return data[0,8]=="\x89\x50\x4e\x47\x0d\x0a\x1a\x0a".force_encoding(Encoding::ASCII_8BIT) 45 | end 46 | 47 | -------------------------------------------------------------------------------- /helpers/odle_import.rb: -------------------------------------------------------------------------------- 1 | require './helpers/vuln_importer' 2 | require './helpers/helper' 3 | 4 | # import_odle(data) 5 | # 6 | # Input: JSON object parsed from an XML using odle 7 | # e.g. data = JSON.parse(Nessus.new().parse(File.open(scan_xml),"0")) 8 | # 9 | # Note: format from odle data is: 10 | # An array of hosts with the tuple [host_location,[{JSON_OBJECT_OF_FINDING_DATA}]] 11 | # For example, host 1's title would be data[0][1][0]['title'] 12 | # 13 | # @return: Returns an array of findings 14 | 15 | def import_odle(data) 16 | vulns = {} 17 | findings = [] 18 | 19 | data.each do |host| 20 | next unless host[1][0] 21 | 22 | host_d = host[1][0] 23 | 24 | finding = Findings.new 25 | finding.title = host_d['title'] 26 | finding.overview = clean(host_d['overview']) 27 | finding.remediation = clean(host_d['remediation']) 28 | finding.type = 'Imported' 29 | finding.risk = clean(host_d['severity']) 30 | finding = provide_null_score(finding) 31 | finding.affected_hosts = host[0] 32 | finding.notes = clean(host_d['notes']) 33 | finding.references = clean(host_d['see_also']) 34 | 35 | findings << finding 36 | end 37 | 38 | vulns['findings'] = uniq_findings(findings) 39 | vulns 40 | end 41 | -------------------------------------------------------------------------------- /helpers/plugin_listener.rb: -------------------------------------------------------------------------------- 1 | # For more information on the behavior of this class, look at helpers/plugin_notifier.rb 2 | class PluginListener 3 | # By design, a PluginListener will return no information when notified. 4 | # => This should be overridden by each plugin that need to add content into a report XML 5 | def notify_report_generated(report_object) 6 | return "" 7 | end 8 | 9 | # By design, a PluginListener will do no cleanup when notified. 10 | # => This should be overridden by each plugin that need to cleanup his local database when reports are deleted. 11 | def notify_report_deleted(report_object) 12 | return 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /helpers/plugin_notifier.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | require 'singleton' 3 | 4 | # This class is used to reproduce an Observer-like design pattern 5 | # => Every plugin that inherits the PluginListener class will receive the events listed in this class 6 | # 7 | # => Do note that the methods in this class should remain generic. If you need something that only applies to 1YWoswqCayG3vIFHuRmnku8g 8 | # plugin, consider doing it elsewhere. 9 | class PluginNotifier 10 | include Singleton 11 | 12 | def initialize 13 | @plugins = [] 14 | end 15 | 16 | # Add a plugin PluginListener 17 | # => The listener will be notified when a report is generated so he can add XML content into the report 18 | # => He will also be notified when a report is deleted so he can cleanup his local database 19 | def attach_plugin(observed_plugin) 20 | if observed_plugin and observed_plugin.class <= PluginListener 21 | @plugins.push observed_plugin 22 | else 23 | raise 'All observed classes must be non-null and inherit from the PluginListener class.' 24 | end 25 | end 26 | 27 | def detach_plugin(observed_plugin) 28 | @plugins.remove observed_plugin 29 | end 30 | 31 | def notify_report_generated(report_object) 32 | returned_xml = "\n" 33 | 34 | @plugins.each { |observer| 35 | returned_xml << observer.notify_report_generated(report_object) 36 | } 37 | 38 | returned_xml << "\n" 39 | return returned_xml 40 | end 41 | 42 | def notify_report_deleted(report_object) 43 | @plugins.each { |observer| observer.notify_report_deleted(report_object) } 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /helpers/sinatra_ssl.rb: -------------------------------------------------------------------------------- 1 | require 'webrick/https' 2 | require 'openssl' 3 | 4 | # I can't take credit for this, this is from a stackoverflow article 5 | # http://stackoverflow.com/questions/2362148/how-to-enable-ssl-for-a-standalone-sinatra-app 6 | 7 | module Sinatra 8 | class Application 9 | def self.run! 10 | server_options = { 11 | Port: port, 12 | SSLEnable: use_ssl, 13 | BindAddress: bind_address 14 | } 15 | if use_ssl 16 | certificate_content = File.open(ssl_certificate).read 17 | key_content = File.open(ssl_key).read 18 | server_options[:SSLCertificate] = OpenSSL::X509::Certificate.new(certificate_content) 19 | server_options[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(key_content) 20 | server_options[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_NONE 21 | end 22 | 23 | Rack::Handler::WEBrick.run self, server_options do |server| 24 | %i[INT TERM].each { |sig| trap(sig) { server.stop } } 25 | server.threaded = settings.threaded if server.respond_to? :threaded= 26 | set :running, true 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /log/blank: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/log/blank -------------------------------------------------------------------------------- /public/codemirror/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | -------------------------------------------------------------------------------- /public/codemirror/.gitattributes: -------------------------------------------------------------------------------- 1 | *.txt text eol=lf 2 | *.js text eol=lf 3 | *.html text eol=lf 4 | *.md text eol=lf 5 | *.json text eol=lf 6 | *.yml text eol=lf 7 | *.css text eol=lf 8 | *.svg text eol=lf 9 | -------------------------------------------------------------------------------- /public/codemirror/.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /demo 3 | /doc 4 | /test 5 | /test*.html 6 | /index.html 7 | /mode/*/*test.js 8 | /mode/*/*.html 9 | /mode/index.html 10 | .* 11 | bin 12 | -------------------------------------------------------------------------------- /public/codemirror/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | sudo: false 5 | -------------------------------------------------------------------------------- /public/codemirror/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2017 by Marijn Haverbeke and others 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /public/codemirror/README.md: -------------------------------------------------------------------------------- 1 | # CodeMirror 2 | [![Build Status](https://travis-ci.org/codemirror/CodeMirror.svg)](https://travis-ci.org/codemirror/CodeMirror) 3 | [![NPM version](https://img.shields.io/npm/v/codemirror.svg)](https://www.npmjs.org/package/codemirror) 4 | [![Join the chat at https://gitter.im/codemirror/CodeMirror](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/codemirror/CodeMirror) 5 | [Funding status: ![maintainer happiness](https://marijnhaverbeke.nl/fund/status_s.png?again)](https://marijnhaverbeke.nl/fund/) 6 | 7 | CodeMirror is a versatile text editor implemented in JavaScript for 8 | the browser. It is specialized for editing code, and comes with over 9 | 100 language modes and various addons that implement more advanced 10 | editing functionality. Every language comes with fully-featured code 11 | and syntax highlighting to help with reading and editing complex code. 12 | 13 | A rich programming API and a CSS theming system are available for 14 | customizing CodeMirror to fit your application, and extending it with 15 | new functionality. 16 | 17 | You can find more information (and the 18 | [manual](http://codemirror.net/doc/manual.html)) on the [project 19 | page](http://codemirror.net). For questions and discussion, use the 20 | [discussion forum](https://discuss.codemirror.net/). 21 | 22 | See 23 | [CONTRIBUTING.md](https://github.com/codemirror/CodeMirror/blob/master/CONTRIBUTING.md) 24 | for contributing guidelines. 25 | 26 | The CodeMirror community aims to be welcoming to everybody. We use the 27 | [Contributor Covenant 28 | (1.1)](http://contributor-covenant.org/version/1/1/0/) as our code of 29 | conduct. 30 | 31 | ### Quickstart 32 | 33 | To build the project, make sure you have Node.js installed (at least version 6) 34 | and then `npm install`. To run, just open `index.html` in your 35 | browser (you don't need to run a webserver). Run the tests with `npm test`. 36 | -------------------------------------------------------------------------------- /public/codemirror/addon/dialog/dialog.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-dialog { 2 | position: absolute; 3 | left: 0; right: 0; 4 | background: inherit; 5 | z-index: 15; 6 | padding: .1em .8em; 7 | overflow: hidden; 8 | color: inherit; 9 | } 10 | 11 | .CodeMirror-dialog-top { 12 | border-bottom: 1px solid #eee; 13 | top: 0; 14 | } 15 | 16 | .CodeMirror-dialog-bottom { 17 | border-top: 1px solid #eee; 18 | bottom: 0; 19 | } 20 | 21 | .CodeMirror-dialog input { 22 | border: none; 23 | outline: none; 24 | background: transparent; 25 | width: 20em; 26 | color: inherit; 27 | font-family: monospace; 28 | } 29 | 30 | .CodeMirror-dialog button { 31 | font-size: 70%; 32 | } 33 | -------------------------------------------------------------------------------- /public/codemirror/addon/display/autorefresh.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")) 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod) 9 | else // Plain browser env 10 | mod(CodeMirror) 11 | })(function(CodeMirror) { 12 | "use strict" 13 | 14 | CodeMirror.defineOption("autoRefresh", false, function(cm, val) { 15 | if (cm.state.autoRefresh) { 16 | stopListening(cm, cm.state.autoRefresh) 17 | cm.state.autoRefresh = null 18 | } 19 | if (val && cm.display.wrapper.offsetHeight == 0) 20 | startListening(cm, cm.state.autoRefresh = {delay: val.delay || 250}) 21 | }) 22 | 23 | function startListening(cm, state) { 24 | function check() { 25 | if (cm.display.wrapper.offsetHeight) { 26 | stopListening(cm, state) 27 | if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight) 28 | cm.refresh() 29 | } else { 30 | state.timeout = setTimeout(check, state.delay) 31 | } 32 | } 33 | state.timeout = setTimeout(check, state.delay) 34 | state.hurry = function() { 35 | clearTimeout(state.timeout) 36 | state.timeout = setTimeout(check, 50) 37 | } 38 | CodeMirror.on(window, "mouseup", state.hurry) 39 | CodeMirror.on(window, "keyup", state.hurry) 40 | } 41 | 42 | function stopListening(_cm, state) { 43 | clearTimeout(state.timeout) 44 | CodeMirror.off(window, "mouseup", state.hurry) 45 | CodeMirror.off(window, "keyup", state.hurry) 46 | } 47 | }); 48 | -------------------------------------------------------------------------------- /public/codemirror/addon/display/fullscreen.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-fullscreen { 2 | position: fixed; 3 | top: 0; left: 0; right: 0; bottom: 0; 4 | height: auto; 5 | z-index: 9; 6 | } 7 | -------------------------------------------------------------------------------- /public/codemirror/addon/display/fullscreen.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineOption("fullScreen", false, function(cm, val, old) { 15 | if (old == CodeMirror.Init) old = false; 16 | if (!old == !val) return; 17 | if (val) setFullscreen(cm); 18 | else setNormal(cm); 19 | }); 20 | 21 | function setFullscreen(cm) { 22 | var wrap = cm.getWrapperElement(); 23 | cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset, 24 | width: wrap.style.width, height: wrap.style.height}; 25 | wrap.style.width = ""; 26 | wrap.style.height = "auto"; 27 | wrap.className += " CodeMirror-fullscreen"; 28 | document.documentElement.style.overflow = "hidden"; 29 | cm.refresh(); 30 | } 31 | 32 | function setNormal(cm) { 33 | var wrap = cm.getWrapperElement(); 34 | wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, ""); 35 | document.documentElement.style.overflow = ""; 36 | var info = cm.state.fullScreenRestore; 37 | wrap.style.width = info.width; wrap.style.height = info.height; 38 | window.scrollTo(info.scrollLeft, info.scrollTop); 39 | cm.refresh(); 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /public/codemirror/addon/display/placeholder.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | CodeMirror.defineOption("placeholder", "", function(cm, val, old) { 13 | var prev = old && old != CodeMirror.Init; 14 | if (val && !prev) { 15 | cm.on("blur", onBlur); 16 | cm.on("change", onChange); 17 | cm.on("swapDoc", onChange); 18 | onChange(cm); 19 | } else if (!val && prev) { 20 | cm.off("blur", onBlur); 21 | cm.off("change", onChange); 22 | cm.off("swapDoc", onChange); 23 | clearPlaceholder(cm); 24 | var wrapper = cm.getWrapperElement(); 25 | wrapper.className = wrapper.className.replace(" CodeMirror-empty", ""); 26 | } 27 | 28 | if (val && !cm.hasFocus()) onBlur(cm); 29 | }); 30 | 31 | function clearPlaceholder(cm) { 32 | if (cm.state.placeholder) { 33 | cm.state.placeholder.parentNode.removeChild(cm.state.placeholder); 34 | cm.state.placeholder = null; 35 | } 36 | } 37 | function setPlaceholder(cm) { 38 | clearPlaceholder(cm); 39 | var elt = cm.state.placeholder = document.createElement("pre"); 40 | elt.style.cssText = "height: 0; overflow: visible"; 41 | elt.style.direction = cm.getOption("direction"); 42 | elt.className = "CodeMirror-placeholder"; 43 | var placeHolder = cm.getOption("placeholder") 44 | if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder) 45 | elt.appendChild(placeHolder) 46 | cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild); 47 | } 48 | 49 | function onBlur(cm) { 50 | if (isEmpty(cm)) setPlaceholder(cm); 51 | } 52 | function onChange(cm) { 53 | var wrapper = cm.getWrapperElement(), empty = isEmpty(cm); 54 | wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : ""); 55 | 56 | if (empty) setPlaceholder(cm); 57 | else clearPlaceholder(cm); 58 | } 59 | 60 | function isEmpty(cm) { 61 | return (cm.lineCount() === 1) && (cm.getLine(0) === ""); 62 | } 63 | }); 64 | -------------------------------------------------------------------------------- /public/codemirror/addon/display/rulers.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineOption("rulers", false, function(cm, val) { 15 | if (cm.state.rulerDiv) { 16 | cm.state.rulerDiv.parentElement.removeChild(cm.state.rulerDiv) 17 | cm.state.rulerDiv = null 18 | cm.off("refresh", drawRulers) 19 | } 20 | if (val && val.length) { 21 | cm.state.rulerDiv = cm.display.lineSpace.parentElement.insertBefore(document.createElement("div"), cm.display.lineSpace) 22 | cm.state.rulerDiv.className = "CodeMirror-rulers" 23 | drawRulers(cm) 24 | cm.on("refresh", drawRulers) 25 | } 26 | }); 27 | 28 | function drawRulers(cm) { 29 | cm.state.rulerDiv.textContent = "" 30 | var val = cm.getOption("rulers"); 31 | var cw = cm.defaultCharWidth(); 32 | var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), "div").left; 33 | cm.state.rulerDiv.style.minHeight = (cm.display.scroller.offsetHeight + 30) + "px"; 34 | for (var i = 0; i < val.length; i++) { 35 | var elt = document.createElement("div"); 36 | elt.className = "CodeMirror-ruler"; 37 | var col, conf = val[i]; 38 | if (typeof conf == "number") { 39 | col = conf; 40 | } else { 41 | col = conf.column; 42 | if (conf.className) elt.className += " " + conf.className; 43 | if (conf.color) elt.style.borderColor = conf.color; 44 | if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle; 45 | if (conf.width) elt.style.borderLeftWidth = conf.width; 46 | } 47 | elt.style.left = (left + col * cw) + "px"; 48 | cm.state.rulerDiv.appendChild(elt) 49 | } 50 | } 51 | }); 52 | -------------------------------------------------------------------------------- /public/codemirror/addon/edit/matchtags.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("../fold/xml-fold")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "../fold/xml-fold"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineOption("matchTags", false, function(cm, val, old) { 15 | if (old && old != CodeMirror.Init) { 16 | cm.off("cursorActivity", doMatchTags); 17 | cm.off("viewportChange", maybeUpdateMatch); 18 | clear(cm); 19 | } 20 | if (val) { 21 | cm.state.matchBothTags = typeof val == "object" && val.bothTags; 22 | cm.on("cursorActivity", doMatchTags); 23 | cm.on("viewportChange", maybeUpdateMatch); 24 | doMatchTags(cm); 25 | } 26 | }); 27 | 28 | function clear(cm) { 29 | if (cm.state.tagHit) cm.state.tagHit.clear(); 30 | if (cm.state.tagOther) cm.state.tagOther.clear(); 31 | cm.state.tagHit = cm.state.tagOther = null; 32 | } 33 | 34 | function doMatchTags(cm) { 35 | cm.state.failedTagMatch = false; 36 | cm.operation(function() { 37 | clear(cm); 38 | if (cm.somethingSelected()) return; 39 | var cur = cm.getCursor(), range = cm.getViewport(); 40 | range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to); 41 | var match = CodeMirror.findMatchingTag(cm, cur, range); 42 | if (!match) return; 43 | if (cm.state.matchBothTags) { 44 | var hit = match.at == "open" ? match.open : match.close; 45 | if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"}); 46 | } 47 | var other = match.at == "close" ? match.open : match.close; 48 | if (other) 49 | cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"}); 50 | else 51 | cm.state.failedTagMatch = true; 52 | }); 53 | } 54 | 55 | function maybeUpdateMatch(cm) { 56 | if (cm.state.failedTagMatch) doMatchTags(cm); 57 | } 58 | 59 | CodeMirror.commands.toMatchingTag = function(cm) { 60 | var found = CodeMirror.findMatchingTag(cm, cm.getCursor()); 61 | if (found) { 62 | var other = found.at == "close" ? found.open : found.close; 63 | if (other) cm.extendSelection(other.to, other.from); 64 | } 65 | }; 66 | }); 67 | -------------------------------------------------------------------------------- /public/codemirror/addon/edit/trailingspace.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { 13 | if (prev == CodeMirror.Init) prev = false; 14 | if (prev && !val) 15 | cm.removeOverlay("trailingspace"); 16 | else if (!prev && val) 17 | cm.addOverlay({ 18 | token: function(stream) { 19 | for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} 20 | if (i > stream.pos) { stream.pos = i; return null; } 21 | stream.pos = l; 22 | return "trailingspace"; 23 | }, 24 | name: "trailingspace" 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /public/codemirror/addon/fold/comment-fold.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.registerGlobalHelper("fold", "comment", function(mode) { 15 | return mode.blockCommentStart && mode.blockCommentEnd; 16 | }, function(cm, start) { 17 | var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd; 18 | if (!startToken || !endToken) return; 19 | var line = start.line, lineText = cm.getLine(line); 20 | 21 | var startCh; 22 | for (var at = start.ch, pass = 0;;) { 23 | var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1); 24 | if (found == -1) { 25 | if (pass == 1) return; 26 | pass = 1; 27 | at = lineText.length; 28 | continue; 29 | } 30 | if (pass == 1 && found < start.ch) return; 31 | if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) && 32 | (found == 0 || lineText.slice(found - endToken.length, found) == endToken || 33 | !/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) { 34 | startCh = found + startToken.length; 35 | break; 36 | } 37 | at = found - 1; 38 | } 39 | 40 | var depth = 1, lastLine = cm.lastLine(), end, endCh; 41 | outer: for (var i = line; i <= lastLine; ++i) { 42 | var text = cm.getLine(i), pos = i == line ? startCh : 0; 43 | for (;;) { 44 | var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos); 45 | if (nextOpen < 0) nextOpen = text.length; 46 | if (nextClose < 0) nextClose = text.length; 47 | pos = Math.min(nextOpen, nextClose); 48 | if (pos == text.length) break; 49 | if (pos == nextOpen) ++depth; 50 | else if (!--depth) { end = i; endCh = pos; break outer; } 51 | ++pos; 52 | } 53 | } 54 | if (end == null || line == end && endCh == startCh) return; 55 | return {from: CodeMirror.Pos(line, startCh), 56 | to: CodeMirror.Pos(end, endCh)}; 57 | }); 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /public/codemirror/addon/fold/foldgutter.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-foldmarker { 2 | color: blue; 3 | text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px; 4 | font-family: arial; 5 | line-height: .3; 6 | cursor: pointer; 7 | } 8 | .CodeMirror-foldgutter { 9 | width: .7em; 10 | } 11 | .CodeMirror-foldgutter-open, 12 | .CodeMirror-foldgutter-folded { 13 | cursor: pointer; 14 | } 15 | .CodeMirror-foldgutter-open:after { 16 | content: "\25BE"; 17 | } 18 | .CodeMirror-foldgutter-folded:after { 19 | content: "\25B8"; 20 | } 21 | -------------------------------------------------------------------------------- /public/codemirror/addon/fold/indent-fold.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | function lineIndent(cm, lineNo) { 15 | var text = cm.getLine(lineNo) 16 | var spaceTo = text.search(/\S/) 17 | if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1)))) 18 | return -1 19 | return CodeMirror.countColumn(text, null, cm.getOption("tabSize")) 20 | } 21 | 22 | CodeMirror.registerHelper("fold", "indent", function(cm, start) { 23 | var myIndent = lineIndent(cm, start.line) 24 | if (myIndent < 0) return 25 | var lastLineInFold = null 26 | 27 | // Go through lines until we find a line that definitely doesn't belong in 28 | // the block we're folding, or to the end. 29 | for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) { 30 | var indent = lineIndent(cm, i) 31 | if (indent == -1) { 32 | } else if (indent > myIndent) { 33 | // Lines with a greater indent are considered part of the block. 34 | lastLineInFold = i; 35 | } else { 36 | // If this line has non-space, non-comment content, and is 37 | // indented less or equal to the start line, it is the start of 38 | // another block. 39 | break; 40 | } 41 | } 42 | if (lastLineInFold) return { 43 | from: CodeMirror.Pos(start.line, cm.getLine(start.line).length), 44 | to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length) 45 | }; 46 | }); 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /public/codemirror/addon/fold/markdown-fold.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.registerHelper("fold", "markdown", function(cm, start) { 15 | var maxDepth = 100; 16 | 17 | function isHeader(lineNo) { 18 | var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0)); 19 | return tokentype && /\bheader\b/.test(tokentype); 20 | } 21 | 22 | function headerLevel(lineNo, line, nextLine) { 23 | var match = line && line.match(/^#+/); 24 | if (match && isHeader(lineNo)) return match[0].length; 25 | match = nextLine && nextLine.match(/^[=\-]+\s*$/); 26 | if (match && isHeader(lineNo + 1)) return nextLine[0] == "=" ? 1 : 2; 27 | return maxDepth; 28 | } 29 | 30 | var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1); 31 | var level = headerLevel(start.line, firstLine, nextLine); 32 | if (level === maxDepth) return undefined; 33 | 34 | var lastLineNo = cm.lastLine(); 35 | var end = start.line, nextNextLine = cm.getLine(end + 2); 36 | while (end < lastLineNo) { 37 | if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break; 38 | ++end; 39 | nextLine = nextNextLine; 40 | nextNextLine = cm.getLine(end + 2); 41 | } 42 | 43 | return { 44 | from: CodeMirror.Pos(start.line, firstLine.length), 45 | to: CodeMirror.Pos(end, cm.getLine(end).length) 46 | }; 47 | }); 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /public/codemirror/addon/hint/anyword-hint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var WORD = /[\w$]+/, RANGE = 500; 15 | 16 | CodeMirror.registerHelper("hint", "anyword", function(editor, options) { 17 | var word = options && options.word || WORD; 18 | var range = options && options.range || RANGE; 19 | var cur = editor.getCursor(), curLine = editor.getLine(cur.line); 20 | var end = cur.ch, start = end; 21 | while (start && word.test(curLine.charAt(start - 1))) --start; 22 | var curWord = start != end && curLine.slice(start, end); 23 | 24 | var list = options && options.list || [], seen = {}; 25 | var re = new RegExp(word.source, "g"); 26 | for (var dir = -1; dir <= 1; dir += 2) { 27 | var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; 28 | for (; line != endLine; line += dir) { 29 | var text = editor.getLine(line), m; 30 | while (m = re.exec(text)) { 31 | if (line == cur.line && m[0] === curWord) continue; 32 | if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) { 33 | seen[m[0]] = true; 34 | list.push(m[0]); 35 | } 36 | } 37 | } 38 | } 39 | return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /public/codemirror/addon/hint/css-hint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("../../mode/css/css")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "../../mode/css/css"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1, 15 | "first-letter": 1, "first-line": 1, "first-child": 1, 16 | before: 1, after: 1, lang: 1}; 17 | 18 | CodeMirror.registerHelper("hint", "css", function(cm) { 19 | var cur = cm.getCursor(), token = cm.getTokenAt(cur); 20 | var inner = CodeMirror.innerMode(cm.getMode(), token.state); 21 | if (inner.mode.name != "css") return; 22 | 23 | if (token.type == "keyword" && "!important".indexOf(token.string) == 0) 24 | return {list: ["!important"], from: CodeMirror.Pos(cur.line, token.start), 25 | to: CodeMirror.Pos(cur.line, token.end)}; 26 | 27 | var start = token.start, end = cur.ch, word = token.string.slice(0, end - start); 28 | if (/[^\w$_-]/.test(word)) { 29 | word = ""; start = end = cur.ch; 30 | } 31 | 32 | var spec = CodeMirror.resolveMode("text/css"); 33 | 34 | var result = []; 35 | function add(keywords) { 36 | for (var name in keywords) 37 | if (!word || name.lastIndexOf(word, 0) == 0) 38 | result.push(name); 39 | } 40 | 41 | var st = inner.state.state; 42 | if (st == "pseudo" || token.type == "variable-3") { 43 | add(pseudoClasses); 44 | } else if (st == "block" || st == "maybeprop") { 45 | add(spec.propertyKeywords); 46 | } else if (st == "prop" || st == "parens" || st == "at" || st == "params") { 47 | add(spec.valueKeywords); 48 | add(spec.colorKeywords); 49 | } else if (st == "media" || st == "media_parens") { 50 | add(spec.mediaTypes); 51 | add(spec.mediaFeatures); 52 | } 53 | 54 | if (result.length) return { 55 | list: result, 56 | from: CodeMirror.Pos(cur.line, start), 57 | to: CodeMirror.Pos(cur.line, end) 58 | }; 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /public/codemirror/addon/hint/show-hint.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-hints { 2 | position: absolute; 3 | z-index: 10; 4 | overflow: hidden; 5 | list-style: none; 6 | 7 | margin: 0; 8 | padding: 2px; 9 | 10 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 11 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 12 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 13 | border-radius: 3px; 14 | border: 1px solid silver; 15 | 16 | background: white; 17 | font-size: 90%; 18 | font-family: monospace; 19 | 20 | max-height: 20em; 21 | overflow-y: auto; 22 | } 23 | 24 | .CodeMirror-hint { 25 | margin: 0; 26 | padding: 0 4px; 27 | border-radius: 2px; 28 | white-space: pre; 29 | color: black; 30 | cursor: pointer; 31 | } 32 | 33 | li.CodeMirror-hint-active { 34 | background: #08f; 35 | color: white; 36 | } 37 | -------------------------------------------------------------------------------- /public/codemirror/addon/lint/coffeescript-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | // Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js 5 | 6 | // declare global: coffeelint 7 | 8 | (function(mod) { 9 | if (typeof exports == "object" && typeof module == "object") // CommonJS 10 | mod(require("../../lib/codemirror")); 11 | else if (typeof define == "function" && define.amd) // AMD 12 | define(["../../lib/codemirror"], mod); 13 | else // Plain browser env 14 | mod(CodeMirror); 15 | })(function(CodeMirror) { 16 | "use strict"; 17 | 18 | CodeMirror.registerHelper("lint", "coffeescript", function(text) { 19 | var found = []; 20 | if (!window.coffeelint) { 21 | if (window.console) { 22 | window.console.error("Error: window.coffeelint not defined, CodeMirror CoffeeScript linting cannot run."); 23 | } 24 | return found; 25 | } 26 | var parseError = function(err) { 27 | var loc = err.lineNumber; 28 | found.push({from: CodeMirror.Pos(loc-1, 0), 29 | to: CodeMirror.Pos(loc, 0), 30 | severity: err.level, 31 | message: err.message}); 32 | }; 33 | try { 34 | var res = coffeelint.lint(text); 35 | for(var i = 0; i < res.length; i++) { 36 | parseError(res[i]); 37 | } 38 | } catch(e) { 39 | found.push({from: CodeMirror.Pos(e.location.first_line, 0), 40 | to: CodeMirror.Pos(e.location.last_line, e.location.last_column), 41 | severity: 'error', 42 | message: e.message}); 43 | } 44 | return found; 45 | }); 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /public/codemirror/addon/lint/css-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | // Depends on csslint.js from https://github.com/stubbornella/csslint 5 | 6 | // declare global: CSSLint 7 | 8 | (function(mod) { 9 | if (typeof exports == "object" && typeof module == "object") // CommonJS 10 | mod(require("../../lib/codemirror")); 11 | else if (typeof define == "function" && define.amd) // AMD 12 | define(["../../lib/codemirror"], mod); 13 | else // Plain browser env 14 | mod(CodeMirror); 15 | })(function(CodeMirror) { 16 | "use strict"; 17 | 18 | CodeMirror.registerHelper("lint", "css", function(text, options) { 19 | var found = []; 20 | if (!window.CSSLint) { 21 | if (window.console) { 22 | window.console.error("Error: window.CSSLint not defined, CodeMirror CSS linting cannot run."); 23 | } 24 | return found; 25 | } 26 | var results = CSSLint.verify(text, options), messages = results.messages, message = null; 27 | for ( var i = 0; i < messages.length; i++) { 28 | message = messages[i]; 29 | var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col; 30 | found.push({ 31 | from: CodeMirror.Pos(startLine, startCol), 32 | to: CodeMirror.Pos(endLine, endCol), 33 | message: message.message, 34 | severity : message.type 35 | }); 36 | } 37 | return found; 38 | }); 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /public/codemirror/addon/lint/html-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | // Depends on htmlhint.js from http://htmlhint.com/js/htmlhint.js 5 | 6 | // declare global: HTMLHint 7 | 8 | (function(mod) { 9 | if (typeof exports == "object" && typeof module == "object") // CommonJS 10 | mod(require("../../lib/codemirror"), require("htmlhint")); 11 | else if (typeof define == "function" && define.amd) // AMD 12 | define(["../../lib/codemirror", "htmlhint"], mod); 13 | else // Plain browser env 14 | mod(CodeMirror, window.HTMLHint); 15 | })(function(CodeMirror, HTMLHint) { 16 | "use strict"; 17 | 18 | var defaultRules = { 19 | "tagname-lowercase": true, 20 | "attr-lowercase": true, 21 | "attr-value-double-quotes": true, 22 | "doctype-first": false, 23 | "tag-pair": true, 24 | "spec-char-escape": true, 25 | "id-unique": true, 26 | "src-not-empty": true, 27 | "attr-no-duplication": true 28 | }; 29 | 30 | CodeMirror.registerHelper("lint", "html", function(text, options) { 31 | var found = []; 32 | if (HTMLHint && !HTMLHint.verify) HTMLHint = HTMLHint.HTMLHint; 33 | if (!HTMLHint) HTMLHint = window.HTMLHint; 34 | if (!HTMLHint) { 35 | if (window.console) { 36 | window.console.error("Error: HTMLHint not found, not defined on window, or not available through define/require, CodeMirror HTML linting cannot run."); 37 | } 38 | return found; 39 | } 40 | var messages = HTMLHint.verify(text, options && options.rules || defaultRules); 41 | for (var i = 0; i < messages.length; i++) { 42 | var message = messages[i]; 43 | var startLine = message.line - 1, endLine = message.line - 1, startCol = message.col - 1, endCol = message.col; 44 | found.push({ 45 | from: CodeMirror.Pos(startLine, startCol), 46 | to: CodeMirror.Pos(endLine, endCol), 47 | message: message.message, 48 | severity : message.type 49 | }); 50 | } 51 | return found; 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /public/codemirror/addon/lint/javascript-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | // declare global: JSHINT 14 | 15 | function validator(text, options) { 16 | if (!window.JSHINT) { 17 | if (window.console) { 18 | window.console.error("Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run."); 19 | } 20 | return []; 21 | } 22 | if (!options.indent) // JSHint error.character actually is a column index, this fixes underlining on lines using tabs for indentation 23 | options.indent = 1; // JSHint default value is 4 24 | JSHINT(text, options, options.globals); 25 | var errors = JSHINT.data().errors, result = []; 26 | if (errors) parseErrors(errors, result); 27 | return result; 28 | } 29 | 30 | CodeMirror.registerHelper("lint", "javascript", validator); 31 | 32 | function parseErrors(errors, output) { 33 | for ( var i = 0; i < errors.length; i++) { 34 | var error = errors[i]; 35 | if (error) { 36 | if (error.line <= 0) { 37 | if (window.console) { 38 | window.console.warn("Cannot display JSHint error (invalid line " + error.line + ")", error); 39 | } 40 | continue; 41 | } 42 | 43 | var start = error.character - 1, end = start + 1; 44 | if (error.evidence) { 45 | var index = error.evidence.substring(start).search(/.\b/); 46 | if (index > -1) { 47 | end += index; 48 | } 49 | } 50 | 51 | // Convert to format expected by validation service 52 | var hint = { 53 | message: error.reason, 54 | severity: error.code ? (error.code.startsWith('W') ? "warning" : "error") : "error", 55 | from: CodeMirror.Pos(error.line - 1, start), 56 | to: CodeMirror.Pos(error.line - 1, end) 57 | }; 58 | 59 | output.push(hint); 60 | } 61 | } 62 | } 63 | }); 64 | -------------------------------------------------------------------------------- /public/codemirror/addon/lint/json-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | // Depends on jsonlint.js from https://github.com/zaach/jsonlint 5 | 6 | // declare global: jsonlint 7 | 8 | (function(mod) { 9 | if (typeof exports == "object" && typeof module == "object") // CommonJS 10 | mod(require("../../lib/codemirror")); 11 | else if (typeof define == "function" && define.amd) // AMD 12 | define(["../../lib/codemirror"], mod); 13 | else // Plain browser env 14 | mod(CodeMirror); 15 | })(function(CodeMirror) { 16 | "use strict"; 17 | 18 | CodeMirror.registerHelper("lint", "json", function(text) { 19 | var found = []; 20 | if (!window.jsonlint) { 21 | if (window.console) { 22 | window.console.error("Error: window.jsonlint not defined, CodeMirror JSON linting cannot run."); 23 | } 24 | return found; 25 | } 26 | jsonlint.parseError = function(str, hash) { 27 | var loc = hash.loc; 28 | found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column), 29 | to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), 30 | message: str}); 31 | }; 32 | try { jsonlint.parse(text); } 33 | catch(e) {} 34 | return found; 35 | }); 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /public/codemirror/addon/lint/yaml-lint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | // Depends on js-yaml.js from https://github.com/nodeca/js-yaml 15 | 16 | // declare global: jsyaml 17 | 18 | CodeMirror.registerHelper("lint", "yaml", function(text) { 19 | var found = []; 20 | if (!window.jsyaml) { 21 | if (window.console) { 22 | window.console.error("Error: window.jsyaml not defined, CodeMirror YAML linting cannot run."); 23 | } 24 | return found; 25 | } 26 | try { jsyaml.load(text); } 27 | catch(e) { 28 | var loc = e.mark, 29 | // js-yaml YAMLException doesn't always provide an accurate lineno 30 | // e.g., when there are multiple yaml docs 31 | // --- 32 | // --- 33 | // foo:bar 34 | from = loc ? CodeMirror.Pos(loc.line, loc.column) : CodeMirror.Pos(0, 0), 35 | to = from; 36 | found.push({ from: from, to: to, message: e.message }); 37 | } 38 | return found; 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /public/codemirror/addon/mode/loadmode.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), "cjs"); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], function(CM) { mod(CM, "amd"); }); 9 | else // Plain browser env 10 | mod(CodeMirror, "plain"); 11 | })(function(CodeMirror, env) { 12 | if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js"; 13 | 14 | var loading = {}; 15 | function splitCallback(cont, n) { 16 | var countDown = n; 17 | return function() { if (--countDown == 0) cont(); }; 18 | } 19 | function ensureDeps(mode, cont) { 20 | var deps = CodeMirror.modes[mode].dependencies; 21 | if (!deps) return cont(); 22 | var missing = []; 23 | for (var i = 0; i < deps.length; ++i) { 24 | if (!CodeMirror.modes.hasOwnProperty(deps[i])) 25 | missing.push(deps[i]); 26 | } 27 | if (!missing.length) return cont(); 28 | var split = splitCallback(cont, missing.length); 29 | for (var i = 0; i < missing.length; ++i) 30 | CodeMirror.requireMode(missing[i], split); 31 | } 32 | 33 | CodeMirror.requireMode = function(mode, cont) { 34 | if (typeof mode != "string") mode = mode.name; 35 | if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont); 36 | if (loading.hasOwnProperty(mode)) return loading[mode].push(cont); 37 | 38 | var file = CodeMirror.modeURL.replace(/%N/g, mode); 39 | if (env == "plain") { 40 | var script = document.createElement("script"); 41 | script.src = file; 42 | var others = document.getElementsByTagName("script")[0]; 43 | var list = loading[mode] = [cont]; 44 | CodeMirror.on(script, "load", function() { 45 | ensureDeps(mode, function() { 46 | for (var i = 0; i < list.length; ++i) list[i](); 47 | }); 48 | }); 49 | others.parentNode.insertBefore(script, others); 50 | } else if (env == "cjs") { 51 | require(file); 52 | cont(); 53 | } else if (env == "amd") { 54 | requirejs([file], cont); 55 | } 56 | }; 57 | 58 | CodeMirror.autoLoadMode = function(instance, mode) { 59 | if (!CodeMirror.modes.hasOwnProperty(mode)) 60 | CodeMirror.requireMode(mode, function() { 61 | instance.setOption("mode", instance.getOption("mode")); 62 | }); 63 | }; 64 | }); 65 | -------------------------------------------------------------------------------- /public/codemirror/addon/mode/multiplex_test.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function() { 5 | CodeMirror.defineMode("markdown_with_stex", function(){ 6 | var inner = CodeMirror.getMode({}, "stex"); 7 | var outer = CodeMirror.getMode({}, "markdown"); 8 | 9 | var innerOptions = { 10 | open: '$', 11 | close: '$', 12 | mode: inner, 13 | delimStyle: 'delim', 14 | innerStyle: 'inner' 15 | }; 16 | 17 | return CodeMirror.multiplexingMode(outer, innerOptions); 18 | }); 19 | 20 | var mode = CodeMirror.getMode({}, "markdown_with_stex"); 21 | 22 | function MT(name) { 23 | test.mode( 24 | name, 25 | mode, 26 | Array.prototype.slice.call(arguments, 1), 27 | 'multiplexing'); 28 | } 29 | 30 | MT( 31 | "stexInsideMarkdown", 32 | "[strong **Equation:**] [delim&delim-open $][inner&tag \\pi][delim&delim-close $]"); 33 | })(); 34 | -------------------------------------------------------------------------------- /public/codemirror/addon/runmode/colorize.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("./runmode")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "./runmode"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/; 15 | 16 | function textContent(node, out) { 17 | if (node.nodeType == 3) return out.push(node.nodeValue); 18 | for (var ch = node.firstChild; ch; ch = ch.nextSibling) { 19 | textContent(ch, out); 20 | if (isBlock.test(node.nodeType)) out.push("\n"); 21 | } 22 | } 23 | 24 | CodeMirror.colorize = function(collection, defaultMode) { 25 | if (!collection) collection = document.body.getElementsByTagName("pre"); 26 | 27 | for (var i = 0; i < collection.length; ++i) { 28 | var node = collection[i]; 29 | var mode = node.getAttribute("data-lang") || defaultMode; 30 | if (!mode) continue; 31 | 32 | var text = []; 33 | textContent(node, text); 34 | node.innerHTML = ""; 35 | CodeMirror.runMode(text.join(""), mode, node); 36 | 37 | node.className += " cm-s-default"; 38 | } 39 | }; 40 | }); 41 | -------------------------------------------------------------------------------- /public/codemirror/addon/runmode/runmode.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.runMode = function(string, modespec, callback, options) { 15 | var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); 16 | var ie = /MSIE \d/.test(navigator.userAgent); 17 | var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9); 18 | 19 | if (callback.appendChild) { 20 | var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; 21 | var node = callback, col = 0; 22 | node.innerHTML = ""; 23 | callback = function(text, style) { 24 | if (text == "\n") { 25 | // Emitting LF or CRLF on IE8 or earlier results in an incorrect display. 26 | // Emitting a carriage return makes everything ok. 27 | node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text)); 28 | col = 0; 29 | return; 30 | } 31 | var content = ""; 32 | // replace tabs 33 | for (var pos = 0;;) { 34 | var idx = text.indexOf("\t", pos); 35 | if (idx == -1) { 36 | content += text.slice(pos); 37 | col += text.length - pos; 38 | break; 39 | } else { 40 | col += idx - pos; 41 | content += text.slice(pos, idx); 42 | var size = tabSize - col % tabSize; 43 | col += size; 44 | for (var i = 0; i < size; ++i) content += " "; 45 | pos = idx + 1; 46 | } 47 | } 48 | 49 | if (style) { 50 | var sp = node.appendChild(document.createElement("span")); 51 | sp.className = "cm-" + style.replace(/ +/g, " cm-"); 52 | sp.appendChild(document.createTextNode(content)); 53 | } else { 54 | node.appendChild(document.createTextNode(content)); 55 | } 56 | }; 57 | } 58 | 59 | var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); 60 | for (var i = 0, e = lines.length; i < e; ++i) { 61 | if (i) callback("\n"); 62 | var stream = new CodeMirror.StringStream(lines[i]); 63 | if (!stream.string && mode.blankLine) mode.blankLine(state); 64 | while (!stream.eol()) { 65 | var style = mode.token(stream, state); 66 | callback(stream.current(), style, i, stream.start, state); 67 | stream.start = stream.pos; 68 | } 69 | } 70 | }; 71 | 72 | }); 73 | -------------------------------------------------------------------------------- /public/codemirror/addon/scroll/scrollpastend.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineOption("scrollPastEnd", false, function(cm, val, old) { 15 | if (old && old != CodeMirror.Init) { 16 | cm.off("change", onChange); 17 | cm.off("refresh", updateBottomMargin); 18 | cm.display.lineSpace.parentNode.style.paddingBottom = ""; 19 | cm.state.scrollPastEndPadding = null; 20 | } 21 | if (val) { 22 | cm.on("change", onChange); 23 | cm.on("refresh", updateBottomMargin); 24 | updateBottomMargin(cm); 25 | } 26 | }); 27 | 28 | function onChange(cm, change) { 29 | if (CodeMirror.changeEnd(change).line == cm.lastLine()) 30 | updateBottomMargin(cm); 31 | } 32 | 33 | function updateBottomMargin(cm) { 34 | var padding = ""; 35 | if (cm.lineCount() > 1) { 36 | var totalH = cm.display.scroller.clientHeight - 30, 37 | lastLineH = cm.getLineHandle(cm.lastLine()).height; 38 | padding = (totalH - lastLineH) + "px"; 39 | } 40 | if (cm.state.scrollPastEndPadding != padding) { 41 | cm.state.scrollPastEndPadding = padding; 42 | cm.display.lineSpace.parentNode.style.paddingBottom = padding; 43 | cm.off("refresh", updateBottomMargin); 44 | cm.setSize(); 45 | cm.on("refresh", updateBottomMargin); 46 | } 47 | } 48 | }); 49 | -------------------------------------------------------------------------------- /public/codemirror/addon/scroll/simplescrollbars.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div { 2 | position: absolute; 3 | background: #ccc; 4 | -moz-box-sizing: border-box; 5 | box-sizing: border-box; 6 | border: 1px solid #bbb; 7 | border-radius: 2px; 8 | } 9 | 10 | .CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical { 11 | position: absolute; 12 | z-index: 6; 13 | background: #eee; 14 | } 15 | 16 | .CodeMirror-simplescroll-horizontal { 17 | bottom: 0; left: 0; 18 | height: 8px; 19 | } 20 | .CodeMirror-simplescroll-horizontal div { 21 | bottom: 0; 22 | height: 100%; 23 | } 24 | 25 | .CodeMirror-simplescroll-vertical { 26 | right: 0; top: 0; 27 | width: 8px; 28 | } 29 | .CodeMirror-simplescroll-vertical div { 30 | right: 0; 31 | width: 100%; 32 | } 33 | 34 | 35 | .CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler { 36 | display: none; 37 | } 38 | 39 | .CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div { 40 | position: absolute; 41 | background: #bcd; 42 | border-radius: 3px; 43 | } 44 | 45 | .CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical { 46 | position: absolute; 47 | z-index: 6; 48 | } 49 | 50 | .CodeMirror-overlayscroll-horizontal { 51 | bottom: 0; left: 0; 52 | height: 6px; 53 | } 54 | .CodeMirror-overlayscroll-horizontal div { 55 | bottom: 0; 56 | height: 100%; 57 | } 58 | 59 | .CodeMirror-overlayscroll-vertical { 60 | right: 0; top: 0; 61 | width: 6px; 62 | } 63 | .CodeMirror-overlayscroll-vertical div { 64 | right: 0; 65 | width: 100%; 66 | } 67 | -------------------------------------------------------------------------------- /public/codemirror/addon/search/jump-to-line.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | // Defines jumpToLine command. Uses dialog.js if present. 5 | 6 | (function(mod) { 7 | if (typeof exports == "object" && typeof module == "object") // CommonJS 8 | mod(require("../../lib/codemirror"), require("../dialog/dialog")); 9 | else if (typeof define == "function" && define.amd) // AMD 10 | define(["../../lib/codemirror", "../dialog/dialog"], mod); 11 | else // Plain browser env 12 | mod(CodeMirror); 13 | })(function(CodeMirror) { 14 | "use strict"; 15 | 16 | function dialog(cm, text, shortText, deflt, f) { 17 | if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true}); 18 | else f(prompt(shortText, deflt)); 19 | } 20 | 21 | var jumpDialog = 22 | 'Jump to line: (Use line:column or scroll% syntax)'; 23 | 24 | function interpretLine(cm, string) { 25 | var num = Number(string) 26 | if (/^[-+]/.test(string)) return cm.getCursor().line + num 27 | else return num - 1 28 | } 29 | 30 | CodeMirror.commands.jumpToLine = function(cm) { 31 | var cur = cm.getCursor(); 32 | dialog(cm, jumpDialog, "Jump to line:", (cur.line + 1) + ":" + cur.ch, function(posStr) { 33 | if (!posStr) return; 34 | 35 | var match; 36 | if (match = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr)) { 37 | cm.setCursor(interpretLine(cm, match[1]), Number(match[2])) 38 | } else if (match = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr)) { 39 | var line = Math.round(cm.lineCount() * Number(match[1]) / 100); 40 | if (/^[-+]/.test(match[1])) line = cur.line + line + 1; 41 | cm.setCursor(line - 1, cur.ch); 42 | } else if (match = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr)) { 43 | cm.setCursor(interpretLine(cm, match[1]), cur.ch); 44 | } 45 | }); 46 | }; 47 | 48 | CodeMirror.keyMap["default"]["Alt-G"] = "jumpToLine"; 49 | }); 50 | -------------------------------------------------------------------------------- /public/codemirror/addon/search/matchesonscrollbar.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-search-match { 2 | background: gold; 3 | border-top: 1px solid orange; 4 | border-bottom: 1px solid orange; 5 | -moz-box-sizing: border-box; 6 | box-sizing: border-box; 7 | opacity: .5; 8 | } 9 | -------------------------------------------------------------------------------- /public/codemirror/addon/selection/active-line.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | var WRAP_CLASS = "CodeMirror-activeline"; 14 | var BACK_CLASS = "CodeMirror-activeline-background"; 15 | var GUTT_CLASS = "CodeMirror-activeline-gutter"; 16 | 17 | CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { 18 | var prev = old == CodeMirror.Init ? false : old; 19 | if (val == prev) return 20 | if (prev) { 21 | cm.off("beforeSelectionChange", selectionChange); 22 | clearActiveLines(cm); 23 | delete cm.state.activeLines; 24 | } 25 | if (val) { 26 | cm.state.activeLines = []; 27 | updateActiveLines(cm, cm.listSelections()); 28 | cm.on("beforeSelectionChange", selectionChange); 29 | } 30 | }); 31 | 32 | function clearActiveLines(cm) { 33 | for (var i = 0; i < cm.state.activeLines.length; i++) { 34 | cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); 35 | cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); 36 | cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); 37 | } 38 | } 39 | 40 | function sameArray(a, b) { 41 | if (a.length != b.length) return false; 42 | for (var i = 0; i < a.length; i++) 43 | if (a[i] != b[i]) return false; 44 | return true; 45 | } 46 | 47 | function updateActiveLines(cm, ranges) { 48 | var active = []; 49 | for (var i = 0; i < ranges.length; i++) { 50 | var range = ranges[i]; 51 | var option = cm.getOption("styleActiveLine"); 52 | if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) 53 | continue 54 | var line = cm.getLineHandleVisualStart(range.head.line); 55 | if (active[active.length - 1] != line) active.push(line); 56 | } 57 | if (sameArray(cm.state.activeLines, active)) return; 58 | cm.operation(function() { 59 | clearActiveLines(cm); 60 | for (var i = 0; i < active.length; i++) { 61 | cm.addLineClass(active[i], "wrap", WRAP_CLASS); 62 | cm.addLineClass(active[i], "background", BACK_CLASS); 63 | cm.addLineClass(active[i], "gutter", GUTT_CLASS); 64 | } 65 | cm.state.activeLines = active; 66 | }); 67 | } 68 | 69 | function selectionChange(cm, sel) { 70 | updateActiveLines(cm, sel.ranges); 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /public/codemirror/addon/tern/tern.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-Tern-completion { 2 | padding-left: 22px; 3 | position: relative; 4 | line-height: 1.5; 5 | } 6 | .CodeMirror-Tern-completion:before { 7 | position: absolute; 8 | left: 2px; 9 | bottom: 2px; 10 | border-radius: 50%; 11 | font-size: 12px; 12 | font-weight: bold; 13 | height: 15px; 14 | width: 15px; 15 | line-height: 16px; 16 | text-align: center; 17 | color: white; 18 | -moz-box-sizing: border-box; 19 | box-sizing: border-box; 20 | } 21 | .CodeMirror-Tern-completion-unknown:before { 22 | content: "?"; 23 | background: #4bb; 24 | } 25 | .CodeMirror-Tern-completion-object:before { 26 | content: "O"; 27 | background: #77c; 28 | } 29 | .CodeMirror-Tern-completion-fn:before { 30 | content: "F"; 31 | background: #7c7; 32 | } 33 | .CodeMirror-Tern-completion-array:before { 34 | content: "A"; 35 | background: #c66; 36 | } 37 | .CodeMirror-Tern-completion-number:before { 38 | content: "1"; 39 | background: #999; 40 | } 41 | .CodeMirror-Tern-completion-string:before { 42 | content: "S"; 43 | background: #999; 44 | } 45 | .CodeMirror-Tern-completion-bool:before { 46 | content: "B"; 47 | background: #999; 48 | } 49 | 50 | .CodeMirror-Tern-completion-guess { 51 | color: #999; 52 | } 53 | 54 | .CodeMirror-Tern-tooltip { 55 | border: 1px solid silver; 56 | border-radius: 3px; 57 | color: #444; 58 | padding: 2px 5px; 59 | font-size: 90%; 60 | font-family: monospace; 61 | background-color: white; 62 | white-space: pre-wrap; 63 | 64 | max-width: 40em; 65 | position: absolute; 66 | z-index: 10; 67 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 68 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 69 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 70 | 71 | transition: opacity 1s; 72 | -moz-transition: opacity 1s; 73 | -webkit-transition: opacity 1s; 74 | -o-transition: opacity 1s; 75 | -ms-transition: opacity 1s; 76 | } 77 | 78 | .CodeMirror-Tern-hint-doc { 79 | max-width: 25em; 80 | margin-top: -3px; 81 | } 82 | 83 | .CodeMirror-Tern-fname { color: black; } 84 | .CodeMirror-Tern-farg { color: #70a; } 85 | .CodeMirror-Tern-farg-current { text-decoration: underline; } 86 | .CodeMirror-Tern-type { color: #07c; } 87 | .CodeMirror-Tern-fhint-guess { opacity: .7; } 88 | -------------------------------------------------------------------------------- /public/codemirror/addon/tern/worker.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | // declare global: tern, server 5 | 6 | var server; 7 | 8 | this.onmessage = function(e) { 9 | var data = e.data; 10 | switch (data.type) { 11 | case "init": return startServer(data.defs, data.plugins, data.scripts); 12 | case "add": return server.addFile(data.name, data.text); 13 | case "del": return server.delFile(data.name); 14 | case "req": return server.request(data.body, function(err, reqData) { 15 | postMessage({id: data.id, body: reqData, err: err && String(err)}); 16 | }); 17 | case "getFile": 18 | var c = pending[data.id]; 19 | delete pending[data.id]; 20 | return c(data.err, data.text); 21 | default: throw new Error("Unknown message type: " + data.type); 22 | } 23 | }; 24 | 25 | var nextId = 0, pending = {}; 26 | function getFile(file, c) { 27 | postMessage({type: "getFile", name: file, id: ++nextId}); 28 | pending[nextId] = c; 29 | } 30 | 31 | function startServer(defs, plugins, scripts) { 32 | if (scripts) importScripts.apply(null, scripts); 33 | 34 | server = new tern.Server({ 35 | getFile: getFile, 36 | async: true, 37 | defs: defs, 38 | plugins: plugins 39 | }); 40 | } 41 | 42 | this.console = { 43 | log: function(v) { postMessage({type: "debug", message: v}); } 44 | }; 45 | -------------------------------------------------------------------------------- /public/codemirror/bin/authors.sh: -------------------------------------------------------------------------------- 1 | # Combine existing list of authors with everyone known in git, sort, add header. 2 | tail --lines=+3 AUTHORS > AUTHORS.tmp 3 | git log --format='%aN' | grep -v "Piët Delport" >> AUTHORS.tmp 4 | echo -e "List of CodeMirror contributors. Updated before every release.\n" > AUTHORS 5 | sort -u AUTHORS.tmp >> AUTHORS 6 | rm -f AUTHORS.tmp 7 | -------------------------------------------------------------------------------- /public/codemirror/bin/lint: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | process.exit(require("../test/lint").ok ? 0 : 1); 4 | -------------------------------------------------------------------------------- /public/codemirror/bin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require("fs"), child = require("child_process"); 4 | 5 | var number, bumpOnly; 6 | 7 | for (var i = 2; i < process.argv.length; i++) { 8 | if (process.argv[i] == "-bump") bumpOnly = true; 9 | else if (/^\d+\.\d+\.\d+$/.test(process.argv[i])) number = process.argv[i]; 10 | else { console.log("Bogus command line arg: " + process.argv[i]); process.exit(1); } 11 | } 12 | 13 | if (!number) { console.log("Must give a version"); process.exit(1); } 14 | 15 | function rewrite(file, f) { 16 | fs.writeFileSync(file, f(fs.readFileSync(file, "utf8")), "utf8"); 17 | } 18 | 19 | rewrite("src/edit/main.js", function(lib) { 20 | return lib.replace(/CodeMirror\.version = "\d+\.\d+\.\d+"/, 21 | "CodeMirror.version = \"" + number + "\""); 22 | }); 23 | function rewriteJSON(pack) { 24 | return pack.replace(/"version":\s*"\d+\.\d+\.\d+"/, "\"version\": \"" + number + "\""); 25 | } 26 | rewrite("package.json", rewriteJSON); 27 | rewrite("doc/manual.html", function(manual) { 28 | return manual.replace(/>version \d+\.\d+\.\d+<\/span>/, ">version " + number + ""); 29 | }); 30 | 31 | if (bumpOnly) process.exit(0); 32 | 33 | child.exec("bash bin/authors.sh", function(){}); 34 | 35 | rewrite("index.html", function(index) { 36 | return index.replace(/\.zip">\d+\.\d+\.\d+<\/a>/, 37 | ".zip\">" + number + ""); 38 | }); 39 | -------------------------------------------------------------------------------- /public/codemirror/bin/source-highlight: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Simple command-line code highlighting tool. Reads code from stdin, 4 | // spits html to stdout. For example: 5 | // 6 | // echo 'function foo(a) { return a; }' | bin/source-highlight -s javascript 7 | // bin/source-highlight -s 8 | 9 | var fs = require("fs"); 10 | 11 | var CodeMirror = require("../addon/runmode/runmode.node.js"); 12 | require("../mode/meta.js"); 13 | 14 | var sPos = process.argv.indexOf("-s"); 15 | if (sPos == -1 || sPos == process.argv.length - 1) { 16 | console.error("Usage: source-highlight -s language"); 17 | process.exit(1); 18 | } 19 | var lang = process.argv[sPos + 1].toLowerCase(), modeName = lang; 20 | var found = CodeMirror.findModeByMIME(lang) || CodeMirror.findModeByName(lang) 21 | if (found) { 22 | modeName = found.mode 23 | lang = found.mime 24 | } 25 | 26 | if (!CodeMirror.modes[modeName]) 27 | require("../mode/" + modeName + "/" + modeName + ".js"); 28 | 29 | function esc(str) { 30 | return str.replace(/[<&]/g, function(ch) { return ch == "&" ? "&" : "<"; }); 31 | } 32 | 33 | var code = fs.readFileSync("/dev/stdin", "utf8"); 34 | var curStyle = null, accum = ""; 35 | function flush() { 36 | if (curStyle) process.stdout.write("" + esc(accum) + ""); 37 | else process.stdout.write(esc(accum)); 38 | } 39 | 40 | CodeMirror.runMode(code, lang, function(text, style) { 41 | if (style != curStyle) { 42 | flush(); 43 | curStyle = style; accum = text; 44 | } else { 45 | accum += text; 46 | } 47 | }); 48 | flush(); 49 | -------------------------------------------------------------------------------- /public/codemirror/bin/upload-release.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | let version = process.argv[2] 4 | let auth = process.argv[3] 5 | 6 | if (!auth) { 7 | console.log("Usage: upload-release.js [TAG] [github-user:password]") 8 | process.exit(1) 9 | } 10 | 11 | require('child_process').exec("git --no-pager show -s --format='%s' " + version, (error, stdout) => { 12 | if (error) throw error 13 | let message = stdout.split("\n").slice(2) 14 | message = message.slice(0, message.indexOf("-----BEGIN PGP SIGNATURE-----")).join("\n") 15 | 16 | let req = require("https").request({ 17 | host: "api.github.com", 18 | auth: auth, 19 | headers: {"user-agent": "Release uploader"}, 20 | path: "/repos/codemirror/codemirror/releases", 21 | method: "POST" 22 | }, res => { 23 | if (res.statusCode >= 300) { 24 | console.error(res.statusMessage) 25 | res.on("data", d => console.log(d.toString())) 26 | res.on("end", process.exit(1)) 27 | } 28 | }) 29 | req.write(JSON.stringify({ 30 | tag_name: version, 31 | name: version, 32 | body: message 33 | })) 34 | req.end() 35 | }) 36 | -------------------------------------------------------------------------------- /public/codemirror/doc/activebookmark.js: -------------------------------------------------------------------------------- 1 | // Kludge in HTML5 tag recognition in IE8 2 | document.createElement("section"); 3 | document.createElement("article"); 4 | 5 | (function() { 6 | if (!window.addEventListener) return; 7 | var pending = false, prevVal = null; 8 | 9 | function updateSoon() { 10 | if (!pending) { 11 | pending = true; 12 | setTimeout(update, 250); 13 | } 14 | } 15 | 16 | function update() { 17 | pending = false; 18 | var marks = document.getElementById("nav").getElementsByTagName("a"), found; 19 | for (var i = 0; i < marks.length; ++i) { 20 | var mark = marks[i], m; 21 | if (mark.getAttribute("data-default")) { 22 | if (found == null) found = i; 23 | } else if (m = mark.href.match(/#(.*)/)) { 24 | var ref = document.getElementById(m[1]); 25 | if (ref && ref.getBoundingClientRect().top < 50) 26 | found = i; 27 | } 28 | } 29 | if (found != null && found != prevVal) { 30 | prevVal = found; 31 | var lis = document.getElementById("nav").getElementsByTagName("li"); 32 | for (var i = 0; i < lis.length; ++i) lis[i].className = ""; 33 | for (var i = 0; i < marks.length; ++i) { 34 | if (found == i) { 35 | marks[i].className = "active"; 36 | for (var n = marks[i]; n; n = n.parentNode) 37 | if (n.nodeName == "LI") n.className = "active"; 38 | } else { 39 | marks[i].className = ""; 40 | } 41 | } 42 | } 43 | } 44 | 45 | window.addEventListener("scroll", updateSoon); 46 | window.addEventListener("load", updateSoon); 47 | window.addEventListener("hashchange", function() { 48 | setTimeout(function() { 49 | var hash = document.location.hash, found = null, m; 50 | var marks = document.getElementById("nav").getElementsByTagName("a"); 51 | for (var i = 0; i < marks.length; i++) 52 | if ((m = marks[i].href.match(/(#.*)/)) && m[1] == hash) { found = i; break; } 53 | if (found != null) for (var i = 0; i < marks.length; i++) 54 | marks[i].className = i == found ? "active" : ""; 55 | }, 300); 56 | }); 57 | })(); 58 | -------------------------------------------------------------------------------- /public/codemirror/doc/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/codemirror/doc/logo.png -------------------------------------------------------------------------------- /public/codemirror/doc/reporting.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | CodeMirror: Reporting Bugs 4 | 5 | 6 | 7 | 19 | 20 |
21 | 22 |

Reporting bugs effectively

23 | 24 |
25 | 26 |

So you found a problem in CodeMirror. By all means, report it! Bug 27 | reports from users are the main drive behind improvements to 28 | CodeMirror. But first, please read over these points:

29 | 30 |
    31 |
  1. CodeMirror is maintained by volunteers. They don't owe you 32 | anything, so be polite. Reports with an indignant or belligerent 33 | tone tend to be moved to the bottom of the pile.
  2. 34 | 35 |
  3. Include information about the browser in which the 36 | problem occurred. Even if you tested several browsers, and 37 | the problem occurred in all of them, mention this fact in the bug 38 | report. Also include browser version numbers and the operating 39 | system that you're on.
  4. 40 | 41 |
  5. Mention which release of CodeMirror you're using. Preferably, 42 | try also with the current development snapshot, to ensure the 43 | problem has not already been fixed.
  6. 44 | 45 |
  7. Mention very precisely what went wrong. "X is broken" is not a 46 | good bug report. What did you expect to happen? What happened 47 | instead? Describe the exact steps a maintainer has to take to reproduce 48 | the error. We can not fix something that we can not observe.
  8. 49 | 50 |
  9. If the problem can not be reproduced in any of the demos 51 | included in the CodeMirror distribution, please provide an HTML 52 | document that demonstrates the problem. The best way to do this is 53 | to go to jsbin.com, enter 54 | it there, press save, and include the resulting link in your bug 55 | report.
  10. 56 |
57 | 58 |
59 | 60 |
61 | -------------------------------------------------------------------------------- /public/codemirror/doc/yinyang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/codemirror/doc/yinyang.png -------------------------------------------------------------------------------- /public/codemirror/mode/Serpico/Serpico.css: -------------------------------------------------------------------------------- 1 | .cm-s-Serpico.CodeMirror { height: 600px; } 2 | .cm-s-Serpico span.cm-choose { color: #c3a1f8; } 3 | .cm-s-Serpico span.cm-loop { color: #ff80e1; } 4 | .cm-s-Serpico span.cm-keyword { color: #ffee80; } 5 | .cm-s-Serpico span.cm-string { color: #3ad900; } 6 | .cm-s-Serpico span.cm-condition { color: #9effff; } 7 | .cm-s-Serpico span.cm-row-loop { color: #ff9900; } 8 | .cm-s-Serpico span.cm-error { color: #e63a2e; } -------------------------------------------------------------------------------- /public/codemirror/mode/Serpico/Serpico.js: -------------------------------------------------------------------------------- 1 | (function(mod) { 2 | if (typeof exports == "object" && typeof module == "object") // CommonJS 3 | mod(require("../../lib/codemirror")); 4 | else if (typeof define == "function" && define.amd) // AMD 5 | define(["../../lib/codemirror"], mod); 6 | else // Plain browser env 7 | mod(CodeMirror); 8 | })(function(CodeMirror) { 9 | "use strict"; 10 | 11 | function function_regex (functions){ 12 | console.log("^((" + functions.join("(?=\\())|(") + "(?=\\()))") 13 | return new RegExp("^((" + functions.join("(?=\\())|(") + "(?=\\()))"); 14 | } 15 | 16 | var function_list = function_regex(["translate", "not", "contains", "text"]) 17 | 18 | CodeMirror.defineMode("serpico", function(){ 19 | return { 20 | token: function(stream) { 21 | if(stream.match(/^.*←$/)){ 22 | return "error"; 23 | } 24 | if(stream.match(function_list)){ 25 | return "keyword"; 26 | } 27 | var ch = stream.next().toString(); 28 | if(ch === "†" || ch === "¥"){ 29 | return "condition"; 30 | } 31 | if(ch === "¬" || ch === "∆"){ 32 | return "loop"; 33 | } 34 | if(ch === "µ" || ch === "ƒ" || ch === "÷" || ch === "å" || ch === "≠"){ 35 | return "choose"; 36 | } 37 | if(ch === "æ" || ch === "∞"){ 38 | return "row-loop"; 39 | } 40 | if(ch === "\"" || ch === "'"){ 41 | var regex = new RegExp("[^"+ch+"]") 42 | while (!stream.eol()) { 43 | //stream.eatWhile(/[^'"]/); 44 | stream.eatWhile(regex); 45 | console.log(stream.current()) 46 | //stream.eat(/['"]/); 47 | if(stream.eat(ch)){ 48 | return "string"; 49 | } 50 | else{ 51 | return "string error" 52 | } 53 | } 54 | } 55 | 56 | }, 57 | fold: "indent" 58 | } 59 | }); 60 | 61 | CodeMirror.defineMIME("text/x-serpico","serpico") 62 | }); -------------------------------------------------------------------------------- /public/codemirror/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codemirror", 3 | "version": "5.36.0", 4 | "main": "lib/codemirror.js", 5 | "style": "lib/codemirror.css", 6 | "description": "Full-featured in-browser code editor", 7 | "license": "MIT", 8 | "directories": { 9 | "lib": "./lib" 10 | }, 11 | "scripts": { 12 | "build": "rollup -c", 13 | "watch": "rollup -w -c", 14 | "prepare": "npm run-script build", 15 | "test": "node ./test/run.js", 16 | "lint": "bin/lint" 17 | }, 18 | "devDependencies": { 19 | "blint": "^1", 20 | "node-static": "0.6.0", 21 | "phantomjs-prebuilt": "^2.1.12", 22 | "rollup": "^0.41.0", 23 | "rollup-plugin-buble": "^0.15.0", 24 | "rollup-watch": "^3.2.0" 25 | }, 26 | "bugs": "http://github.com/codemirror/CodeMirror/issues", 27 | "keywords": [ 28 | "JavaScript", 29 | "CodeMirror", 30 | "Editor" 31 | ], 32 | "homepage": "http://codemirror.net", 33 | "maintainers": [ 34 | { 35 | "name": "Marijn Haverbeke", 36 | "email": "marijnh@gmail.com", 37 | "web": "http://marijnhaverbeke.nl" 38 | } 39 | ], 40 | "repository": { 41 | "type": "git", 42 | "url": "https://github.com/codemirror/CodeMirror.git" 43 | }, 44 | "jspm": { 45 | "directories": {}, 46 | "dependencies": {}, 47 | "devDependencies": {} 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /public/codemirror/rollup.config.js: -------------------------------------------------------------------------------- 1 | import buble from 'rollup-plugin-buble'; 2 | 3 | export default { 4 | banner: `// CodeMirror, copyright (c) by Marijn Haverbeke and others 5 | // Distributed under an MIT license: http://codemirror.net/LICENSE 6 | 7 | // This is CodeMirror (http://codemirror.net), a code editor 8 | // implemented in JavaScript on top of the browser's DOM. 9 | // 10 | // You can find some technical background for some of the code below 11 | // at http://marijnhaverbeke.nl/blog/#cm-internals . 12 | `, 13 | entry: "src/codemirror.js", 14 | format: "umd", 15 | dest: "lib/codemirror.js", 16 | moduleName: "CodeMirror", 17 | plugins: [ buble({namedFunctionExpressions: false}) ] 18 | }; 19 | -------------------------------------------------------------------------------- /public/codemirror/src/codemirror.js: -------------------------------------------------------------------------------- 1 | import { CodeMirror } from "./edit/main.js" 2 | 3 | export default CodeMirror 4 | -------------------------------------------------------------------------------- /public/codemirror/src/display/focus.js: -------------------------------------------------------------------------------- 1 | import { restartBlink } from "./selection.js" 2 | import { webkit } from "../util/browser.js" 3 | import { addClass, rmClass } from "../util/dom.js" 4 | import { signal } from "../util/event.js" 5 | 6 | export function ensureFocus(cm) { 7 | if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) } 8 | } 9 | 10 | export function delayBlurEvent(cm) { 11 | cm.state.delayingBlurEvent = true 12 | setTimeout(() => { if (cm.state.delayingBlurEvent) { 13 | cm.state.delayingBlurEvent = false 14 | onBlur(cm) 15 | } }, 100) 16 | } 17 | 18 | export function onFocus(cm, e) { 19 | if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false 20 | 21 | if (cm.options.readOnly == "nocursor") return 22 | if (!cm.state.focused) { 23 | signal(cm, "focus", cm, e) 24 | cm.state.focused = true 25 | addClass(cm.display.wrapper, "CodeMirror-focused") 26 | // This test prevents this from firing when a context 27 | // menu is closed (since the input reset would kill the 28 | // select-all detection hack) 29 | if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { 30 | cm.display.input.reset() 31 | if (webkit) setTimeout(() => cm.display.input.reset(true), 20) // Issue #1730 32 | } 33 | cm.display.input.receivedFocus() 34 | } 35 | restartBlink(cm) 36 | } 37 | export function onBlur(cm, e) { 38 | if (cm.state.delayingBlurEvent) return 39 | 40 | if (cm.state.focused) { 41 | signal(cm, "blur", cm, e) 42 | cm.state.focused = false 43 | rmClass(cm.display.wrapper, "CodeMirror-focused") 44 | } 45 | clearInterval(cm.display.blinker) 46 | setTimeout(() => { if (!cm.state.focused) cm.display.shift = false }, 150) 47 | } 48 | -------------------------------------------------------------------------------- /public/codemirror/src/display/gutters.js: -------------------------------------------------------------------------------- 1 | import { elt, removeChildren } from "../util/dom.js" 2 | import { indexOf } from "../util/misc.js" 3 | 4 | import { updateGutterSpace } from "./update_display.js" 5 | 6 | // Rebuild the gutter elements, ensure the margin to the left of the 7 | // code matches their width. 8 | export function updateGutters(cm) { 9 | let gutters = cm.display.gutters, specs = cm.options.gutters 10 | removeChildren(gutters) 11 | let i = 0 12 | for (; i < specs.length; ++i) { 13 | let gutterClass = specs[i] 14 | let gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)) 15 | if (gutterClass == "CodeMirror-linenumbers") { 16 | cm.display.lineGutter = gElt 17 | gElt.style.width = (cm.display.lineNumWidth || 1) + "px" 18 | } 19 | } 20 | gutters.style.display = i ? "" : "none" 21 | updateGutterSpace(cm) 22 | } 23 | 24 | // Make sure the gutters options contains the element 25 | // "CodeMirror-linenumbers" when the lineNumbers option is true. 26 | export function setGuttersForLineNumbers(options) { 27 | let found = indexOf(options.gutters, "CodeMirror-linenumbers") 28 | if (found == -1 && options.lineNumbers) { 29 | options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]) 30 | } else if (found > -1 && !options.lineNumbers) { 31 | options.gutters = options.gutters.slice(0) 32 | options.gutters.splice(found, 1) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /public/codemirror/src/display/highlight_worker.js: -------------------------------------------------------------------------------- 1 | import { getContextBefore, highlightLine, processLine } from "../line/highlight.js" 2 | import { copyState } from "../modes.js" 3 | import { bind } from "../util/misc.js" 4 | 5 | import { runInOp } from "./operations.js" 6 | import { regLineChange } from "./view_tracking.js" 7 | 8 | // HIGHLIGHT WORKER 9 | 10 | export function startWorker(cm, time) { 11 | if (cm.doc.highlightFrontier < cm.display.viewTo) 12 | cm.state.highlight.set(time, bind(highlightWorker, cm)) 13 | } 14 | 15 | function highlightWorker(cm) { 16 | let doc = cm.doc 17 | if (doc.highlightFrontier >= cm.display.viewTo) return 18 | let end = +new Date + cm.options.workTime 19 | let context = getContextBefore(cm, doc.highlightFrontier) 20 | let changedLines = [] 21 | 22 | doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), line => { 23 | if (context.line >= cm.display.viewFrom) { // Visible 24 | let oldStyles = line.styles 25 | let resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null 26 | let highlighted = highlightLine(cm, line, context, true) 27 | if (resetState) context.state = resetState 28 | line.styles = highlighted.styles 29 | let oldCls = line.styleClasses, newCls = highlighted.classes 30 | if (newCls) line.styleClasses = newCls 31 | else if (oldCls) line.styleClasses = null 32 | let ischange = !oldStyles || oldStyles.length != line.styles.length || 33 | oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass) 34 | for (let i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i] 35 | if (ischange) changedLines.push(context.line) 36 | line.stateAfter = context.save() 37 | context.nextLine() 38 | } else { 39 | if (line.text.length <= cm.options.maxHighlightLength) 40 | processLine(cm, line.text, context) 41 | line.stateAfter = context.line % 5 == 0 ? context.save() : null 42 | context.nextLine() 43 | } 44 | if (+new Date > end) { 45 | startWorker(cm, cm.options.workDelay) 46 | return true 47 | } 48 | }) 49 | doc.highlightFrontier = context.line 50 | doc.modeFrontier = Math.max(doc.modeFrontier, context.line) 51 | if (changedLines.length) runInOp(cm, () => { 52 | for (let i = 0; i < changedLines.length; i++) 53 | regLineChange(cm, changedLines[i], "text") 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /public/codemirror/src/display/line_numbers.js: -------------------------------------------------------------------------------- 1 | import { lineNumberFor } from "../line/utils_line.js" 2 | import { compensateForHScroll } from "../measurement/position_measurement.js" 3 | import { elt } from "../util/dom.js" 4 | 5 | import { updateGutterSpace } from "./update_display.js" 6 | 7 | // Re-align line numbers and gutter marks to compensate for 8 | // horizontal scrolling. 9 | export function alignHorizontally(cm) { 10 | let display = cm.display, view = display.view 11 | if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return 12 | let comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft 13 | let gutterW = display.gutters.offsetWidth, left = comp + "px" 14 | for (let i = 0; i < view.length; i++) if (!view[i].hidden) { 15 | if (cm.options.fixedGutter) { 16 | if (view[i].gutter) 17 | view[i].gutter.style.left = left 18 | if (view[i].gutterBackground) 19 | view[i].gutterBackground.style.left = left 20 | } 21 | let align = view[i].alignable 22 | if (align) for (let j = 0; j < align.length; j++) 23 | align[j].style.left = left 24 | } 25 | if (cm.options.fixedGutter) 26 | display.gutters.style.left = (comp + gutterW) + "px" 27 | } 28 | 29 | // Used to ensure that the line number gutter is still the right 30 | // size for the current document size. Returns true when an update 31 | // is needed. 32 | export function maybeUpdateLineNumberWidth(cm) { 33 | if (!cm.options.lineNumbers) return false 34 | let doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display 35 | if (last.length != display.lineNumChars) { 36 | let test = display.measure.appendChild(elt("div", [elt("div", last)], 37 | "CodeMirror-linenumber CodeMirror-gutter-elt")) 38 | let innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW 39 | display.lineGutter.style.width = "" 40 | display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1 41 | display.lineNumWidth = display.lineNumInnerWidth + padding 42 | display.lineNumChars = display.lineNumInnerWidth ? last.length : -1 43 | display.lineGutter.style.width = display.lineNumWidth + "px" 44 | updateGutterSpace(cm) 45 | return true 46 | } 47 | return false 48 | } 49 | -------------------------------------------------------------------------------- /public/codemirror/src/display/mode_state.js: -------------------------------------------------------------------------------- 1 | import { getMode } from "../modes.js" 2 | 3 | import { startWorker } from "./highlight_worker.js" 4 | import { regChange } from "./view_tracking.js" 5 | 6 | // Used to get the editor into a consistent state again when options change. 7 | 8 | export function loadMode(cm) { 9 | cm.doc.mode = getMode(cm.options, cm.doc.modeOption) 10 | resetModeState(cm) 11 | } 12 | 13 | export function resetModeState(cm) { 14 | cm.doc.iter(line => { 15 | if (line.stateAfter) line.stateAfter = null 16 | if (line.styles) line.styles = null 17 | }) 18 | cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first 19 | startWorker(cm, 100) 20 | cm.state.modeGen++ 21 | if (cm.curOp) regChange(cm) 22 | } 23 | -------------------------------------------------------------------------------- /public/codemirror/src/display/update_lines.js: -------------------------------------------------------------------------------- 1 | import { heightAtLine } from "../line/spans.js" 2 | import { getLine, lineAtHeight, updateLineHeight } from "../line/utils_line.js" 3 | import { paddingTop, textHeight } from "../measurement/position_measurement.js" 4 | import { ie, ie_version } from "../util/browser.js" 5 | 6 | // Read the actual heights of the rendered lines, and update their 7 | // stored heights to match. 8 | export function updateHeightsInViewport(cm) { 9 | let display = cm.display 10 | let prevBottom = display.lineDiv.offsetTop 11 | for (let i = 0; i < display.view.length; i++) { 12 | let cur = display.view[i], height 13 | if (cur.hidden) continue 14 | if (ie && ie_version < 8) { 15 | let bot = cur.node.offsetTop + cur.node.offsetHeight 16 | height = bot - prevBottom 17 | prevBottom = bot 18 | } else { 19 | let box = cur.node.getBoundingClientRect() 20 | height = box.bottom - box.top 21 | } 22 | let diff = cur.line.height - height 23 | if (height < 2) height = textHeight(display) 24 | if (diff > .005 || diff < -.005) { 25 | updateLineHeight(cur.line, height) 26 | updateWidgetHeight(cur.line) 27 | if (cur.rest) for (let j = 0; j < cur.rest.length; j++) 28 | updateWidgetHeight(cur.rest[j]) 29 | } 30 | } 31 | } 32 | 33 | // Read and store the height of line widgets associated with the 34 | // given line. 35 | function updateWidgetHeight(line) { 36 | if (line.widgets) for (let i = 0; i < line.widgets.length; ++i) { 37 | let w = line.widgets[i], parent = w.node.parentNode 38 | if (parent) w.height = parent.offsetHeight 39 | } 40 | } 41 | 42 | // Compute the lines that are visible in a given viewport (defaults 43 | // the the current scroll position). viewport may contain top, 44 | // height, and ensure (see op.scrollToPos) properties. 45 | export function visibleLines(display, doc, viewport) { 46 | let top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop 47 | top = Math.floor(top - paddingTop(display)) 48 | let bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight 49 | 50 | let from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom) 51 | // Ensure is a {from: {line, ch}, to: {line, ch}} object, and 52 | // forces those lines into the viewport (if possible). 53 | if (viewport && viewport.ensure) { 54 | let ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line 55 | if (ensureFrom < from) { 56 | from = ensureFrom 57 | to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight) 58 | } else if (Math.min(ensureTo, doc.lastLine()) >= to) { 59 | from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight) 60 | to = ensureTo 61 | } 62 | } 63 | return {from: from, to: Math.max(to, from + 1)} 64 | } 65 | -------------------------------------------------------------------------------- /public/codemirror/src/edit/deleteNearSelection.js: -------------------------------------------------------------------------------- 1 | import { runInOp } from "../display/operations.js" 2 | import { ensureCursorVisible } from "../display/scrolling.js" 3 | import { cmp } from "../line/pos.js" 4 | import { replaceRange } from "../model/changes.js" 5 | import { lst } from "../util/misc.js" 6 | 7 | // Helper for deleting text near the selection(s), used to implement 8 | // backspace, delete, and similar functionality. 9 | export function deleteNearSelection(cm, compute) { 10 | let ranges = cm.doc.sel.ranges, kill = [] 11 | // Build up a set of ranges to kill first, merging overlapping 12 | // ranges. 13 | for (let i = 0; i < ranges.length; i++) { 14 | let toKill = compute(ranges[i]) 15 | while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { 16 | let replaced = kill.pop() 17 | if (cmp(replaced.from, toKill.from) < 0) { 18 | toKill.from = replaced.from 19 | break 20 | } 21 | } 22 | kill.push(toKill) 23 | } 24 | // Next, remove those actual ranges. 25 | runInOp(cm, () => { 26 | for (let i = kill.length - 1; i >= 0; i--) 27 | replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") 28 | ensureCursorVisible(cm) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /public/codemirror/src/edit/fromTextArea.js: -------------------------------------------------------------------------------- 1 | import { CodeMirror } from "./CodeMirror.js" 2 | import { activeElt } from "../util/dom.js" 3 | import { off, on } from "../util/event.js" 4 | import { copyObj } from "../util/misc.js" 5 | 6 | export function fromTextArea(textarea, options) { 7 | options = options ? copyObj(options) : {} 8 | options.value = textarea.value 9 | if (!options.tabindex && textarea.tabIndex) 10 | options.tabindex = textarea.tabIndex 11 | if (!options.placeholder && textarea.placeholder) 12 | options.placeholder = textarea.placeholder 13 | // Set autofocus to true if this textarea is focused, or if it has 14 | // autofocus and no other element is focused. 15 | if (options.autofocus == null) { 16 | let hasFocus = activeElt() 17 | options.autofocus = hasFocus == textarea || 18 | textarea.getAttribute("autofocus") != null && hasFocus == document.body 19 | } 20 | 21 | function save() {textarea.value = cm.getValue()} 22 | 23 | let realSubmit 24 | if (textarea.form) { 25 | on(textarea.form, "submit", save) 26 | // Deplorable hack to make the submit method do the right thing. 27 | if (!options.leaveSubmitMethodAlone) { 28 | let form = textarea.form 29 | realSubmit = form.submit 30 | try { 31 | let wrappedSubmit = form.submit = () => { 32 | save() 33 | form.submit = realSubmit 34 | form.submit() 35 | form.submit = wrappedSubmit 36 | } 37 | } catch(e) {} 38 | } 39 | } 40 | 41 | options.finishInit = cm => { 42 | cm.save = save 43 | cm.getTextArea = () => textarea 44 | cm.toTextArea = () => { 45 | cm.toTextArea = isNaN // Prevent this from being ran twice 46 | save() 47 | textarea.parentNode.removeChild(cm.getWrapperElement()) 48 | textarea.style.display = "" 49 | if (textarea.form) { 50 | off(textarea.form, "submit", save) 51 | if (typeof textarea.form.submit == "function") 52 | textarea.form.submit = realSubmit 53 | } 54 | } 55 | } 56 | 57 | textarea.style.display = "none" 58 | let cm = CodeMirror(node => textarea.parentNode.insertBefore(node, textarea.nextSibling), 59 | options) 60 | return cm 61 | } 62 | -------------------------------------------------------------------------------- /public/codemirror/src/edit/global_events.js: -------------------------------------------------------------------------------- 1 | import { onBlur } from "../display/focus.js" 2 | import { on } from "../util/event.js" 3 | 4 | // These must be handled carefully, because naively registering a 5 | // handler for each editor will cause the editors to never be 6 | // garbage collected. 7 | 8 | function forEachCodeMirror(f) { 9 | if (!document.getElementsByClassName) return 10 | let byClass = document.getElementsByClassName("CodeMirror") 11 | for (let i = 0; i < byClass.length; i++) { 12 | let cm = byClass[i].CodeMirror 13 | if (cm) f(cm) 14 | } 15 | } 16 | 17 | let globalsRegistered = false 18 | export function ensureGlobalHandlers() { 19 | if (globalsRegistered) return 20 | registerGlobalHandlers() 21 | globalsRegistered = true 22 | } 23 | function registerGlobalHandlers() { 24 | // When the window resizes, we need to refresh active editors. 25 | let resizeTimer 26 | on(window, "resize", () => { 27 | if (resizeTimer == null) resizeTimer = setTimeout(() => { 28 | resizeTimer = null 29 | forEachCodeMirror(onResize) 30 | }, 100) 31 | }) 32 | // When the window loses focus, we want to show the editor as blurred 33 | on(window, "blur", () => forEachCodeMirror(onBlur)) 34 | } 35 | // Called when the window resizes 36 | function onResize(cm) { 37 | let d = cm.display 38 | if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth) 39 | return 40 | // Might be a text scaling operation, clear size caches. 41 | d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null 42 | d.scrollbarsClipped = false 43 | cm.setSize() 44 | } 45 | -------------------------------------------------------------------------------- /public/codemirror/src/edit/legacy.js: -------------------------------------------------------------------------------- 1 | import { scrollbarModel } from "../display/scrollbars.js" 2 | import { wheelEventPixels } from "../display/scroll_events.js" 3 | import { keyMap, keyName, isModifierKey, lookupKey, normalizeKeyMap } from "../input/keymap.js" 4 | import { keyNames } from "../input/keynames.js" 5 | import { Line } from "../line/line_data.js" 6 | import { cmp, Pos } from "../line/pos.js" 7 | import { changeEnd } from "../model/change_measurement.js" 8 | import Doc from "../model/Doc.js" 9 | import { LineWidget } from "../model/line_widget.js" 10 | import { SharedTextMarker, TextMarker } from "../model/mark_text.js" 11 | import { copyState, extendMode, getMode, innerMode, mimeModes, modeExtensions, modes, resolveMode, startState } from "../modes.js" 12 | import { addClass, contains, rmClass } from "../util/dom.js" 13 | import { e_preventDefault, e_stop, e_stopPropagation, off, on, signal } from "../util/event.js" 14 | import { splitLinesAuto } from "../util/feature_detection.js" 15 | import { countColumn, findColumn, isWordCharBasic, Pass } from "../util/misc.js" 16 | import StringStream from "../util/StringStream.js" 17 | 18 | import { commands } from "./commands.js" 19 | 20 | export function addLegacyProps(CodeMirror) { 21 | CodeMirror.off = off 22 | CodeMirror.on = on 23 | CodeMirror.wheelEventPixels = wheelEventPixels 24 | CodeMirror.Doc = Doc 25 | CodeMirror.splitLines = splitLinesAuto 26 | CodeMirror.countColumn = countColumn 27 | CodeMirror.findColumn = findColumn 28 | CodeMirror.isWordChar = isWordCharBasic 29 | CodeMirror.Pass = Pass 30 | CodeMirror.signal = signal 31 | CodeMirror.Line = Line 32 | CodeMirror.changeEnd = changeEnd 33 | CodeMirror.scrollbarModel = scrollbarModel 34 | CodeMirror.Pos = Pos 35 | CodeMirror.cmpPos = cmp 36 | CodeMirror.modes = modes 37 | CodeMirror.mimeModes = mimeModes 38 | CodeMirror.resolveMode = resolveMode 39 | CodeMirror.getMode = getMode 40 | CodeMirror.modeExtensions = modeExtensions 41 | CodeMirror.extendMode = extendMode 42 | CodeMirror.copyState = copyState 43 | CodeMirror.startState = startState 44 | CodeMirror.innerMode = innerMode 45 | CodeMirror.commands = commands 46 | CodeMirror.keyMap = keyMap 47 | CodeMirror.keyName = keyName 48 | CodeMirror.isModifierKey = isModifierKey 49 | CodeMirror.lookupKey = lookupKey 50 | CodeMirror.normalizeKeyMap = normalizeKeyMap 51 | CodeMirror.StringStream = StringStream 52 | CodeMirror.SharedTextMarker = SharedTextMarker 53 | CodeMirror.TextMarker = TextMarker 54 | CodeMirror.LineWidget = LineWidget 55 | CodeMirror.e_preventDefault = e_preventDefault 56 | CodeMirror.e_stopPropagation = e_stopPropagation 57 | CodeMirror.e_stop = e_stop 58 | CodeMirror.addClass = addClass 59 | CodeMirror.contains = contains 60 | CodeMirror.rmClass = rmClass 61 | CodeMirror.keyNames = keyNames 62 | } 63 | -------------------------------------------------------------------------------- /public/codemirror/src/edit/main.js: -------------------------------------------------------------------------------- 1 | // EDITOR CONSTRUCTOR 2 | 3 | import { CodeMirror } from "./CodeMirror.js" 4 | export { CodeMirror } from "./CodeMirror.js" 5 | 6 | import { eventMixin } from "../util/event.js" 7 | import { indexOf } from "../util/misc.js" 8 | 9 | import { defineOptions } from "./options.js" 10 | 11 | defineOptions(CodeMirror) 12 | 13 | import addEditorMethods from "./methods.js" 14 | 15 | addEditorMethods(CodeMirror) 16 | 17 | import Doc from "../model/Doc.js" 18 | 19 | // Set up methods on CodeMirror's prototype to redirect to the editor's document. 20 | let dontDelegate = "iter insert remove copy getEditor constructor".split(" ") 21 | for (let prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) 22 | CodeMirror.prototype[prop] = (function(method) { 23 | return function() {return method.apply(this.doc, arguments)} 24 | })(Doc.prototype[prop]) 25 | 26 | eventMixin(Doc) 27 | 28 | // INPUT HANDLING 29 | 30 | import ContentEditableInput from "../input/ContentEditableInput.js" 31 | import TextareaInput from "../input/TextareaInput.js" 32 | CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput} 33 | 34 | // MODE DEFINITION AND QUERYING 35 | 36 | import { defineMIME, defineMode } from "../modes.js" 37 | 38 | // Extra arguments are stored as the mode's dependencies, which is 39 | // used by (legacy) mechanisms like loadmode.js to automatically 40 | // load a mode. (Preferred mechanism is the require/define calls.) 41 | CodeMirror.defineMode = function(name/*, mode, …*/) { 42 | if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name 43 | defineMode.apply(this, arguments) 44 | } 45 | 46 | CodeMirror.defineMIME = defineMIME 47 | 48 | // Minimal default mode. 49 | CodeMirror.defineMode("null", () => ({token: stream => stream.skipToEnd()})) 50 | CodeMirror.defineMIME("text/plain", "null") 51 | 52 | // EXTENSIONS 53 | 54 | CodeMirror.defineExtension = (name, func) => { 55 | CodeMirror.prototype[name] = func 56 | } 57 | CodeMirror.defineDocExtension = (name, func) => { 58 | Doc.prototype[name] = func 59 | } 60 | 61 | import { fromTextArea } from "./fromTextArea.js" 62 | 63 | CodeMirror.fromTextArea = fromTextArea 64 | 65 | import { addLegacyProps } from "./legacy.js" 66 | 67 | addLegacyProps(CodeMirror) 68 | 69 | CodeMirror.version = "5.36.0" 70 | -------------------------------------------------------------------------------- /public/codemirror/src/edit/utils.js: -------------------------------------------------------------------------------- 1 | import { clearCaches } from "../measurement/position_measurement.js" 2 | 3 | export function themeChanged(cm) { 4 | cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + 5 | cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-") 6 | clearCaches(cm) 7 | } 8 | -------------------------------------------------------------------------------- /public/codemirror/src/input/indent.js: -------------------------------------------------------------------------------- 1 | import { getContextBefore } from "../line/highlight.js" 2 | import { Pos } from "../line/pos.js" 3 | import { getLine } from "../line/utils_line.js" 4 | import { replaceRange } from "../model/changes.js" 5 | import { Range } from "../model/selection.js" 6 | import { replaceOneSelection } from "../model/selection_updates.js" 7 | import { countColumn, Pass, spaceStr } from "../util/misc.js" 8 | 9 | // Indent the given line. The how parameter can be "smart", 10 | // "add"/null, "subtract", or "prev". When aggressive is false 11 | // (typically set to true for forced single-line indents), empty 12 | // lines are not indented, and places where the mode returns Pass 13 | // are left alone. 14 | export function indentLine(cm, n, how, aggressive) { 15 | let doc = cm.doc, state 16 | if (how == null) how = "add" 17 | if (how == "smart") { 18 | // Fall back to "prev" when the mode doesn't have an indentation 19 | // method. 20 | if (!doc.mode.indent) how = "prev" 21 | else state = getContextBefore(cm, n).state 22 | } 23 | 24 | let tabSize = cm.options.tabSize 25 | let line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize) 26 | if (line.stateAfter) line.stateAfter = null 27 | let curSpaceString = line.text.match(/^\s*/)[0], indentation 28 | if (!aggressive && !/\S/.test(line.text)) { 29 | indentation = 0 30 | how = "not" 31 | } else if (how == "smart") { 32 | indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text) 33 | if (indentation == Pass || indentation > 150) { 34 | if (!aggressive) return 35 | how = "prev" 36 | } 37 | } 38 | if (how == "prev") { 39 | if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize) 40 | else indentation = 0 41 | } else if (how == "add") { 42 | indentation = curSpace + cm.options.indentUnit 43 | } else if (how == "subtract") { 44 | indentation = curSpace - cm.options.indentUnit 45 | } else if (typeof how == "number") { 46 | indentation = curSpace + how 47 | } 48 | indentation = Math.max(0, indentation) 49 | 50 | let indentString = "", pos = 0 51 | if (cm.options.indentWithTabs) 52 | for (let i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} 53 | if (pos < indentation) indentString += spaceStr(indentation - pos) 54 | 55 | if (indentString != curSpaceString) { 56 | replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input") 57 | line.stateAfter = null 58 | return true 59 | } else { 60 | // Ensure that, if the cursor was in the whitespace at the start 61 | // of the line, it is moved to the end of that space. 62 | for (let i = 0; i < doc.sel.ranges.length; i++) { 63 | let range = doc.sel.ranges[i] 64 | if (range.head.line == n && range.head.ch < curSpaceString.length) { 65 | let pos = Pos(n, curSpaceString.length) 66 | replaceOneSelection(doc, i, new Range(pos, pos)) 67 | break 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /public/codemirror/src/input/keynames.js: -------------------------------------------------------------------------------- 1 | export let keyNames = { 2 | 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", 3 | 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 4 | 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", 5 | 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 6 | 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock", 7 | 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 8 | 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 9 | 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" 10 | } 11 | 12 | // Number keys 13 | for (let i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i) 14 | // Alphabetic keys 15 | for (let i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i) 16 | // Function keys 17 | for (let i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i 18 | -------------------------------------------------------------------------------- /public/codemirror/src/line/pos.js: -------------------------------------------------------------------------------- 1 | import { getLine } from "./utils_line.js" 2 | 3 | // A Pos instance represents a position within the text. 4 | export function Pos(line, ch, sticky = null) { 5 | if (!(this instanceof Pos)) return new Pos(line, ch, sticky) 6 | this.line = line 7 | this.ch = ch 8 | this.sticky = sticky 9 | } 10 | 11 | // Compare two positions, return 0 if they are the same, a negative 12 | // number when a is less, and a positive number otherwise. 13 | export function cmp(a, b) { return a.line - b.line || a.ch - b.ch } 14 | 15 | export function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } 16 | 17 | export function copyPos(x) {return Pos(x.line, x.ch)} 18 | export function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } 19 | export function minPos(a, b) { return cmp(a, b) < 0 ? a : b } 20 | 21 | // Most of the external API clips given positions to make sure they 22 | // actually exist within the document. 23 | export function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} 24 | export function clipPos(doc, pos) { 25 | if (pos.line < doc.first) return Pos(doc.first, 0) 26 | let last = doc.first + doc.size - 1 27 | if (pos.line > last) return Pos(last, getLine(doc, last).text.length) 28 | return clipToLen(pos, getLine(doc, pos.line).text.length) 29 | } 30 | function clipToLen(pos, linelen) { 31 | let ch = pos.ch 32 | if (ch == null || ch > linelen) return Pos(pos.line, linelen) 33 | else if (ch < 0) return Pos(pos.line, 0) 34 | else return pos 35 | } 36 | export function clipPosArray(doc, array) { 37 | let out = [] 38 | for (let i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]) 39 | return out 40 | } 41 | -------------------------------------------------------------------------------- /public/codemirror/src/line/saw_special_spans.js: -------------------------------------------------------------------------------- 1 | // Optimize some code when these features are not used. 2 | export let sawReadOnlySpans = false, sawCollapsedSpans = false 3 | 4 | export function seeReadOnlySpans() { 5 | sawReadOnlySpans = true 6 | } 7 | 8 | export function seeCollapsedSpans() { 9 | sawCollapsedSpans = true 10 | } 11 | -------------------------------------------------------------------------------- /public/codemirror/src/line/utils_line.js: -------------------------------------------------------------------------------- 1 | import { indexOf } from "../util/misc.js" 2 | 3 | // Find the line object corresponding to the given line number. 4 | export function getLine(doc, n) { 5 | n -= doc.first 6 | if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.") 7 | let chunk = doc 8 | while (!chunk.lines) { 9 | for (let i = 0;; ++i) { 10 | let child = chunk.children[i], sz = child.chunkSize() 11 | if (n < sz) { chunk = child; break } 12 | n -= sz 13 | } 14 | } 15 | return chunk.lines[n] 16 | } 17 | 18 | // Get the part of a document between two positions, as an array of 19 | // strings. 20 | export function getBetween(doc, start, end) { 21 | let out = [], n = start.line 22 | doc.iter(start.line, end.line + 1, line => { 23 | let text = line.text 24 | if (n == end.line) text = text.slice(0, end.ch) 25 | if (n == start.line) text = text.slice(start.ch) 26 | out.push(text) 27 | ++n 28 | }) 29 | return out 30 | } 31 | // Get the lines between from and to, as array of strings. 32 | export function getLines(doc, from, to) { 33 | let out = [] 34 | doc.iter(from, to, line => { out.push(line.text) }) // iter aborts when callback returns truthy value 35 | return out 36 | } 37 | 38 | // Update the height of a line, propagating the height change 39 | // upwards to parent nodes. 40 | export function updateLineHeight(line, height) { 41 | let diff = height - line.height 42 | if (diff) for (let n = line; n; n = n.parent) n.height += diff 43 | } 44 | 45 | // Given a line object, find its line number by walking up through 46 | // its parent links. 47 | export function lineNo(line) { 48 | if (line.parent == null) return null 49 | let cur = line.parent, no = indexOf(cur.lines, line) 50 | for (let chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { 51 | for (let i = 0;; ++i) { 52 | if (chunk.children[i] == cur) break 53 | no += chunk.children[i].chunkSize() 54 | } 55 | } 56 | return no + cur.first 57 | } 58 | 59 | // Find the line at the given vertical position, using the height 60 | // information in the document tree. 61 | export function lineAtHeight(chunk, h) { 62 | let n = chunk.first 63 | outer: do { 64 | for (let i = 0; i < chunk.children.length; ++i) { 65 | let child = chunk.children[i], ch = child.height 66 | if (h < ch) { chunk = child; continue outer } 67 | h -= ch 68 | n += child.chunkSize() 69 | } 70 | return n 71 | } while (!chunk.lines) 72 | let i = 0 73 | for (; i < chunk.lines.length; ++i) { 74 | let line = chunk.lines[i], lh = line.height 75 | if (h < lh) break 76 | h -= lh 77 | } 78 | return n + i 79 | } 80 | 81 | export function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} 82 | 83 | export function lineNumberFor(options, i) { 84 | return String(options.lineNumberFormatter(i + options.firstLineNumber)) 85 | } 86 | -------------------------------------------------------------------------------- /public/codemirror/src/measurement/widgets.js: -------------------------------------------------------------------------------- 1 | import { contains, elt, removeChildrenAndAdd } from "../util/dom.js" 2 | import { e_target } from "../util/event.js" 3 | 4 | export function widgetHeight(widget) { 5 | if (widget.height != null) return widget.height 6 | let cm = widget.doc.cm 7 | if (!cm) return 0 8 | if (!contains(document.body, widget.node)) { 9 | let parentStyle = "position: relative;" 10 | if (widget.coverGutter) 11 | parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" 12 | if (widget.noHScroll) 13 | parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" 14 | removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)) 15 | } 16 | return widget.height = widget.node.parentNode.offsetHeight 17 | } 18 | 19 | // Return true when the given mouse event happened in a widget 20 | export function eventInWidget(display, e) { 21 | for (let n = e_target(e); n != display.wrapper; n = n.parentNode) { 22 | if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || 23 | (n.parentNode == display.sizer && n != display.mover)) 24 | return true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /public/codemirror/src/model/change_measurement.js: -------------------------------------------------------------------------------- 1 | import { cmp, Pos } from "../line/pos.js" 2 | import { lst } from "../util/misc.js" 3 | 4 | import { normalizeSelection, Range, Selection } from "./selection.js" 5 | 6 | // Compute the position of the end of a change (its 'to' property 7 | // refers to the pre-change end). 8 | export function changeEnd(change) { 9 | if (!change.text) return change.to 10 | return Pos(change.from.line + change.text.length - 1, 11 | lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) 12 | } 13 | 14 | // Adjust a position to refer to the post-change position of the 15 | // same text, or the end of the change if the change covers it. 16 | function adjustForChange(pos, change) { 17 | if (cmp(pos, change.from) < 0) return pos 18 | if (cmp(pos, change.to) <= 0) return changeEnd(change) 19 | 20 | let line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch 21 | if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch 22 | return Pos(line, ch) 23 | } 24 | 25 | export function computeSelAfterChange(doc, change) { 26 | let out = [] 27 | for (let i = 0; i < doc.sel.ranges.length; i++) { 28 | let range = doc.sel.ranges[i] 29 | out.push(new Range(adjustForChange(range.anchor, change), 30 | adjustForChange(range.head, change))) 31 | } 32 | return normalizeSelection(out, doc.sel.primIndex) 33 | } 34 | 35 | function offsetPos(pos, old, nw) { 36 | if (pos.line == old.line) 37 | return Pos(nw.line, pos.ch - old.ch + nw.ch) 38 | else 39 | return Pos(nw.line + (pos.line - old.line), pos.ch) 40 | } 41 | 42 | // Used by replaceSelections to allow moving the selection to the 43 | // start or around the replaced test. Hint may be "start" or "around". 44 | export function computeReplacedSel(doc, changes, hint) { 45 | let out = [] 46 | let oldPrev = Pos(doc.first, 0), newPrev = oldPrev 47 | for (let i = 0; i < changes.length; i++) { 48 | let change = changes[i] 49 | let from = offsetPos(change.from, oldPrev, newPrev) 50 | let to = offsetPos(changeEnd(change), oldPrev, newPrev) 51 | oldPrev = change.to 52 | newPrev = to 53 | if (hint == "around") { 54 | let range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0 55 | out[i] = new Range(inv ? to : from, inv ? from : to) 56 | } else { 57 | out[i] = new Range(from, from) 58 | } 59 | } 60 | return new Selection(out, doc.sel.primIndex) 61 | } 62 | -------------------------------------------------------------------------------- /public/codemirror/src/model/selection.js: -------------------------------------------------------------------------------- 1 | import { cmp, copyPos, equalCursorPos, maxPos, minPos } from "../line/pos.js" 2 | import { indexOf } from "../util/misc.js" 3 | 4 | // Selection objects are immutable. A new one is created every time 5 | // the selection changes. A selection is one or more non-overlapping 6 | // (and non-touching) ranges, sorted, and an integer that indicates 7 | // which one is the primary selection (the one that's scrolled into 8 | // view, that getCursor returns, etc). 9 | export class Selection { 10 | constructor(ranges, primIndex) { 11 | this.ranges = ranges 12 | this.primIndex = primIndex 13 | } 14 | 15 | primary() { return this.ranges[this.primIndex] } 16 | 17 | equals(other) { 18 | if (other == this) return true 19 | if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false 20 | for (let i = 0; i < this.ranges.length; i++) { 21 | let here = this.ranges[i], there = other.ranges[i] 22 | if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) return false 23 | } 24 | return true 25 | } 26 | 27 | deepCopy() { 28 | let out = [] 29 | for (let i = 0; i < this.ranges.length; i++) 30 | out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)) 31 | return new Selection(out, this.primIndex) 32 | } 33 | 34 | somethingSelected() { 35 | for (let i = 0; i < this.ranges.length; i++) 36 | if (!this.ranges[i].empty()) return true 37 | return false 38 | } 39 | 40 | contains(pos, end) { 41 | if (!end) end = pos 42 | for (let i = 0; i < this.ranges.length; i++) { 43 | let range = this.ranges[i] 44 | if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) 45 | return i 46 | } 47 | return -1 48 | } 49 | } 50 | 51 | export class Range { 52 | constructor(anchor, head) { 53 | this.anchor = anchor; this.head = head 54 | } 55 | 56 | from() { return minPos(this.anchor, this.head) } 57 | to() { return maxPos(this.anchor, this.head) } 58 | empty() { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch } 59 | } 60 | 61 | // Take an unsorted, potentially overlapping set of ranges, and 62 | // build a selection out of it. 'Consumes' ranges array (modifying 63 | // it). 64 | export function normalizeSelection(ranges, primIndex) { 65 | let prim = ranges[primIndex] 66 | ranges.sort((a, b) => cmp(a.from(), b.from())) 67 | primIndex = indexOf(ranges, prim) 68 | for (let i = 1; i < ranges.length; i++) { 69 | let cur = ranges[i], prev = ranges[i - 1] 70 | if (cmp(prev.to(), cur.from()) >= 0) { 71 | let from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()) 72 | let inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head 73 | if (i <= primIndex) --primIndex 74 | ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)) 75 | } 76 | } 77 | return new Selection(ranges, primIndex) 78 | } 79 | 80 | export function simpleSelection(anchor, head) { 81 | return new Selection([new Range(anchor, head || anchor)], 0) 82 | } 83 | -------------------------------------------------------------------------------- /public/codemirror/src/util/browser.js: -------------------------------------------------------------------------------- 1 | // Kludges for bugs and behavior differences that can't be feature 2 | // detected are enabled based on userAgent etc sniffing. 3 | let userAgent = navigator.userAgent 4 | let platform = navigator.platform 5 | 6 | export let gecko = /gecko\/\d/i.test(userAgent) 7 | let ie_upto10 = /MSIE \d/.test(userAgent) 8 | let ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent) 9 | let edge = /Edge\/(\d+)/.exec(userAgent) 10 | export let ie = ie_upto10 || ie_11up || edge 11 | export let ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]) 12 | export let webkit = !edge && /WebKit\//.test(userAgent) 13 | let qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent) 14 | export let chrome = !edge && /Chrome\//.test(userAgent) 15 | export let presto = /Opera\//.test(userAgent) 16 | export let safari = /Apple Computer/.test(navigator.vendor) 17 | export let mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent) 18 | export let phantom = /PhantomJS/.test(userAgent) 19 | 20 | export let ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent) 21 | export let android = /Android/.test(userAgent) 22 | // This is woefully incomplete. Suggestions for alternative methods welcome. 23 | export let mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent) 24 | export let mac = ios || /Mac/.test(platform) 25 | export let chromeOS = /\bCrOS\b/.test(userAgent) 26 | export let windows = /win/i.test(platform) 27 | 28 | let presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/) 29 | if (presto_version) presto_version = Number(presto_version[1]) 30 | if (presto_version && presto_version >= 15) { presto = false; webkit = true } 31 | // Some browsers use the wrong event properties to signal cmd/ctrl on OS X 32 | export let flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)) 33 | export let captureRightClick = gecko || (ie && ie_version >= 9) 34 | -------------------------------------------------------------------------------- /public/codemirror/src/util/operation_group.js: -------------------------------------------------------------------------------- 1 | import { getHandlers } from "./event.js" 2 | 3 | let operationGroup = null 4 | 5 | export function pushOperation(op) { 6 | if (operationGroup) { 7 | operationGroup.ops.push(op) 8 | } else { 9 | op.ownsGroup = operationGroup = { 10 | ops: [op], 11 | delayedCallbacks: [] 12 | } 13 | } 14 | } 15 | 16 | function fireCallbacksForOps(group) { 17 | // Calls delayed callbacks and cursorActivity handlers until no 18 | // new ones appear 19 | let callbacks = group.delayedCallbacks, i = 0 20 | do { 21 | for (; i < callbacks.length; i++) 22 | callbacks[i].call(null) 23 | for (let j = 0; j < group.ops.length; j++) { 24 | let op = group.ops[j] 25 | if (op.cursorActivityHandlers) 26 | while (op.cursorActivityCalled < op.cursorActivityHandlers.length) 27 | op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) 28 | } 29 | } while (i < callbacks.length) 30 | } 31 | 32 | export function finishOperation(op, endCb) { 33 | let group = op.ownsGroup 34 | if (!group) return 35 | 36 | try { fireCallbacksForOps(group) } 37 | finally { 38 | operationGroup = null 39 | endCb(group) 40 | } 41 | } 42 | 43 | let orphanDelayedCallbacks = null 44 | 45 | // Often, we want to signal events at a point where we are in the 46 | // middle of some work, but don't want the handler to start calling 47 | // other methods on the editor, which might be in an inconsistent 48 | // state or simply not expect any other events to happen. 49 | // signalLater looks whether there are any handlers, and schedules 50 | // them to be executed when the last operation ends, or, if no 51 | // operation is active, when a timeout fires. 52 | export function signalLater(emitter, type /*, values...*/) { 53 | let arr = getHandlers(emitter, type) 54 | if (!arr.length) return 55 | let args = Array.prototype.slice.call(arguments, 2), list 56 | if (operationGroup) { 57 | list = operationGroup.delayedCallbacks 58 | } else if (orphanDelayedCallbacks) { 59 | list = orphanDelayedCallbacks 60 | } else { 61 | list = orphanDelayedCallbacks = [] 62 | setTimeout(fireOrphanDelayed, 0) 63 | } 64 | for (let i = 0; i < arr.length; ++i) 65 | list.push(() => arr[i].apply(null, args)) 66 | } 67 | 68 | function fireOrphanDelayed() { 69 | let delayed = orphanDelayedCallbacks 70 | orphanDelayedCallbacks = null 71 | for (let i = 0; i < delayed.length; ++i) delayed[i]() 72 | } 73 | -------------------------------------------------------------------------------- /public/codemirror/theme/cobalt.css: -------------------------------------------------------------------------------- 1 | .cm-s-cobalt.CodeMirror { background: #002240; color: white; } 2 | .cm-s-cobalt div.CodeMirror-selected { background: #b36539; } 3 | .cm-s-cobalt .CodeMirror-line::selection, .cm-s-cobalt .CodeMirror-line > span::selection, .cm-s-cobalt .CodeMirror-line > span > span::selection { background: rgba(179, 101, 57, .99); } 4 | .cm-s-cobalt .CodeMirror-line::-moz-selection, .cm-s-cobalt .CodeMirror-line > span::-moz-selection, .cm-s-cobalt .CodeMirror-line > span > span::-moz-selection { background: rgba(179, 101, 57, .99); } 5 | .cm-s-cobalt .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } 6 | .cm-s-cobalt .CodeMirror-guttermarker { color: #ffee80; } 7 | .cm-s-cobalt .CodeMirror-guttermarker-subtle { color: #d0d0d0; } 8 | .cm-s-cobalt .CodeMirror-linenumber { color: #d0d0d0; } 9 | .cm-s-cobalt .CodeMirror-cursor { border-left: 1px solid white; } 10 | 11 | .cm-s-cobalt span.cm-comment { color: #08f; } 12 | .cm-s-cobalt span.cm-atom { color: #845dc4; } 13 | .cm-s-cobalt span.cm-number, .cm-s-cobalt span.cm-attribute { color: #ff80e1; } 14 | .cm-s-cobalt span.cm-keyword { color: #ffee80; } 15 | .cm-s-cobalt span.cm-string { color: #3ad900; } 16 | .cm-s-cobalt span.cm-meta { color: #ff9d00; } 17 | .cm-s-cobalt span.cm-variable-2, .cm-s-cobalt span.cm-tag { color: #9effff; } 18 | .cm-s-cobalt span.cm-variable-3, .cm-s-cobalt span.cm-def, .cm-s-cobalt .cm-type { color: white; } 19 | .cm-s-cobalt span.cm-bracket { color: #d8d8d8; } 20 | .cm-s-cobalt span.cm-builtin, .cm-s-cobalt span.cm-special { color: #ff9e59; } 21 | .cm-s-cobalt span.cm-link { color: #845dc4; } 22 | .cm-s-cobalt span.cm-error { color: #9d1e15; } 23 | 24 | .cm-s-cobalt .CodeMirror-activeline-background { background: #002D57; } 25 | .cm-s-cobalt .CodeMirror-matchingbracket { outline:1px solid grey;color:white !important; } 26 | -------------------------------------------------------------------------------- /public/css/bootstrap-suggest.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * bootstra-suggest - v2.0.1 (https://github.com/lodev09/bootstrap-suggest#readme) 3 | * Copyright 2013-2019 Jovanni Lo (lodev09@gmail.com) 4 | * Licensed under MIT (https://github.com/lodev09/bootstrap-suggest/blob/master/LICENSE) 5 | */ 6 | 7 | .suggest { 8 | /* position: relative; */ 9 | top: 7px; 10 | z-index: 30; 11 | text-align: left; 12 | } 13 | 14 | .suggest > .dropdown-menu { 15 | margin-top: 15px; 16 | position: absolute; 17 | } 18 | 19 | .suggest > .dropdown-menu > a.dropdown-item { 20 | border-top: 1px solid #eeeeee; 21 | padding: 5px 10px; 22 | } 23 | 24 | .suggest > .dropdown-menu > a.dropdown-item:first-child { 25 | border-top: 0; 26 | } 27 | -------------------------------------------------------------------------------- /public/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This section of the stylesheet comes from https://codepen.io/gungorbudak/pen/ooKNpz 3 | * 4 | */ 5 | 6 | .has-search .form-control { 7 | padding-left: 2.375rem; 8 | } 9 | 10 | .has-search .form-control-feedback { 11 | position: absolute; 12 | display: block; 13 | z-index: 2; 14 | width: 2.375rem; 15 | height: 2.375rem; 16 | line-height: 2.375rem; 17 | text-align: center; 18 | pointer-events: none; 19 | color: #aaa; 20 | } 21 | 22 | /* 23 | * This section of the stylesheet comes from the https://getbootstrap.com/docs/4.3/examples/dashboard/ 24 | * bootstrap example. 25 | * 26 | */ 27 | 28 | body { 29 | font-size: .875rem; 30 | } 31 | 32 | .feather { 33 | width: 16px; 34 | height: 16px; 35 | vertical-align: text-bottom; 36 | } 37 | 38 | /* 39 | * Sidebar 40 | */ 41 | 42 | .sidebar { 43 | position: fixed; 44 | top: 0; 45 | bottom: 0; 46 | left: 0; 47 | z-index: 100; /* Behind the navbar */ 48 | padding: 56px 0 0; /* Height of navbar */ 49 | box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1); 50 | } 51 | 52 | .sidebar-sticky { 53 | position: relative; 54 | top: 0; 55 | height: calc(100vh - 56px); 56 | padding-top: .5rem; 57 | overflow-x: hidden; 58 | overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ 59 | } 60 | 61 | @supports ((position: -webkit-sticky) or (position: sticky)) { 62 | .sidebar-sticky { 63 | position: -webkit-sticky; 64 | position: sticky; 65 | } 66 | } 67 | 68 | .sidebar .nav-link { 69 | font-weight: 500; 70 | color: #333; 71 | } 72 | 73 | .sidebar .nav-link .feather { 74 | margin-right: 4px; 75 | color: #999; 76 | } 77 | 78 | .sidebar .nav-link.active { 79 | color: #007bff; 80 | } 81 | 82 | .sidebar .nav-link:hover .feather, 83 | .sidebar .nav-link.active .feather { 84 | color: inherit; 85 | } 86 | 87 | .sidebar-heading { 88 | font-size: .75rem; 89 | text-transform: uppercase; 90 | } 91 | 92 | /* 93 | * Content 94 | */ 95 | 96 | [role="main"] { 97 | padding-top: 80px; /* Space for fixed navbar */ 98 | } 99 | 100 | 101 | /* 102 | * This section of the stylesheet contains custom CSS. 103 | * 104 | */ 105 | .pointer { 106 | cursor: pointer; 107 | } 108 | 109 | .striped-rows > .row.form-group { 110 | margin-bottom: 0rem; 111 | margin-left: 15px; 112 | } 113 | 114 | .striped-rows > .row:nth-of-type(odd) *[class*=' col'], 115 | .striped-rows > .row:nth-of-type(odd) *[class^='col'] { 116 | background-color: #efefef; 117 | padding-bottom: 0.5rem; 118 | padding-top: 0.5rem; 119 | } 120 | 121 | .striped-rows > .row:nth-of-type(even) *[class*=' col'], 122 | .striped-rows > .row:nth-of-type(even) *[class^='col'] { 123 | background-color: #ffffff; 124 | padding-bottom: 0.5rem; 125 | padding-top: 0.5rem; 126 | } 127 | -------------------------------------------------------------------------------- /public/css/signin.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | body { 5 | display: -ms-flexbox; 6 | display: flex; 7 | -ms-flex-align: center; 8 | align-items: center; 9 | padding-top: 40px; 10 | padding-bottom: 40px; 11 | background-color: #f5f5f5; 12 | } 13 | .form-signin { 14 | width: 100%; 15 | max-width: 330px; 16 | padding: 15px; 17 | margin: auto; 18 | } 19 | .form-signin .form-control { 20 | position: relative; 21 | box-sizing: border-box; 22 | height: auto; 23 | padding: 10px; 24 | font-size: 16px; 25 | } 26 | .form-signin .form-control:focus { 27 | z-index: 2; 28 | } 29 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/favicon.ico -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/css/brands.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Font Awesome 5 Brands'; 3 | font-style: normal; 4 | font-weight: normal; 5 | font-display: auto; 6 | src: url("../webfonts/fa-brands-400.eot"); 7 | src: url("../webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.woff") format("woff"), url("../webfonts/fa-brands-400.ttf") format("truetype"), url("../webfonts/fa-brands-400.svg#fontawesome") format("svg"); } 8 | 9 | .fab { 10 | font-family: 'Font Awesome 5 Brands'; } 11 | -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/css/brands.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:normal;font-display:auto;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands"} -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/css/regular.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Font Awesome 5 Free'; 3 | font-style: normal; 4 | font-weight: 400; 5 | font-display: auto; 6 | src: url("../webfonts/fa-regular-400.eot"); 7 | src: url("../webfonts/fa-regular-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.woff") format("woff"), url("../webfonts/fa-regular-400.ttf") format("truetype"), url("../webfonts/fa-regular-400.svg#fontawesome") format("svg"); } 8 | 9 | .far { 10 | font-family: 'Font Awesome 5 Free'; 11 | font-weight: 400; } 12 | -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/css/regular.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;font-display:auto;src:url(../webfonts/fa-regular-400.eot);src:url(../webfonts/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.woff) format("woff"),url(../webfonts/fa-regular-400.ttf) format("truetype"),url(../webfonts/fa-regular-400.svg#fontawesome) format("svg")}.far{font-family:"Font Awesome 5 Free";font-weight:400} -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/css/solid.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Font Awesome 5 Free'; 3 | font-style: normal; 4 | font-weight: 900; 5 | font-display: auto; 6 | src: url("../webfonts/fa-solid-900.eot"); 7 | src: url("../webfonts/fa-solid-900.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.woff") format("woff"), url("../webfonts/fa-solid-900.ttf") format("truetype"), url("../webfonts/fa-solid-900.svg#fontawesome") format("svg"); } 8 | 9 | .fa, 10 | .fas { 11 | font-family: 'Font Awesome 5 Free'; 12 | font-weight: 900; } 13 | -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/css/solid.min.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:auto;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:"Font Awesome 5 Free";font-weight:900} -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/fontawesome-5.7.2/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/fontawesome-5.7.2/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/fontawesome-5.7.2/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/fontawesome-5.7.2/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/fontawesome-5.7.2/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/fontawesome-5.7.2/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/fontawesome-5.7.2/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/fontawesome-5.7.2/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/fontawesome-5.7.2/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/fontawesome-5.7.2/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/fontawesome-5.7.2/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /public/fontawesome-5.7.2/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/fontawesome-5.7.2/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /public/img/cvssicons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/img/cvssicons.png -------------------------------------------------------------------------------- /public/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /public/img/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/img/logo.jpg -------------------------------------------------------------------------------- /public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/public/img/logo.png -------------------------------------------------------------------------------- /public/js/format_shortcut.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | setConvertToListListener(); 3 | }); 4 | 5 | /* Escape user input before inserting it into regexp */ 6 | function escapeRegExp(str) { 7 | return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); 8 | } 9 | 10 | /* add event listener for each texterea with allowMarkupShortcut class */ 11 | function setConvertToListListener(){ 12 | var elements = $("textarea.allowMarkupShortcut"); 13 | for(var i=0; i 0) 62 | startSelection--; 63 | 64 | while(fullText[endSelection] !== "\n" && endSelection !== fullText.length) 65 | endSelection++; 66 | 67 | var textToEdit = fullText.slice(startSelection, endSelection) 68 | var listRow = textToEdit.split("\n"); 69 | 70 | /* check from first line if must must add or remove tags */ 71 | var tagIsPresent = false; 72 | 73 | var regexTag = new RegExp("^"+escapeRegExp(tags.startTag)+".*"+escapeRegExp(tags.endTag)+"$"); 74 | 75 | /* for each line, remove all tags, an if necessary, add the new tags corresponding to the pressed key */ 76 | if(listRow[0].match(regexTag)) 77 | tagIsPresent = true; 78 | 79 | for(var i=0; i/g, '&rt;').replace(/"/g, '"') + "\"]) { display: none; }"; 12 | } 13 | 14 | searchBox.addEventListener('input', function() { 15 | if (!this.value) { 16 | searchStyle.innerHTML = ""; 17 | return; 18 | } 19 | searchStyle.innerHTML = ".searchable:not([data-index*=\"" + this.value.toLowerCase().replace(//g, '&rt;').replace(/"/g, '"') + "\"]) { display: none; }"; 20 | }); 21 | } 22 | } 23 | 24 | $(document).ready(function(){ 25 | initSearch(); 26 | }); 27 | 28 | // Used to confirm deletion 29 | function confirmDelete(evt) { 30 | if (!confirm('Are you sure you want to permanently delete the selected element(s) ?')) { 31 | evt.preventDefault(); 32 | } 33 | } 34 | $(document).ready(function(){ 35 | var deleteElements = $('a.btn-danger'); 36 | for (var index = 0, length = deleteElements.length; index < length; index++) { 37 | deleteElements[index].addEventListener('click', confirmDelete, false); 38 | } 39 | }); 40 | //used for managing checkbox with multiple deletion 41 | $(document).ready(function(){ 42 | //manage the box checking all other boxes 43 | $("#mytable #checkall").click(function () { 44 | if ($("#mytable #checkall").is(':checked')) { 45 | $("#mytable input[type=checkbox]").each(function () { 46 | $(this).prop("checked", true); 47 | }); 48 | 49 | } else { 50 | $("#mytable input[type=checkbox]").each(function () { 51 | $(this).prop("checked", false); 52 | }); 53 | } 54 | }); 55 | //add the ids to delete to the parameter 56 | $("[data-toggle=tooltip]").tooltip(); 57 | $("#deletemultiple").click(function () { 58 | var selected = []; 59 | $('tbody input:checked[name]').each(function() { 60 | selected.push($(this).attr('name')); 61 | }); 62 | if (selected.length == 0 ) { 63 | alert("You should check at least one attachment name first :-)"); 64 | event.preventDefault(); 65 | } else { 66 | $(this).attr('href', this.href + selected.join()); 67 | } 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /routes/mapping.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | 3 | config_options = JSON.parse(File.read('./config.json')) 4 | 5 | # Delete a mapping from finding 6 | get '/mapping/:id/nessus/:mappingid/delete' do 7 | # Check for kosher name in report name 8 | id = params[:id] 9 | 10 | mappingid = params[:mappingid] 11 | 12 | @map = NessusMapping.first(templatefindings_id: id, pluginid: mappingid) 13 | 14 | @map.destroy 15 | redirect to("/master/findings/#{id}/edit") 16 | end 17 | 18 | # Delete a mapping from finding 19 | get '/mapping/:id/burp/:mappingid/delete' do 20 | # Check for kosher name in report name 21 | id = params[:id] 22 | 23 | mappingid = params[:mappingid] 24 | 25 | @map = BurpMapping.first(templatefindings_id: id, pluginid: mappingid) 26 | 27 | @map.destroy 28 | redirect to("/master/findings/#{id}/edit") 29 | end 30 | 31 | # Delete a vuln mapping from finding 32 | get '/mapping/:id/vulnmap/:mappingid/delete' do 33 | # Check for kosher name in report name 34 | id = params[:id] 35 | 36 | mappingid = params[:mappingid] 37 | 38 | @vulnmappings = VulnMappings.first(templatefindings_id: id, id: mappingid) 39 | @vulnmappings.destroy 40 | redirect to("/master/findings/#{id}/edit") 41 | end 42 | -------------------------------------------------------------------------------- /scripts/Serpico_init_template: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SERPDIR=/opt/Serpico #Update this to point to your Serpico install 3 | PATH=$SCRIPTDIR:$PATH 4 | 5 | case "$1" in 6 | start) echo "Staring Serpico....." 7 | cd $SERPDIR 8 | echo `/usr/local/bin/ruby serpico.rb &` #If you're using RVM you may need to update this to the RVM version of ruby 9 | ;; 10 | 11 | stop) echo "Stopping Serpico...." 12 | pkill -f serpico.rb 13 | ;; 14 | restart) echo "Restarting Serpico....." 15 | $0 stop 16 | $0 start 17 | ;; 18 | esac 19 | exit 0 20 | -------------------------------------------------------------------------------- /scripts/alert_unapproved_findings.rb: -------------------------------------------------------------------------------- 1 | require './model/master.rb' 2 | 3 | findings = TemplateFindings.all 4 | 5 | fd = false 6 | findings.each do |finding| 7 | if finding["approved"] == false 8 | puts "|+| Title: #{finding["title"]} (https://127.0.0.1:8443/master/findings/#{finding["id"]}/edit)" 9 | fd = true 10 | end 11 | end 12 | 13 | unless fd 14 | puts "|!| No Unapproved Findings Found" 15 | end 16 | -------------------------------------------------------------------------------- /scripts/create_user.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require './model/master.rb' 3 | 4 | if ARGV.size < 3 5 | # With no arguments a list of users is dumped 6 | puts "\n ****Usage: create_user.rb username password level \n" 7 | 8 | users = User.all 9 | 10 | puts "\n Current Users" 11 | puts "Username \t Type \t Created At \n " 12 | 13 | users.each do |u| 14 | puts "#{u.username} \t #{u.type} \t #{u.created_at}" 15 | end 16 | puts "\n" 17 | exit 18 | end 19 | 20 | user = User.new 21 | user.username = ARGV[0] 22 | user.password = ARGV[1] 23 | user.type = ARGV[2] 24 | user.auth_type = "Local" 25 | user.save 26 | -------------------------------------------------------------------------------- /scripts/docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # This script is used as the entry point for the docker image. 3 | # It will initialize the database if it isn't already present. 4 | 5 | if [ ! -f "$SRP_ROOT/db/master.db" ]; then 6 | echo "First run detected. Initializing database..." 7 | ruby "$SRP_ROOT/scripts/first_time.rb" 8 | fi 9 | 10 | # CMD ["ruby", "serpico.rb"] 11 | ruby serpico.rb 12 | 13 | -------------------------------------------------------------------------------- /scripts/export_reports.rb: -------------------------------------------------------------------------------- 1 | require './model/master.rb' 2 | require 'json' 3 | 4 | if ARGV.size < 1 5 | puts "|!| usage: export_reports.rb [id] [-d]\n" 6 | puts "\tThis script can be used to backup a large number of reports. Note, it does not save attachments." 7 | exit 8 | end 9 | 10 | id = ARGV[0] 11 | puts "|+| Exporting all reports before id #{id}" 12 | 13 | del = ARGV[1] 14 | puts "|!| Deleting after export" if del 15 | 16 | 0.upto(id.to_i) do |temp| 17 | json = {} 18 | 19 | report = Reports.first(:id => temp) 20 | 21 | # bail without a report 22 | if not report 23 | puts "|!| report #{temp} does not exist, skipping" 24 | end 25 | next unless report 26 | 27 | puts "|+| exporting #{temp} to tmp/report_#{temp}.JSON" 28 | 29 | # add the report 30 | json["report"] = report 31 | 32 | # add the findings 33 | findings = Findings.all(:report_id => temp) 34 | json["findings"] = findings 35 | 36 | local_filename = "./tmp/report_#{temp}.json" 37 | File.open(local_filename, 'w') {|f| f.write(JSON.pretty_generate(json)) } 38 | 39 | if del 40 | puts "|!| deleting report #{temp}" 41 | report.destroy 42 | findings.destroy 43 | end 44 | report = "" 45 | findings = "" 46 | 47 | end 48 | -------------------------------------------------------------------------------- /scripts/export_template_findings.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require './model/master.rb' 3 | 4 | # This will export all findings as JSON, useful for import later 5 | 6 | if ARGV.size > 0 7 | id = ARGV[0] 8 | puts "Exporting single finding with id #{id}" 9 | 10 | findings = TemplateFindings.first(:id => id) 11 | 12 | puts findings.to_json 13 | 14 | else 15 | 16 | findings = TemplateFindings.all 17 | 18 | findings.each do |f| 19 | puts f.to_json 20 | end 21 | 22 | end 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /scripts/give_plugin_access.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require './model/master.rb' 3 | 4 | if ARGV.size < 1 5 | # With no arguments a list of users is dumped 6 | puts "\n ****Usage: give_plugin_access.rb username \n" 7 | 8 | exit 9 | end 10 | 11 | username = ARGV[0] 12 | user = User.first(:username => username) 13 | 14 | if not user 15 | puts "|+| #{username} not found" 16 | exit 17 | end 18 | 19 | user.update(:plugin => true) 20 | puts "|+| #{username} is updated to upload plugins" -------------------------------------------------------------------------------- /scripts/lf.sed: -------------------------------------------------------------------------------- 1 | s/>/>\ 2 | /g 3 | -------------------------------------------------------------------------------- /scripts/make_export.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | zip -r export.zip helpers/* serpico.rb model/* public/* server* templates/* views/* scripts/create_user.rb scripts/first_time.rb scripts/reset_pw.rb scripts/update_templates.rb 4 | -------------------------------------------------------------------------------- /scripts/reset_pw.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require './model/master.rb' 3 | 4 | user = User.first 5 | 6 | print "Would you like to change the password for #{user.username} (Y/n) " 7 | 8 | change = gets.chomp.downcase 9 | 10 | if change == "y" or change == "" 11 | 12 | password = rand(36**10).to_s(36) 13 | 14 | user.update(:type => "Administrator", :auth_type => "Local", :password => password) 15 | 16 | puts "User successfully updated." 17 | 18 | puts "\t\t New password is : #{password} \n\n" 19 | else 20 | puts "Exiting..." 21 | end 22 | -------------------------------------------------------------------------------- /scripts/serpico.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=serpico: SimplE RePort wrIting and COllaboration tool 3 | After=network.target auditd.service 4 | 5 | [Service] 6 | WorkingDirectory=/opt/Serpico 7 | Environment=GEM_HOME=/home/$USER/.rvm/gems/ruby-2.3.3 8 | ExecStart=/usr/share/rvm/rubies/ruby-2.3.3/bin/ruby /opt/Serpico/serpico.rb & 9 | ExecStop=/usr/bin/pkill -f serpico.rb 10 | #ExecReload=/etc/init.d/serpico restart 11 | KillMode=process 12 | Restart=on-failure 13 | 14 | [Install] 15 | WantedBy=default.target 16 | Alias=serpico 17 | 18 | #using rvm 1.29.7 19 | -------------------------------------------------------------------------------- /scripts/serpicoInstall.sh: -------------------------------------------------------------------------------- 1 | # serpicoInstall.sh 2 | 3 | #!/bin/bash 4 | 5 | username=$(whoami) 6 | 7 | # Use this to download the rvm gpg 8 | \curl -sSL https://rvm.io/mpapis.asc | sudo gpg --import - 9 | 10 | # Download and install the stable version of RVM in multi user mode 11 | \curl -sSL https://get.rvm.io | sudo bash -s stable 12 | 13 | sudo -s <> /home/$username/.bashrc 23 | echo source /etc/profile >> /root/.bashrc 24 | 25 | # Install openssl library (required for ruby install 2.1.5) 26 | apt-get install -y libssl1.0-dev 27 | 28 | rvm get master 29 | 30 | # Install and use Ruby 2.1.5, the version used by Serpico 31 | rvm install 2.6.3 32 | 33 | source /etc/profile.d/rvm.sh 34 | rvm use 2.6.3 35 | 36 | # Serpico Dependencies 37 | apt-get -y install libsqlite3-dev libxslt-dev libxml2-dev zlib1g-dev gcc 38 | 39 | # Get and install the development version of Serpico 40 | git clone https://github.com/SerpicoProject/Serpico.git /opt/Serpico-Dev 41 | cd /opt/Serpico-Dev/ 42 | 43 | gem install bundler 44 | 45 | bundle install 46 | 47 | # Initialize the findings database 48 | puts "|+| Please run 'ruby scripts/first_time.rb' to complete the installation" 49 | EOF 50 | 51 | # One completed, you can edit /opt/Serpico-Dev/config.json so that your Serpico instance is local only. 52 | # Change "bind_address": "0.0.0.0", to "bind_address": "127.0.0.1", 53 | -------------------------------------------------------------------------------- /scripts/update_templates.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require './model/master.rb' 3 | 4 | findings = TemplateFindings.all 5 | 6 | findings.each do |finding| 7 | finding["approved"] = true 8 | finding.save 9 | end 10 | -------------------------------------------------------------------------------- /serpico.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'webrick/https' 3 | require 'openssl' 4 | require 'json' 5 | require './server.rb' 6 | config_options = JSON.parse(File.read('./config.json')) 7 | 8 | ## SSL Settings 9 | ssl_certificate = config_options['ssl_certificate'] 10 | ssl_key = config_options['ssl_key'] 11 | use_ssl = config_options['use_ssl'] 12 | port = config_options['port'] 13 | bind_address = config_options['bind_address'] 14 | 15 | server_options = { 16 | Port: port, 17 | Host: bind_address 18 | } 19 | 20 | if config_options['show_exceptions'].to_s.casecmp('false').zero? || !(config_options['show_exceptions']) 21 | puts "|+| [#{DateTime.now.strftime('%d/%m/%Y %H:%M')}] Sending Webrick logging to /dev/null.." 22 | server_options[:Logger] = WEBrick::Log.new(File.open(File::NULL, 'w')) 23 | server_options[:AccessLog] = [] 24 | end 25 | 26 | if use_ssl 27 | certificate_content = File.open(ssl_certificate).read 28 | key_content = File.open(ssl_key).read 29 | server_options[:SSLEnable] = true 30 | server_options[:SSLCertificate] = OpenSSL::X509::Certificate.new(certificate_content) 31 | server_options[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(key_content) 32 | server_options[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_NONE 33 | 34 | no_ssl3 = OpenSSL::SSL::OP_NO_SSLv3 35 | no_ssl2 = OpenSSL::SSL::OP_NO_SSLv2 36 | no_compression = OpenSSL::SSL::OP_NO_COMPRESSION 37 | ssl_options = no_ssl2 + no_ssl3 + no_compression 38 | server_options[:SSLOptions] = ssl_options 39 | server_options[:SSLVersion] = :TLSv1_2 40 | 41 | if(config_options.key?('ssl_ciphers')) 42 | cz = config_options['ssl_ciphers'] 43 | else 44 | # SSL Ciphers 45 | cz = ['ECDHE-RSA-AES128-GCM-SHA256','ECDHE-RSA-AES256-GCM-SHA384', 46 | 'ECDHE-RSA-AES128-CBC-SHA','ECDHE-RSA-AES256-CBC-SHA', 47 | 'AES128-GCM-SHA256','AES256-GCM-SHA384','AES128-SHA256', 48 | 'AES256-SHA256','AES128-SHA','AES256-SHA'] 49 | end 50 | 51 | CIPHERS = cz.push("TLSv1.2","!aNULL","!eNULL","!SSLv2","!SSLv3") 52 | server_options[:SSLCiphers] = CIPHERS.join(":") 53 | 54 | end 55 | 56 | Rack::Handler::WEBrick.run Server, server_options 57 | -------------------------------------------------------------------------------- /templates/CVSS_Template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/templates/CVSS_Template.docx -------------------------------------------------------------------------------- /templates/Default CVSS 3 Report.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/templates/Default CVSS 3 Report.docx -------------------------------------------------------------------------------- /templates/Default NIST800 Report.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/templates/Default NIST800 Report.docx -------------------------------------------------------------------------------- /templates/Default Status.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/templates/Default Status.docx -------------------------------------------------------------------------------- /templates/Default Template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/templates/Default Template.docx -------------------------------------------------------------------------------- /templates/Serpico - Finding.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/templates/Serpico - Finding.docx -------------------------------------------------------------------------------- /templates/Serpico - GenericRiskScoring.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/templates/Serpico - GenericRiskScoring.docx -------------------------------------------------------------------------------- /templates/Serpico - No DREAD.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/templates/Serpico - No DREAD.docx -------------------------------------------------------------------------------- /templates/Serpico - Report.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/templates/Serpico - Report.docx -------------------------------------------------------------------------------- /templates/Serpico - Risk Finding.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/templates/Serpico - Risk Finding.docx -------------------------------------------------------------------------------- /templates/Serpico - Status.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/templates/Serpico - Status.docx -------------------------------------------------------------------------------- /tmp/tmp.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerpicoProject/Serpico/667a4853ecd0b01e2d40166269f3b1e916a480d5/tmp/tmp.txt -------------------------------------------------------------------------------- /views/add_template.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Add Template 3 | 4 | %form{ :method => "post", :action => "/admin/templates/add", :enctype => "multipart/form-data" } 5 | .form-group.row 6 | %label.col-lg-2.col-form-label{ :for => "report_type" } Report Type 7 | .col-lg-4 8 | %input#report_type.form-control{ :type => "text", :name => "report_type", :required => true } 9 | .form-group.row 10 | %label.col-lg-2.col-form-label{ :for => "description" } File Description 11 | .col-lg-4 12 | %input#description.form-control{ :type => "text", :name => "description" } 13 | .form-group.row 14 | %label.col-lg-2.col-form-label{ :for => "finding_template" } Finding Template 15 | .col-lg-4 16 | .form-check 17 | %input#finding_template.form-check-input{ :type => "checkbox", :name => "finding_template" } 18 | .form-group.row 19 | %label.col-lg-2.col-form-label{ :for => "status_template" } Status Template 20 | .col-lg-4 21 | .form-check 22 | %input#status_template.form-check-input{ :type => "checkbox", :name => "status_template" } 23 | .form-group.row 24 | %label.col-lg-2.col-form-label{ :for => "file" } DOCX 25 | .col-lg-4 26 | .custom-file 27 | %input#file.custom-file-input{ :type => "file", :name => "file", :required => true } 28 | %label.custom-file-label{ :for => "file" } Choose file 29 | 30 | %br 31 | %input.btn.btn-primary{ :type => "submit", :value => "Add" } 32 | %a.btn.btn-secondary{ :href => "/admin/templates" } 33 | Cancel 34 | -------------------------------------------------------------------------------- /views/add_user.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | - if @user 3 | %h1.h2 Edit User 4 | - else 5 | %h1.h2 Add User 6 | 7 | %form{ :method => "post", :action => "/admin/add_user" } 8 | .form-group.row 9 | %label.col-lg-2.col-form-label{ :for => "username" } Username 10 | .col-lg-4 11 | - if @user 12 | %input#username.form-control{ :type => "text", :name => "username", :value => "#{@user.username}" } 13 | - else 14 | %input#username.form-control{ :type => "text", :name => "username" } 15 | .form-group.row 16 | %label.col-lg-2.col-form-label{ :for => "password" } Password (Not required for AD Auth Type) 17 | .col-lg-4 18 | %input#password.form-control{ :type => "password", :name => "password" } 19 | .form-group.row 20 | %label.col-lg-2.col-form-label{ :for => "auth_type" } Authorization Type 21 | .col-lg-4 22 | %select#auth_type.custom-select{ :name => "auth_type" } 23 | - if @user 24 | - if @user.auth_type == "AD" 25 | %option{ :selected => "selected" } AD 26 | - else 27 | %option AD 28 | - if @user.auth_type == "Local" 29 | %option{ :selected => "selected" } Local 30 | - else 31 | %option Local 32 | - else 33 | %option{ :selected => "selected" } AD 34 | %option Local 35 | .form-group.row 36 | %label.col-lg-2.col-form-label{ :for => "type" } User Level 37 | .col-lg-4 38 | %select#type.custom-select{ :name => "type" } 39 | - if @user 40 | - if @user.type == "Administrator" 41 | %option{ :selected => "selected" } Administrator 42 | - else 43 | %option Administrator 44 | - if @user.type == "User" 45 | %option{ :selected => "selected" } User 46 | - else 47 | %option User 48 | - else 49 | %option{ :selected => "selected" } User 50 | %option Administrator 51 | 52 | %br 53 | %input.btn.btn-primary{ :type => "submit", :value => "Add" } 54 | %a.btn.btn-secondary{ :href => "/admin/list_user" } 55 | Cancel 56 | -------------------------------------------------------------------------------- /views/add_user_report.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Editing #{@report.report_name} Report 3 | 4 | %h2.h4 Current Authors 5 | .table-responsive.col-6.px-0 6 | %table.table.table-striped.table-sm 7 | %tbody 8 | %tr 9 | %td 10 | #{@report.owner} 11 | %td 12 |   13 | - if @report.authors 14 | - @report.authors.each do |user| 15 | %tr 16 | %td 17 | :escaped 18 | #{user} 19 | %td.text-right 20 | %a.btn.btn-sm.btn-danger{ :href => "/admin/del_user_report/#{@report.id}/#{user}" } 21 | %i.far.fa-trash-alt{ :title => "Remove Author" } 22 | 23 | %br 24 | 25 | %form{ :method => "post", :action => "/admin/add_user/#{@report.id}" } 26 | .form-group.row 27 | %label.col-lg-2.col-form-label{ :for => "author" } Add Collaborator 28 | .col-lg-4 29 | %select#author.custom-select{ :name => "author" } 30 | - @users.each do |user| 31 | %option #{user.username} 32 | 33 | %br 34 | %input.btn.btn-primary{ :type => "submit", :value => "Add" } 35 | %a.btn.btn-secondary{ :href => "/reports/list" } 36 | Cancel 37 | -------------------------------------------------------------------------------- /views/additional_features.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Additional Features 3 | 4 | %a{ :href => "/report/#{@report.id}/user_defined_variables" } Manage User Defined Variables 5 | %br 6 | %a{ :href => "/report/#{@report.id}/udo/manage" } Manage User Defined Objects 7 | %br 8 | %a{ :href => "/report/#{@report.id}/export" } Export Current Report (Warning: Attachments must be exported separately) 9 | %br 10 | %a{ :href => "/report/#{@report.id}/export_attachments" } Export Attachments 11 | %br 12 | %a{ :href => "/report/#{@report.id}/restore_attachments" } Restore Attachments 13 | %br 14 | %a{ :href => "/report/#{@report.id}/msfsettings" } Configure a Metasploit RPC Connection 15 | 16 | %br 17 | %br 18 | 19 | %a{ :href => "/report/#{@report.id}/import/vulns" } Auto Add Vulnerabilities from Metasploit DB 20 | %br 21 | %a{ :href => "/report/#{@report.id}/import_nessus" } Auto Add Findings from a Nessus XML (Deprecated - Use MSF RPC) 22 | %br 23 | %a{ :href => "/report/#{@report.id}/import_burp" } Auto Add Findings from a Burp XML scanner report 24 | 25 | %br 26 | %br 27 | 28 | %a{ :href => "/report/#{@report.id}/status" } Generate Status Report 29 | %br 30 | %a{ :href => "/report/#{@report.id}/text_status" } Generate Text Only Status Report 31 | %br 32 | %a{ :href => "/report/#{@report.id}/csv_export" } Generate Findings CSV (Pipe Delimited) 33 | %br 34 | %a{ :href => "/report/#{@report.id}/asciidoc_status" } Generate AsciiDoc of Current Findings 35 | %br 36 | %a{ :href => "/report/#{@report.id}/presentation" } Generate Presentation from Report 37 | %br 38 | %a{ :href => "/report/#{@report.id}/presentation?print-pdf" } Generate Presentation to PDF (NOTE: Print and Save to PDF to view) 39 | %br 40 | %a{ :href => "/report/#{@report.id}/presentation_export" } Export Presentation to HTML 41 | -------------------------------------------------------------------------------- /views/admin.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Heyyyyoo. You are an Admin. 3 | 4 | %h2.h4 Use the menu on the left to complete actions. 5 | -------------------------------------------------------------------------------- /views/config.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Configuration Options (Must Restart After Changes) 3 | 4 | %form{ :method => "post", :action => "/admin/config" } 5 | .form-group.row 6 | %label.col-lg-2.col-form-label{ :for => "risk_scoring" } Risk Scoring Algorithm 7 | .col-lg-4 8 | %select#risk_scoring.custom-select{ :name => "risk_scoring" } 9 | - if @scoring == "default" 10 | %option{ :selected => "selected" } Default 11 | - else 12 | %option Default Scoring 13 | - if @scoring == "dread" 14 | %option{ :selected => "selected" } DREAD 15 | - else 16 | %option DREAD 17 | - if @scoring == "cvss" 18 | %option{ :selected => "selected" } CVSSv2 19 | - else 20 | %option CVSSv2 21 | - if @scoring == "cvssv3" 22 | %option{ :selected => "selected" } CVSSv3 23 | - else 24 | %option CVSSv3 25 | - if @scoring == "riskmatrix" 26 | %option{ :selected => "selected" } RISKMATRIX 27 | - else 28 | %option RISKMATRIX 29 | - if @scoring == "nist800" 30 | %option{ :selected => "selected" } NIST800-30 31 | - else 32 | %option NIST800-30 33 | 34 | - @config.each do |k,v| 35 | - next if k =~ /ssl_certificate/ 36 | - next if k =~ /ssl_key/ 37 | - next if k =~ /log_file/ 38 | - next if k =~ /image_align/ 39 | - next if k == "cvss" or k == "dread" or k == "cvssv3" or k == "riskmatrix" or k == "nist800" 40 | .form-group.row 41 | %label.col-lg-2.col-form-label{ :for => "#{k}" } #{k} 42 | .col-lg-4 43 | - if (v == false) or (v == true) or (v == "false") or (v == "true") 44 | .form-check 45 | - if (v == true) or (v == "true") 46 | %input.form-check-input{ :id => "#{k}", :type => "checkbox", :name => "#{k}", :checked => "checked" } 47 | - else 48 | %input.form-check-input{ :id => "#{k}", :type => "checkbox", :name => "#{k}" } 49 | - elsif v.instance_of? Array 50 | %input.form-control{ :id => "#{k}", :type => "text", :name => "#{k}", :value => "#{v.join(',')}" } 51 | - else 52 | %input.form-control{ :id => "#{k}", :type => "text", :name => "#{k}", :value => "#{v}" } 53 | 54 | %br 55 | %input.btn.btn-primary{ :type => "submit", :value => "Save" } 56 | %a.btn.btn-secondary{ :href => "/admin/" } 57 | Cancel 58 | -------------------------------------------------------------------------------- /views/dbhosts.haml: -------------------------------------------------------------------------------- 1 | - if not @vulnmap 2 | %h1.h5 Metasploit integration not enabled. Contact your administrator to enable (vulnmap configuration setting) 3 | - else 4 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 5 | %h1.h2 Hosts 6 | This is a read-only view of the first 10k hosts within Metasploits database. 7 | %br 8 | %br 9 | .table-responsive.col-6.px-0 10 | %table.table.table-striped.table-sm 11 | %thead 12 | %tr 13 | %td 14 | %b IP Address 15 | %td 16 | %b Hostname 17 | %td 18 | %b Platform 19 | %td 20 | %b Info 21 | %tbody 22 | - if @hosts 23 | - @hosts.each do |host| 24 | %tr 25 | %td.searchable{ :style => "width: 20%", :"data-index" => "#{host["address"]}" } 26 | #{host["address"]} 27 | %td.searchable{ :style => "width: 20%", :"data-index" => "#{host["name"]}" } 28 | #{host["name"]} 29 | %td.searchable{ :style => "width: 20%", :"data-index" => "#{host["os_name"]}" } 30 | #{host["os_name"]} 31 | #{host["os_sp"]} 32 | %td.searchable{ :style => "width: 40%", :"data-index" => "#{host["info"]}" } 33 | #{host["info"]} 34 | -------------------------------------------------------------------------------- /views/dbservices.haml: -------------------------------------------------------------------------------- 1 | - if not @vulnmap 2 | %h1.h5 Metasploit integration not enabled. Contact your administrator to enable (vulnmap configuration setting) 3 | - else 4 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 5 | %h1.h2 Services 6 | This is a read-only view of the first 10k opened services within Metasploits database. 7 | %br 8 | %br 9 | .table-responsive.col-6.px-0 10 | %table.table.table-striped.table-sm 11 | %thead 12 | %tr 13 | %td 14 | %b IP Address 15 | %td 16 | %b service Port 17 | %td 18 | %b service Name 19 | %td 20 | %b State 21 | %td 22 | %b Info 23 | %tbody 24 | - if @services 25 | - @services.each do |service| 26 | %tr 27 | %td.searchable{ :style => "width: 15%", :"data-index" => "#{service["host"]}" } 28 | #{service["host"]} 29 | %td.searchable{ :style => "width: 15%", :"data-index" => "#{service["port"]}" } 30 | #{service["port"]} 31 | %td.searchable{ :style => "width: 15%", :"data-index" => "#{service["proto"]}" } 32 | #{service["name"]} 33 | %td.searchable{ :style => "width: 15%", :"data-index" => "#{service["state"]}" } 34 | #{service["state"]} 35 | %td.searchable{ :style => "width: 40%", :"data-index" => "#{service["info"]}" } 36 | #{service["info"]} 37 | -------------------------------------------------------------------------------- /views/dbvulns.haml: -------------------------------------------------------------------------------- 1 | - if not @vulnmap 2 | %h1.h5 Metasploit integration not enabled. Contact your administrator to enable (vulnmap configuration setting) 3 | - else 4 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 5 | %h1.h2 Vulnerabilities 6 | This is a read-only view of the first 10k vulnerabilities within Metasploits database. 7 | %br 8 | %br 9 | 10 | %label.control-label 11 | %a.btn.btn-info{ :href => "#vulninfomodal", "data-toggle" => "modal" } 12 | More Info 13 | 14 | .modal.modal.hide.fade#vulninfomodal{ :tabindex => "-1", :role => "dialog" } 15 | .modal-dialog.modal-lg{ :role => "document" } 16 | .modal-content 17 | .modal-header 18 | %h3#modal-label 19 | Metasploit Auto Mapping Vulnerabilities 20 | %button.close{ :type => "button", "data-dismiss" => "modal", "aria-label" => "Close" } 21 | × 22 | .modal-body 23 | To automatically add vulnerabilities from the Metasploit database into your report, you'll need to have one of the reference codes mapped to an existing Serpico finding. 24 | %p 25 | A reference code can be added by editing the 26 | %b Vuln ID 27 | field of a finding in the Findings Database. 28 | %p 29 | %a.btn.btn-primary{ :href=> "/master/findings" } 30 | Go to Findings Database 31 | %p 32 | 33 | .table-responsive.col-6.px-0 34 | %table.table.table-striped.table-sm 35 | %thead 36 | %tr 37 | %td 38 | %b Vulnerability 39 | %td 40 | %b Host 41 | %td 42 | %b Port 43 | %td 44 | %b References 45 | %tbody 46 | - if @vulns 47 | - @vulns.each do |vuln| 48 | %tr 49 | %td.searchable{ :style => "width: 50%", :"data-index" => "#{vuln["name"]}" } 50 | #{vuln["name"]} 51 | %td.searchable{ :style => "width: 10%", :"data-index" => "#{vuln["host"]}" } 52 | #{vuln["host"]} 53 | %td.searchable{ :style => "width: 5%", :"data-index" => "#{vuln["port"]}" } 54 | #{vuln["port"]} 55 | %td.searchable{ :style => "width: 35%", :"data-index" => "#{vuln["refs"]}" } 56 | #{vuln["refs"]} 57 | -------------------------------------------------------------------------------- /views/edit_template.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Edit Template 3 | 4 | %form{ :method => "post", :action => "/admin/templates/edit", :enctype => "multipart/form-data" } 5 | %input{ :type => "hidden", :name => "id", :value => "#{@template.id}" } 6 | .form-group.row 7 | %label.col-lg-2.col-form-label{ :for => "report_type" } Report Type 8 | .col-lg-4 9 | %input#report_type.form-control{ :type => "text", :name => "report_type", :required => true, :value => "#{@template.report_type}" } 10 | .form-group.row 11 | %label.col-lg-2.col-form-label{ :for => "description" } File Description 12 | .col-lg-4 13 | %input#description.form-control{ :type => "text", :name => "description", :value => "#{@template.description}" } 14 | .form-group.row 15 | %label.col-lg-2.col-form-label{ :for => "finding_template" } Finding Template 16 | .col-lg-4 17 | .form-check 18 | %input#finding_template.form-check-input{ :type => "checkbox", :name => "finding_template" } 19 | .form-group.row 20 | %label.col-lg-2.col-form-label{ :for => "status_template" } Status Template 21 | .col-lg-4 22 | .form-check 23 | %input#status_template.form-check-input{ :type => "checkbox", :name => "status_template" } 24 | .form-group.row 25 | %label.col-lg-2.col-form-label{ :for => "file" } DOCX 26 | .col-lg-4 27 | .custom-file 28 | %input#file.custom-file-input{ :type => "file", :name => "file", :required => true } 29 | %label.custom-file-label{ :for => "file" } Choose file 30 | 31 | %br 32 | %input.btn.btn-primary{ :type => "submit", :value => "Modify" } 33 | %a.btn.btn-secondary{ :href => "/admin/templates" } 34 | Cancel 35 | -------------------------------------------------------------------------------- /views/enabled_plugins.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Administrator Specific Plugins 3 | 4 | - if @menu.size > 0 5 | - @menu.each do |item| 6 | .form-group.row 7 | .col-lg-2 8 | %b #{item["name"]} 9 | .col-lg-6 #{item["description"]} 10 | .col-lg-2 11 | %a{ :href => "#{item["link"]}" } #{item["name"]} 12 | - else 13 | No administrator specific plugins found. 14 | -------------------------------------------------------------------------------- /views/footer.haml: -------------------------------------------------------------------------------- 1 | %footer.footer 2 | %p 3 | © Company 2012 4 | -------------------------------------------------------------------------------- /views/import_burp.haml: -------------------------------------------------------------------------------- 1 | - if not @burpmap 2 | %h1.h5 Burp Auto Mapping not enabled. Contact your administrator to enable. 3 | - else 4 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 5 | %h1.h2 Auto Add Findings from Burp scanner report (XML only) 6 | 7 | %form{ :method => "post", :action => "/report/#{@report.id}/import_autoadd", :enctype => "multipart/form-data" } 8 | %input{ :type => "hidden", :name => "type", :value => "burp" } 9 | .form-group.row 10 | %label.col.col-form-label{ :for => "file" } Upload burp scanner report file (XML only). 11 | 12 | .form-group.row 13 | .col-lg-4 14 | .custom-file 15 | %input#file.custom-file-input{ :type => "file", :name => "file", :required => true } 16 | %label.custom-file-label{ :for => "file" } Choose file 17 | 18 | %br 19 | %input.btn.btn-primary{ :type => "submit", :value => "Upload" } 20 | %a.btn.btn-secondary{ :href => "/report/#{@report.id}/additional_features" } 21 | Cancel 22 | -------------------------------------------------------------------------------- /views/import_nessus.haml: -------------------------------------------------------------------------------- 1 | - if not @nessusmap 2 | %h1.h5 Nessus Auto Mapping not enabled. Contact your administrator to enable. 3 | - else 4 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 5 | %h1.h2 Auto Add Findings from Nessus XML 6 | 7 | %form{ :method => "post", :action => "/report/#{@report.id}/import_autoadd", :enctype => "multipart/form-data" } 8 | %input{:type => "hidden", :name => "type", :value => "nessus"} 9 | .form-group.row 10 | %label.col.col-form-label{ :for => "file" } Upload Nessus XML File (Nessusv2 only). 11 | 12 | .form-group.row 13 | .col-lg-4 14 | .custom-file 15 | %input#file.custom-file-input{ :type => "file", :name => "file", :required => true } 16 | %label.custom-file-label{ :for => "file" } Choose file 17 | 18 | %br 19 | %input.btn.btn-primary{ :type => "submit", :value => "Upload" } 20 | %a.btn.btn-secondary{ :href => "/report/#{@report.id}/additional_features" } 21 | Cancel 22 | -------------------------------------------------------------------------------- /views/import_report.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Import Report 3 | 4 | %form{ :method => "post", :enctype=>"multipart/form-data" } 5 | .form-group.row 6 | %label.col.col-form-label{ :for => "file" } Upload JSON Report - Please be careful. This must have been exported from the UI. 7 | 8 | .form-group.row 9 | .col-lg-4 10 | .custom-file 11 | %input#file.custom-file-input{ :type => "file", :name => "file", :required => true } 12 | %label.custom-file-label{ :for => "file" } Choose file 13 | 14 | %br 15 | %input.btn.btn-primary{ :type => "submit", :value => "Upload" } 16 | %a.btn.btn-secondary{ :href => "/" } 17 | Cancel 18 | -------------------------------------------------------------------------------- /views/import_scan_data.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Import Findings from Scan Data XML File 3 | 4 | %h2.h5 5 | This feature uses the 6 | %a(title="Odle" href="https://rubygems.com/odle") odle 7 | gem, check 8 | %a(title="Odle" href="https://github.com/BuffaloWill/odle") here 9 | for a list of supported formats. 10 | 11 | %p NOTE: All findings are imported via this method, Nessus and Burp plugin ids are not respected 12 | 13 | %br 14 | 15 | %form{ :method => "post", :action => "/report/#{@report.id}/import_scan_data", :enctype => "multipart/form-data" } 16 | .form-group.row 17 | %label.col-lg-2.col-form-label{ :for => "auth_type" } Data Type 18 | .col-lg-4 19 | %select#type.custom-select{ :name => "type"} 20 | - %w[\ Nessus BurpV1 Metasploit NMap].each do |type| 21 | %option #{type} 22 | .form-group.row 23 | %label.col-lg-2.col-form-label{ :for => "file" } Upload File 24 | .col-lg-4 25 | .custom-file 26 | %input#file.custom-file-input{ :type => "file", :name => "file", :required => true } 27 | %label.custom-file-label{ :for => "file" } Choose file 28 | 29 | %br 30 | %input.btn.btn-primary{ :type => "submit", :value => "Upload" } 31 | %a.btn.btn-secondary{ :href => "/report/#{@report.id}/findings" } 32 | Cancel 33 | -------------------------------------------------------------------------------- /views/import_templates.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Upload Templated Findings 3 | 4 | .form-group.row 5 | %p.ml-3 Upload JSON File - Please be careful. This must have been exported from the UI.  6 | 7 | %form{ :method => "post", :enctype => "multipart/form-data" } 8 | .form-group.row 9 | .col-lg-4 10 | .custom-file 11 | %input#file.custom-file-input{ :type => "file", :name => "file", :required => true } 12 | %label.custom-file-label{ :for => "file" } Choose file 13 | 14 | .form-group.row 15 | .col 16 | .form-check 17 | %input#approved.form-check-input{ :type => "checkbox", :name => "approved" } 18 | %label.form-check-label.pl-2{ :for => "approved" } Automatically Approve Imported Findings. By default you should manually approve findings, this overrides that. 19 | 20 | %br 21 | %input.btn.btn-primary{ :type => "submit", :value => "Upload" } 22 | %a.btn.btn-secondary{ :href => "/master/findings" } 23 | Cancel 24 | -------------------------------------------------------------------------------- /views/index.haml: -------------------------------------------------------------------------------- 1 | .row 2 | %h1 Hello, world! 3 | %p lorem ipsum 4 | -------------------------------------------------------------------------------- /views/info.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Consultant Information 3 | 4 | %form{ :method => "post" } 5 | .form-group.row 6 | %label.col-lg-2.col-form-label{ :for => "company" } Consultant Company 7 | .col-lg-4 8 | %input#company.form-control{ :type => "text", :name => "company", :value=> "#{@user.consultant_company}" } 9 | .form-group.row 10 | %label.col-lg-2.col-form-label{ :for => "name" } Consultant Name 11 | .col-lg-4 12 | %input#name.form-control{ :type => "text", :name => "name", :value=> "#{@user.consultant_name}" } 13 | .form-group.row 14 | %label.col-lg-2.col-form-label{ :for => "email" } Email 15 | .col-lg-4 16 | %input#email.form-control{ :type => "text", :name => "email", :value => "#{@user.consultant_email}" } 17 | .form-group.row 18 | %label.col-lg-2.col-form-label{ :for => "phone" } Phone 19 | .col-lg-4 20 | %input#phone.form-control{ :type => "text", :name => "phone", :value => "#{@user.consultant_phone}" } 21 | .form-group.row 22 | %label.col-lg-2.col-form-label{ :for => "title" } Title 23 | .col-lg-4 24 | %input#title.form-control{ :type => "text", :name => "title", :value => "#{@user.consultant_title}" } 25 | 26 | %br 27 | %input.btn.btn-primary{ :type => "submit", :value => "Save" } 28 | %a.btn.btn-secondary{ :href => "/" } 29 | Cancel 30 | -------------------------------------------------------------------------------- /views/list_user.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Current Users 3 | 4 | - if @users 5 | .table.table-striped 6 | %table{ :style => "width: 50%" } 7 | %thead 8 | %tr 9 | %td{ :style => "width: 40%" } 10 | %b Username 11 | %td{ :style => "width: 34%" } 12 | %b Level/Authentication Type 13 | %td.text-right{ :style => "width: 15%" } 14 | %b Actions 15 | %tbody 16 | - @users.each do |user| 17 | %tr 18 | %td 19 | :escaped 20 | #{user.username} 21 | %td 22 | :escaped 23 | #{user.type} / #{user.auth_type} 24 | %td.text-right 25 | %a.btn.btn-sm.btn-warning{ :href => "/admin/edit_user/#{user.id}" } 26 | %i.fas.fa-pencil-alt{ :title => "Edit Password" } 27 | %a.btn.btn-sm.btn-danger{ :href => "/admin/delete/#{user.id}" } 28 | %i.far.fa-trash-alt{ :title => "Delete User" } 29 | - else 30 | No Users. How Curious. Who are you? 31 | -------------------------------------------------------------------------------- /views/msfsettings.haml: -------------------------------------------------------------------------------- 1 | - if not @vulnmap 2 | %h1.h5 Vulnerability Mapping not enabled. Contact your administrator to enable (vulnmap configuration setting) 3 | - else 4 | .row 5 | .alert.alert-info 6 | .fa.fa-exclamation-circle 7 |  Remember to start your metasploit RPC daemon by running the following command in your terminal: 8 | %b msfrpcd -U $username -P $password --ssl 9 | 10 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 11 | %h1.h2 Metasploit RPC Settings 12 | 13 | %h1.h5 Specify the metasploit RPC settings for this report. 14 | %br 15 | 16 | %form.form-horizontal{ :method => "post", :enctype => "application/x-www-form-urlencoded" } 17 | .form-group.row 18 | %label.col-lg-2.col-form-label{ :for => "ip" } IP Address 19 | .col-lg-4 20 | - if @msfsettings and @msfsettings.ip 21 | %input#ip.form-control{ :type => "text", :name => "ip", :value => "#{@msfsettings.ip}" } 22 | - else 23 | %input#ip.form-control{ :type => "text", :name => "ip", :value => "" } 24 | .form-group.row 25 | %label.col-lg-2.col-form-label{ :for => "port" } Port 26 | .col-lg-4 27 | - if @msfsettings and @msfsettings.port 28 | %input#port.form-control{ :type => "text", :name => "port", :value => "#{@msfsettings.port}" } 29 | -else 30 | %input#port.form-control{ :type => "text", :name => "port", :value => "" } 31 | .form-group.row 32 | %label.col-lg-2.col-form-label{ :for => "user" } Username 33 | .col-lg-4 34 | - if @msfsettings and @msfsettings.user 35 | %input#user.form-control{ :type => "text", :name => "user", :value => "#{@msfsettings.user}" } 36 | - else 37 | %input#user.form-control{ :type => "text", :name => "user", :value => "" } 38 | .form-group.row 39 | %label.col-lg-2.col-form-label{ :for => "pass" } Password 40 | .col-lg-4 41 | - if @msfsettings and @msfsettings.pass 42 | %input#pass.form-control{ :type => "password", :name => "pass", :value => "#{@msfsettings.pass}" } 43 | - else 44 | %input#pass.form-control{ :type => "password", :name => "pass", :value => "" } 45 | .form-group.row 46 | %label.col-lg-2.col-form-label{ :for => "workspace" } Workspace 47 | .col-lg-4 48 | - if @msfsettings and @msfsettings.workspace 49 | %input#workspace.form-control{ :type => "text", :name => "workspace", :value => "#{@msfsettings.workspace}" } 50 | - else 51 | %input#workspace.form-control{ :type => "text", :name => "workspace", :value => "" } 52 | 53 | %br 54 | %input.btn.btn-primary{ :type => "submit", :value => "Save" } 55 | %a.btn.btn-secondary{ :href => "/report/#{@report.id}/additional_features" } 56 | Cancel 57 | -------------------------------------------------------------------------------- /views/new_report.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Create Report (or Import) 3 | 4 | %form{ :method => "post" } 5 | - if @templates.size > 0 6 | .form-group.row 7 | %label.col-lg-2.col-form-label{ :for => "report_name" } Title 8 | .col-lg-4 9 | %input#report_name.form-control{ :type => "text", :name => "report_name", :required => "" } 10 | - if @languages 11 | .form-group.row 12 | %label.col-lg-2.col-form-label{ :for => "language" } Language 13 | .col-lg-4 14 | %select#language.custom-select{ :name => "language" } 15 | - @languages.each do |lang| 16 | %option #{lang} 17 | .form-group.row 18 | %label.col-lg-2.col-form-label{ :for => "full_company_name" } Full Company Name 19 | .col-lg-4 20 | %input#full_company_name.form-control{ :type => "text", :name => "full_company_name" } 21 | .form-group.row 22 | %label.col-lg-2.col-form-label{ :for => "short_company_name" } Short Company Name 23 | .col-lg-4 24 | %input#short_company_name.form-control{ :type => "text", :name => "short_company_name" } 25 | .form-group.row 26 | %label.col-lg-2.col-form-label{ :for => "assessment_type" } Assessment Type 27 | .col-lg-4 28 | %select#assessment_type.custom-select{ :name => "assessment_type" } 29 | - @assessment_types.each do |assessment_type| 30 | %option #{assessment_type} 31 | .form-group.row 32 | %label.col-lg-2.col-form-label{ :for => "report_type" } Report Type 33 | .col-lg-4 34 | %select#report_type.custom-select{ :name => "report_type" } 35 | - @templates.each do |template| 36 | - if template.finding_template == false and template.status_template == false 37 | - if @report and template.report_type == @report.report_type 38 | %option{ :selected => "selected" } #{type} 39 | - else 40 | %option #{template.report_type} 41 | 42 | %br 43 | %input.btn.btn-primary{ :type => "submit", :value => "Save" } 44 | %a.btn.btn-secondary{ :href => "/" } 45 | Cancel 46 | 47 | - else 48 | %h4 49 | Hrm, there don't seem to be any report templates. An admin will need to add one -_- 50 | -------------------------------------------------------------------------------- /views/plugins.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Available Plugins (Must Restart After Changes) 3 | 4 | %form{ :method => "post" } 5 | - if @plugins.size > 0 6 | %form 7 | - @plugins.each do |plug| 8 | - next unless plug["name"] 9 | .form-group.row 10 | .col-lg-2 11 | %b #{plug["name"]} 12 | .col-lg-6 #{plug["description"]} 13 | .col-lg-2 14 | - if (plug["enabled"] == false) or (plug["enabled"] == true) or (plug["enabled"] == "false") or (plug["enabled"] == "true") 15 | - if (plug["enabled"] == true) or (plug["enabled"] == "true") 16 | %input.form-check-input{ :type => "checkbox", :name => "#{plug['name']}", :checked => "checked" } 17 | - else 18 | %input.form-check-input{ :type => "checkbox", :name => "#{plug['name']}" } 19 | 20 | %br 21 | %input.btn.btn-primary{ :type => "submit", :value => "Save" } 22 | %a.btn.btn-secondary{ :type => "button", :href => "/admin/" } 23 | Cancel 24 | - else 25 | No plugins found. 26 | #{@plugin} 27 | - if @admin == true and @plugin == true 28 | %h2 Upload a plugin (Must Restart After Upload) 29 | %form{ :method => "post", :enctype => "multipart/form-data", :action => "/admin/plugin_upload" } 30 | %br 31 | %input{ :type => "file", :name => "files[]", :multiple => true} 32 | %br 33 | %input{ :type => "submit", :value => "Upload" } 34 | -------------------------------------------------------------------------------- /views/reports_list.haml: -------------------------------------------------------------------------------- 1 | - if @reports.size > 0 2 | .form-group.has-search.row.mt-5 3 | .col-6 4 | %span.fa.fa-search.form-control-feedback 5 | %input#search.form-control{ :type => "text", :placeholder => "Report Name Search" } 6 | 7 | %style#search_style 8 | %br 9 | %a.btn.btn-danger#deletemultiple{ :href => "/report/remove/" } 10 | Delete selected 11 | %br 12 | %br 13 | 14 | #mytable.table.table-striped{ :style => "width: 80%" } 15 | %table 16 | %thead 17 | %tr 18 | %th 19 | %input.checkbox#checkall{ :type => "checkbox" } 20 | %th{ :style => "width: 65%" } 21 | Report Name 22 | %th{ :style => "width: 10%" } 23 | Customer 24 | %th{ :style => "width: 10%" } 25 | Owner 26 | %th.text-right{ :style => "width: 15%" } 27 | Actions 28 | %tbody 29 | - @reports.each do |report| 30 | %tr 31 | %td.searchable{ :"data-index" => "#{report.report_name.downcase}" } 32 | %input.checkbox{ :type => "checkbox", :name => "#{report.id}" } 33 | %td.searchable{ :"data-index" => "#{report.report_name.downcase}" } 34 | #{report.report_name} 35 | %td.searchable{ :"data-index" => "#{report.report_name.downcase}" } 36 | #{report.full_company_name} 37 | %td.searchable{ :"data-index" => "#{report.report_name.downcase}" } 38 | #{report.owner} 39 | %td.searchable.text-right{ :"data-index" => "#{report.report_name.downcase}" } 40 | - if @master 41 | %a.btn.btn-sm.btn-warning{ :href => "/master/reports/#{finding.id}" } 42 | %i.fas.fa-pencil-alt{ :title => "Edit" } 43 | %a.btn.btn-sm.btn-info{ :href => "/master/reports/#{report.id}/generate" } 44 | %i.far.fa-play-circle{ :title => "Preview" } 45 | - else 46 | %a.btn.btn-sm.btn-warning{ :href => "/report/#{report.id}/edit" } 47 | %i.fas.fa-pencil-alt{ :title => "Edit" } 48 | %a.btn.btn-sm.btn-info{ :href => "/report/#{report.id}/generate" } 49 | %i.far.fa-play-circle{ :title => "Preview" } 50 | %a.btn.btn-sm.btn-danger{ :href => "/report/remove/#{report.id}" } 51 | %i.far.fa-trash-alt{ :title => "Delete" } 52 | %a.btn.btn-sm.btn-dark{ :href => "/admin/add_user/#{report.id}" } 53 | %i.fas.fa-user{ :title => "Add Author" } 54 | 55 | - else 56 | %h1.h4 You have no reports, how about creating or importing one... 57 | -------------------------------------------------------------------------------- /views/reset.haml: -------------------------------------------------------------------------------- 1 | - if @message and @message == "success" 2 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 3 | %h1.h2 Password Successfully Changed 4 | - else 5 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 6 | %h1.h2 Change Password 7 | 8 | %form{ :method => "post" } 9 | .form-group.row 10 | %label.col-lg-2.col-form-label{ :for => "old_pass" } Old Password 11 | .col-lg-4 12 | %input#old_pass.form-control{ :type => "password", :name => "old_pass", :value=> "", :autocomplete => "off" } 13 | .form-group.row 14 | %label.col-lg-2.col-form-label{ :for => "new_pass" } New Password 15 | .col-lg-4 16 | %input#new_pass.form-control{ :type => "password", :name => "new_pass", :value=> "", :autocomplete => "off" } 17 | .form-group.row 18 | %label.col-lg-2.col-form-label{ :for => "new_pass_confirm" } Confirm New Password 19 | .col-lg-4 20 | %input#new_pass_confirm.form-control{ :type => "password", :name => "new_pass_confirm", :value=> "", :autocomplete => "off" } 21 | 22 | %br 23 | %input.btn.btn-primary{ :type => "submit", :value => "Save" } 24 | %a.btn.btn-secondary{ :href => "/reports/list" } 25 | Cancel 26 | -------------------------------------------------------------------------------- /views/restore_attachments.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Restore attachments from Attachments Export 3 | 4 | %h2.h5 Note: the restore file MUST be a zip file from the Attachments Export 5 | %br 6 | %form{ :method => "post", :enctype=>"multipart/form-data" } 7 | .form-group.row 8 | .col-lg-4 9 | .custom-file 10 | %input#file.custom-file-input{ :type => "file", :name => "file", :required => true } 11 | %label.custom-file-label{ :for => "file" } Choose file 12 | 13 | %br 14 | %input.btn.btn-primary{ :type => "submit", :value => "Upload" } 15 | %a.btn.btn-secondary{ :href => "/report/#{@report.id}/additional_features" } 16 | Cancel 17 | -------------------------------------------------------------------------------- /views/template_list.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Current Templates 3 | 4 | - if @templates 5 | .table.table-striped 6 | %table{ :style => "width: 80%" } 7 | %thead 8 | %tr 9 | %td{ :style => "width: 30%" } 10 | %b Report Type 11 | %td{ :style => "width: 30%" } 12 | %b Description 13 | %td{ :style => "width: 25%" } 14 | %b Template Type 15 | %td.text-right{ :style => "width: 15%" } 16 | %b Actions 17 | %tbody 18 | - @templates.each do |template| 19 | %tr 20 | %td 21 | #{template.report_type} 22 | %td 23 | #{template.description} 24 | %td 25 | - if template.finding_template 26 | Finding Template 27 | - elsif template.status_template 28 | Status Template 29 | - else 30 | Report Template 31 | %td.text-right 32 | %a.btn.btn-sm.btn-warning{ :href => "/admin/templates/#{template.id}/edit" } 33 | %i.fas.fa-pencil-alt{ :title => "Edit" } 34 | %a.btn.btn-sm.btn-info{ :href => "/admin/templates/#{template.id}/download" } 35 | %i.far.fa-play-circle{ :title => "Preview" } 36 | %a.btn.btn-sm.btn-danger{ :href => "/admin/delete/templates/#{template.id}" } 37 | %i.far.fa-trash-alt{ :title => "Delete Template" } 38 | %a.btn.btn-sm.btn-success{:href => "/admin/templates/#{template.id}/tree"} 39 | %i.fas.fa-indent{:title => 'Tree'} 40 | - else 41 | No Templates. Silly goose, how will you create reports? 42 | -------------------------------------------------------------------------------- /views/test.haml: -------------------------------------------------------------------------------- 1 | %h1 AAAAA 2 | -------------------------------------------------------------------------------- /views/text_status.haml: -------------------------------------------------------------------------------- 1 | .col-md-10 2 | %br 3 | - if @findings.size > 0 4 | Hi! 5 | %br 6 | Here is a current list of findings from the assessment and the risk rating: 7 | %br 8 | - @findings.each do |finding| 9 | %br 10 |    #{finding.title} - 11 | - if @dread 12 | DREAD Score: #{finding.dread_total} 13 | - else 14 | - risk_t = ["Informational","Low","Moderate","High","Critical"] 15 | - if finding.risk 16 | #{risk_t[finding.risk]} 17 | - else 18 | - scr = finding.dread_total/10 19 | - scr = 4 if dread_total == 50 20 | #{risk_t[scr]} 21 | - else 22 | This is going to be a short status update as you don't have any findings associated with this project. 23 | -------------------------------------------------------------------------------- /views/user_defined_object_create.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Create new #{@udo_template.type} 3 | 4 | %form{ :method => "post", :enctype => "application/x-www-form-urlencoded" } 5 | - @udo_template_properties.each do |param_name, param_default_value| 6 | .form-group.row 7 | %label.col-lg-2.col-form-label{ :for => "param_#{param_name}" } 8 | %b #{param_name} 9 | .col-lg-4.striped-col 10 | %textarea.allowMarkupShortcut.form-control{ :id => "param_#{param_name}", :name => "param_#{param_name}", :rows => "2" } 11 | #{meta_markup(param_default_value)} 12 | 13 | %br 14 | %input.btn.btn-primary{ :type => "submit", :value => "Save", :name => "action" } 15 | %a.btn.btn-secondary{ :href => "/report/#{@report.id}/udo/manage" } 16 | Cancel 17 | -------------------------------------------------------------------------------- /views/user_defined_object_edit.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Edit #{@udo_to_edit.type} object 3 | 4 | %form{ :method => "post", :enctype => "application/x-www-form-urlencoded" } 5 | - @udo_template_properties.each do |param_name, value| 6 | .form-group.row 7 | %label.col-lg-2.col-form-label{ :for => "param_#{param_name}" } 8 | %b #{param_name} 9 | .col-lg-4.striped-col 10 | %textarea.allowMarkupShortcut.form-control{ :id => "param_#{param_name}", :name => "param_#{param_name}", :rows => "2" } 11 | #{meta_markup(@udo_to_edit_properties[param_name.downcase])} 12 | 13 | %br 14 | %input.btn.btn-primary{ :type => "submit", :value => "Save", :name => "action" } 15 | %a.btn.btn-secondary{ :href => "/report/#{@report.id}/udo/manage" } 16 | Cancel 17 | -------------------------------------------------------------------------------- /views/user_defined_variable.haml: -------------------------------------------------------------------------------- 1 | .d-flex.justify-content-between.flex-wrap.flex-md-nowrap.align-items-center.pt-3.pb-2.mb-3.border-bottom 2 | %h1.h2 Report Name: #{@report.report_name} 3 | 4 | %form{ :method => "post" } 5 | - if @user_variables 6 | - i = 0 7 | - w = 66 8 | - @user_variables.each do | variables_name, variables_data| 9 | .striped-rows 10 | .form-group.row 11 | %label.col-lg-2.col-form-label{ :for => "variable_name_#{w}_" } Variable Name: 12 | .col-lg-4.striped-col 13 | %input.form-control{ :id => "variable_name_#{w}_", :type => "text", :name =>"variable_name_#{w}_", :value => "#{variables_name}" } 14 | .form-group.row 15 | %label.col-lg-2.col-form-label{ :for => "variable_data_#{w}_" } Variable data: 16 | .col-lg-4.striped-col 17 | %textarea.form-control{ :id => "variable_data_#{w}_", :name => "variable_data_#{w}_" } 18 | #{variables_data} 19 | %br 20 | - w = w + 1 21 | %h2.h5 Add user defined variables 22 | .striped-rows 23 | .form-group.row 24 | %label.col-lg-2.col-form-label{ :for => "variable_name_#{i}_" } Variable Name: 25 | .col-lg-4.striped-col 26 | %input.form-control{ :id => "variable_name_#{i}_", :type => "text", :name =>"variable_name_#{i}_" } 27 | .form-group.row 28 | %label.col-lg-2.col-form-label{ :for => "variable_data_#{i}_" } Variable data: 29 | .col-lg-4.striped-col 30 | %textarea.form-control{ :id => "variable_data_#{i}_", :rows => "10", :name => "variable_data_#{i}_" } 31 | %br 32 | - i = i + 1 33 | 34 | %input.btn.btn-primary{ :type => "submit", :value => "Save" } 35 | %a.btn.btn-secondary{ :href => "/report/#{@report.id}/additional_features" } 36 | Cancel 37 | --------------------------------------------------------------------------------