├── .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 | [](https://travis-ci.org/codemirror/CodeMirror)
3 | [](https://www.npmjs.org/package/codemirror)
4 | [](https://gitter.im/codemirror/CodeMirror)
5 | [Funding status: ](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 | 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.
34 |
35 | 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.
40 |
41 | 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.
44 |
45 | 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.
49 |
50 | 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.
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 |
--------------------------------------------------------------------------------