Codebase list python-faraday / upstream/3.6.0
New upstream version 3.6.0 Sophie Brun 5 years ago
96 changed file(s) with 1976 addition(s) and 756 deletion(s). Raw diff Collapse all Expand all
6868
6969 # Documentation builds
7070 doc/_build/
71 scripts/searcher/log/searcher.log
72 =0.54.0
73 stream.svg
74 scripts/searcher/output/searcher.db
75 .pytest_cache
76 reports/executive/outputs/scope.docx
77 .gitignore
0 ---
1 name: Feature request
2 about: Suggest an idea for this
3
4 ---
5
6 **What's the problem this feature will solve?**
7 <!-- What are you trying to do, that you are unable to achieve with faraday as it currently stands? -->
8
9 **Describe the solution you'd like**
10 <!-- Clear and concise description of what you want to happen. -->
11
12 <!-- Provide examples of real world use cases that this would enable and how it solves the problem described above. -->
13
14 **Alternative Solutions**
15 <!-- different approach to solving this issue? Please elaborate here. -->
16
17 **Additional context**
18 <!-- Add any other context, links, etc. about the feature here. -->
0 ---
1 name: Bug report
2 about: Create a report an issue
3
4 ---
5
6 Please search the [Wiki](https://github.com/infobyte/faraday/wiki) for a solution before posting a ticket. Use the <strong>“New Support Request”</strong> button to the right of the screen to submit a ticket for technical support.
7
8 ## Issue Type
9 - Bug Re port
10 - Feature Idea
11 - Documentation Report
12
13
14 ## Faraday version
15
16 Paste the output of the *./faraday.py --version* command
17
18 ## Component Name
19
20 If you know where the problem lays indicate it:
21 WebGui/GTKGui/Plugin/Console/Continuous Scanning/Etc.
22
23 ## Steps to reproduce
24
25 Provide detailed steps on how the issue happened so we can try to reproduce it. If the issue is random, please provide as much information as possible.
26
27 ## Expected results
28
29 What did you expect to happen when following the steps above?
30
31 ### Debugging tracebacks (current results)
32
33 Try to reproduce the bug with the server and/or gtk client in debug mode and check the logs for the ERROR string.
34 Add here any errors you find while running in debug mode or, if possible, Faraday’s log files (located at *$HOME/.faraday/logs/*).
35
36 If you need help on how to execute in debug mode [click here for more information](https://github.com/infobyte/faraday/wiki/troubleshooting).
37
38 Please attach the result of:
39
40 pip freeze > requirements_freeze.txt
41
42 ### Screenshots
43
44 If you don't find anything on the logs, please provide screenshots of the error.
45
46 ## Environment information
47
48 ### Configuration files
49
50 Mention any settings you have changed/added/removed.
51
52 ### Reports/Extra data
53
54 If you are having issues with plugins, please attach relevant files if possible.
55 (strip your reports of all sensitive information beforehand).
56
57 ### OS
58
59 Provide information on your operating system. Example:
60
61 $ cat /etc/lsb-release
62 DISTRIB_ID=Ubuntu
63 DISTRIB_RELEASE=16.10
64 DISTRIB_CODENAME=yakkety
65 DISTRIB_DESCRIPTION="Ubuntu 16.10"
0 variables:
1 TZ: "America/New_York"
2 # Configure postgres service (https://hub.docker.com/_/postgres/)
3 POSTGRES_DB: custom_db
4 POSTGRES_USER: custom_user
5 POSTGRES_PASSWORD: custom_pass
6 PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
7
8 cache:
9 paths:
10 - .cache/pip
11
12 stages:
13 - pre_testing
14 - testing
15 - post_testing
16
17 services:
18 - postgres:latest
19
20 closure_compiler:
21 image:
22 name: jborza/closure-compiler
23 entrypoint: ["/bin/sh", "-c"]
24
25 stage: pre_testing
26 script:
27 - /opt/cc.sh server/www/scripts
28
29 merge_conflict_check:
30 image: python:3
31 stage: pre_testing
32 allow_failure: true
33 script:
34 - git config --global user.email "[email protected]"
35 - git config --global user.name "Mergerbot"
36 - python3 merge-conflict-detector.py
37
38 pylint:
39 image: registry.gitlab.com/faradaysec/faraday/faraday_testing_base # I just need an image with python-dev and python-pip
40 stage: pre_testing
41 script:
42 - pip install pylint anybadge
43 - pylint server |tee pylint.txt || true
44 - score=$(sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' pylint.txt)
45 - anybadge --label pylint --value=$score --file pylint.svg 4=red 6=orange 8=yellow 10=green
46 artifacts:
47 paths:
48 - pylint.svg
49
50 postgresql_test:
51 image: registry.gitlab.com/faradaysec/faraday/faraday_testing_base
52 stage: testing
53 coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
54 script:
55 - pip install virtualenv
56 - virtualenv -p python2 faraday_venv
57 - source faraday_venv/bin/activate
58 - pip install --upgrade -r requirements_server.txt
59 - pip install --upgrade responses pytest-xdist pytest-cov
60 - pip install --upgrade -r requirements_dev.txt
61 - mkdir -p ~/.faraday/config
62 - cp test_cases/data/server.ini ~/.faraday/config
63 - sed -i 's/mapped_table/persist_selectable/' faraday_venv/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py # TODO remove when flask_sqlalchemy fixes the issue
64 - pytest test_cases -v --cov=server --connection-string=postgresql+psycopg2://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres/$POSTGRES_DB
65 artifacts:
66 when: on_failure
67 paths:
68 - ~/.faraday/logs/faraday-server.log
69
70 sqlite_test:
71 image: registry.gitlab.com/faradaysec/faraday/faraday_testing_base
72 stage: testing
73 coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
74 script:
75 - pip install virtualenv
76 - virtualenv -p python2 faraday_venv
77 - source faraday_venv/bin/activate
78 - pip install --upgrade -r requirements_server.txt
79 - pip install --upgrade responses pytest-xdist pytest-cov
80 - pip install --upgrade -r requirements_dev.txt
81 - mkdir -p ~/.faraday/config
82 - cp test_cases/data/server.ini ~/.faraday/config
83 - sed -i 's/mapped_table/persist_selectable/' faraday_venv/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py # TODO remove when flask_sqlalchemy fixes the issue
84 - pytest test_cases -v --cov=server --color=yes
85
86 generate_release_file:
87 image: python:3
88 stage: post_testing
89 allow_failure: true
90 script:
91 - apt-get update -qy
92 - apt-get install -y python-dev python-pip
93 - pip install packaging
94 - cd CHANGELOG && python3 changelog.py
95 artifacts:
96 paths:
97 - CHANGELOG/RELEASE.md
98 only:
99 variables:
100 - $FULL_DOC == "True"
101 - $RELEASE_FILE == "True"
102 - $CI_COMMIT_REF_NAME =~ /^.*\/(dev|master)$/
103
104 # This is a test of future test case that will be scheduled
105 i_do_nothing:
106 image: python:3
107 stage: post_testing
108 allow_failure: true
109 script:
110 - apt-get update -qy
111 - apt-get install -y python-dev python-pip
112 only:
113 variables:
114 - $FULL_TEST == "True"
115 - $I_DO_NOTH_TEST == "True"
116
117
118 # This is a test of future test case that will be scheduled
119 i_do_nothing2:
120 image: python:3
121 stage: post_testing
122 allow_failure: true
123 script:
124 - apt-get update -qy
125 - apt-get install -y python-dev python-pip
126 only:
127 variables:
128 - $FULL_TEST == "True"
129 - $I_DO_NOTH2_TEST == "True"
0 Feb 21th, 2019
0 * Fix CSRF (Cross-Site Request Forgery) vulnerability in vulnerability attachments API.
1 This allowed an attacker to upload evidence to vulns. He/she required to know the
2 desired workspace name and vulnerability id so it complicated the things a bit. We
3 classified this vuln as a low impact one.
4 * Readonly and disabled workspaces
5 * Add fields 'impact', 'easeofresolution' and 'policyviolations' to vulnerability_template
6 * Add pagination in 'Command history', 'Last Vulnerabilities', 'Activity logs' into dashboard
7 * Add status_code field to web vulnerability
8 * Preserve selection after bulk edition of vulnerabilities in the Web UI
9 * Faraday's database will be created using UTF-8 encoding
10 * Fix bug of "select a different workspace" from an empty list loop.
11 * Fix bug when creating duplicate custom fields
12 * Fix bug when loading in server.ini with extra configs
13 * Fix `./manage.py command`. It wasn't working since the last schema migration
14 * `./manage.py createsuperuser` command renamed to `./manage.py create-superuser`
15 * Fix bug when non-numeric vulnerability IDs were passed to the attachments API
16 * Fix logic in search exploits
17 * Add ability to 'Searcher' to execute rules in loop with dynamic variables
18 * Send searcher alert with custom mail
19 * Add gitlab-ci.yml file to execute test and pylint on gitlab runner
20 * Fix 500 error when updating services and vulns with specific read-only parameters set
21 * Fix SQLMap plugin to support newer versions of the tool
22 * Improve service's parser for Lynis plugin
23 * Fix bug when parsing URLs in Acunetix reports
24 * Fix and update NetSparker Plugin
25 * Fix bug in nessus plugin. It was trying to create a host without IP. Enabled logs on the server for plugin processing (use --debug)
26 * Fix bug when parsing hostnames in Nessus reports
27 * Fix SSLyze report automatic detection, so reports can be imported from the web ui
28 * Update Dnsmap Plugin
77 New features in the latest update
88 =====================================
99
10
11 3.6 [Feb 21th, 2019]:
12 ---
13 * Fix CSRF (Cross-Site Request Forgery) vulnerability in vulnerability attachments API.
14 This allowed an attacker to upload evidence to vulns. He/she required to know the
15 desired workspace name and vulnerability id so it complicated the things a bit. We
16 classified this vuln as a low impact one.
17 * Readonly and disabled workspaces
18 * Add fields 'impact', 'easeofresolution' and 'policyviolations' to vulnerability_template
19 * Add pagination in 'Command history', 'Last Vulnerabilities', 'Activity logs' into dashboard
20 * Add status_code field to web vulnerability
21 * Preserve selection after bulk edition of vulnerabilities in the Web UI
22 * Faraday's database will be created using UTF-8 encoding
23 * Fix bug of "select a different workspace" from an empty list loop.
24 * Fix bug when creating duplicate custom fields
25 * Fix bug when loading in server.ini with extra configs
26 * Fix `./manage.py command`. It wasn't working since the last schema migration
27 * `./manage.py createsuperuser` command renamed to `./manage.py create-superuser`
28 * Fix bug when non-numeric vulnerability IDs were passed to the attachments API
29 * Fix logic in search exploits
30 * Add ability to 'Searcher' to execute rules in loop with dynamic variables
31 * Send searcher alert with custom mail
32 * Add gitlab-ci.yml file to execute test and pylint on gitlab runner
33 * Fix 500 error when updating services and vulns with specific read-only parameters set
34 * Fix SQLMap plugin to support newer versions of the tool
35 * Improve service's parser for Lynis plugin
36 * Fix bug when parsing URLs in Acunetix reports
37 * Fix and update NetSparker Plugin
38 * Fix bug in nessus plugin. It was trying to create a host without IP. Enabled logs on the server for plugin processing (use --debug)
39 * Fix bug when parsing hostnames in Nessus reports
40 * Fix SSLyze report automatic detection, so reports can be imported from the web ui
41 * Update Dnsmap Plugin
1042
1143 3.5 [Jan 16th, 2019]:
1244 ---
(New empty file)
77 New features in the latest update
88 =====================================
99
10
11 3.6 [Feb 21th, 2019]:
12 ---
13 * Fix CSRF (Cross-Site Request Forgery) vulnerability in vulnerability attachments API.
14 This allowed an attacker to upload evidence to vulns. He/she required to know the
15 desired workspace name and vulnerability id so it complicated the things a bit. We
16 classified this vuln as a low impact one.
17 * Readonly and disabled workspaces
18 * Add fields 'impact', 'easeofresolution' and 'policyviolations' to vulnerability_template
19 * Add pagination in 'Command history', 'Last Vulnerabilities', 'Activity logs' into dashboard
20 * Add status_code field to web vulnerability
21 * Preserve selection after bulk edition of vulnerabilities in the Web UI
22 * Faraday's database will be created using UTF-8 encoding
23 * Fix bug of "select a different workspace" from an empty list loop.
24 * Fix bug when creating duplicate custom fields
25 * Fix bug when loading in server.ini with extra configs
26 * Fix `./manage.py command`. It wasn't working since the last schema migration
27 * `./manage.py createsuperuser` command renamed to `./manage.py create-superuser`
28 * Fix bug when non-numeric vulnerability IDs were passed to the attachments API
29 * Fix logic in search exploits
30 * Add ability to 'Searcher' to execute rules in loop with dynamic variables
31 * Send searcher alert with custom mail
32 * Add gitlab-ci.yml file to execute test and pylint on gitlab runner
33 * Fix 500 error when updating services and vulns with specific read-only parameters set
34 * Fix SQLMap plugin to support newer versions of the tool
35 * Improve service's parser for Lynis plugin
36 * Fix bug when parsing URLs in Acunetix reports
37 * Fix and update NetSparker Plugin
38 * Fix bug in nessus plugin. It was trying to create a host without IP. Enabled logs on the server for plugin processing (use --debug)
39 * Fix bug when parsing hostnames in Nessus reports
40 * Fix SSLyze report automatic detection, so reports can be imported from the web ui
41 * Update Dnsmap Plugin
1042
1143 3.5 [Jan 16th, 2019]:
1244 ---
0 3.5.0
0 3.6.0
260260 try:
261261 models.create_host(WORKSPACE, host)
262262 except Exception as ex:
263 import ipdb; ipdb.set_trace()
263 print(ex)
264264 host = models.get_host(WORKSPACE, ip=host.getName())
265265
266266 if service is not None:
6363
6464
6565 DEFAULT_XML = os.path.dirname(__file__) + "/default.xml"
66 DEFAULT_SERVER_INI = os.path.join(os.path.dirname(__file__), "..", "server", "default.ini")
6667
6768
6869 class Configuration:
647648 def getInstanceConfiguration():
648649 global the_config
649650 if the_config is None:
651 faraday_dir = os.path.expanduser("~/.faraday")
652 if not os.path.exists(faraday_dir):
653 os.mkdir(faraday_dir)
650654 config_dir = os.path.expanduser("~/.faraday/config")
651655 if not os.path.exists(config_dir):
652656 os.mkdir(config_dir)
657
658 faraday_server_config = os.path.expanduser("~/.faraday/config/server.ini")
659 if not os.path.isfile(faraday_server_config):
660 shutil.copy(DEFAULT_SERVER_INI, faraday_server_config)
661
653662 faraday_user_config = os.path.expanduser("~/.faraday/config/user.xml")
654663 if not os.path.isfile(faraday_user_config):
655664 shutil.copy(DEFAULT_XML, faraday_user_config)
665
656666 if os.path.exists(os.path.expanduser("~/.faraday/config/user.xml")):
657667 the_config = Configuration(os.path.expanduser("~/.faraday/config/user.xml"))
658668 else:
11 <faraday>
22
33 <appname>Faraday - Penetration Test IDE</appname>
4 <version>3.5.0</version>
4 <version>3.6.0</version>
55 <debug_status>0</debug_status>
66 <font>-Misc-Fixed-medium-r-normal-*-12-100-100-100-c-70-iso8859-1</font>
77 <home_path>~/</home_path>
1919 from utils import dependencies
2020 from utils.user_input import query_yes_no
2121 from faraday import FARADAY_BASE
22 from utils.logs import setUpLogger
2223 from alembic.script import ScriptDirectory
2324 from alembic.config import Config
2425 from alembic.migration import MigrationContext
138139 if head_revision != context.get_current_revision():
139140 print('--' * 20)
140141 print('Missing migrations, please execute: \n\n')
141 print('python manage.py migrate --upgrade head')
142 print('python manage.py migrate')
142143 sys.exit(1)
143144
144145 def main():
145146 os.chdir(FARADAY_BASE)
147 check_alembic_version()
146148 check_postgresql()
147 check_alembic_version()
148149 parser = argparse.ArgumentParser()
149150 parser.add_argument('--ssl', action='store_true', help='enable HTTPS')
150151 parser.add_argument('--debug', action='store_true', help='run Faraday Server in debug mode')
164165 version='Faraday v{version}'.format(version=f_version))
165166
166167 args = parser.parse_args()
168 setUpLogger(args.debug)
167169
168170 if args.debug:
169171 server.utils.logger.set_logging_level(server.config.DEBUG)
168168 @click.option('--email', prompt=True, callback=validate_email)
169169 @click.option('--password', prompt=True, hide_input=True,
170170 confirmation_prompt=True)
171 def createsuperuser(username, email, password):
171 def create_superuser(username, email, password):
172172 with app.app_context():
173173 if db.session.query(User).filter_by(active=True).count() > 0:
174174 print("Can't create more users. The comumunity edition only allows one user. Please contact support for further information.")
237237 cli.add_command(initdb)
238238 cli.add_command(import_from_couchdb)
239239 cli.add_command(database_schema)
240 cli.add_command(createsuperuser)
240 cli.add_command(create_superuser)
241241 cli.add_command(sql_shell)
242242 cli.add_command(status_check)
243243 cli.add_command(create_tables)
381381 return "Core Impact"
382382 elif tag == "NexposeReport":
383383 return "NexposeFull"
384 elif tag == "ASSET_DATA_REPORT" or "SCAN":
384 elif tag in ("ASSET_DATA_REPORT", "SCAN"):
385385 return "Qualysguard"
386386 elif tag == "scanJob":
387387 return "Retina"
395395 return "Lynis"
396396 elif tag == "reconng":
397397 return "Reconng"
398 elif tag == "document":
399 if re.search("SSLyzeVersion", output) is not None:
400 return "Sslyze"
398401 else:
399402 return None
0 #!/usr/bin/env python3
1
2 # Faraday Penetration Test IDE
3 # Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
4 # See the file 'doc/LICENSE' for the license information
5
6 '''
7 Internal script used to detect merge conflicts to branch with
8 our propiertary code. Not useful if you don't have access to
9 the code of Faraday Professional or Faraday Corporate
10 '''
11
12 import os
13 import re
14 import sys
15 import subprocess
16 import logging
17 import argparse
18 from contextlib import contextmanager
19 from tempfile import mkdtemp
20 from shutil import rmtree
21
22 VERSIONS = ['white', 'pink', 'black']
23 BRANCH_FORMAT = 'origin/{}/dev'
24
25 @contextmanager
26 def chdir(directory):
27 """Context manager to work in the specified directory"""
28 current = os.getcwd()
29 os.chdir(directory)
30 yield
31 os.chdir(current)
32
33 @contextmanager
34 def temp_worktree(branch=None):
35 """Context manager that creates a temporal worktree and
36 changes the current working directory, and when finished
37 removes the dir and runs a git worktree prune"""
38 directory = mkdtemp()
39 cmd = ["git", "worktree", "add", directory]
40 if branch is not None:
41 cmd.append(branch)
42 subprocess.check_output(cmd)
43 with chdir(directory):
44 yield
45 rmtree(directory)
46 subprocess.check_output(['git', 'worktree', 'prune'])
47
48 def check_merge(dst_branch, cur_branch='HEAD'):
49 """Return a boolean indicating if the merge from cur_branch
50 to dst_branch will merge without causing conflicts that need
51 manual resolution"""
52 # https://stackoverflow.com/questions/501407/is-there-a-git-merge-dry-run-option
53 with temp_worktree(dst_branch):
54 exit_code = subprocess.call(
55 ['git', 'merge', '--no-commit', '--no-ff', cur_branch])
56 # Use call because it will have exit code 128 when there is nothing to
57 # abort
58 subprocess.call(['git', 'merge', '--abort'])
59 return exit_code == 0
60
61
62 def get_current_branch():
63 """Return the current branch of the current workspace"""
64 # https://stackoverflow.com/questions/6245570/how-to-get-the-current-branch-name-in-git
65 branch = subprocess.check_output(
66 ['git', 'rev-parse', '--abbrev-ref', 'HEAD']).decode().strip()
67 if branch == 'HEAD':
68 # Probably in a detached state inside gitlab CI
69 # Fallback to the branch name defined in an env var
70 branch = 'origin/' + os.environ['CI_COMMIT_REF_NAME']
71 return branch
72
73
74 def branch_exists(branch_name):
75 exit_code = subprocess.call(
76 ['git', 'rev-parse', '--verify', '--quiet', branch_name])
77 if exit_code == 0:
78 return True
79 elif exit_code == 1:
80 return False
81 else:
82 raise ValueError('Error when checking for branch existence')
83
84
85 def version_of_branch(branch_name):
86 """
87 >>> version_of_branch('tkt_white_this_is_not_a_pink_branch')
88 'white'
89 """
90 positions = {version: branch_name.find(version)
91 for version in VERSIONS}
92 if all((pos < 0) for pos in positions.values()):
93 # The branch name doesn't contain white, pink or black
94 return
95 positions = {version: pos
96 for (version, pos) in positions.items()
97 if pos >= 0}
98 return min(positions.keys(), key=positions.get)
99
100
101 def main(branch):
102 logging.getLogger().setLevel(getattr(logging, args.log_level.upper()))
103 logger = logging # TODO FIXME
104 logger.info('Checking merge conflicts for branch %s', branch)
105 version = version_of_branch(branch)
106 if version is None:
107 logger.error('Unknown version name. Exiting')
108 sys.exit(-1)
109
110 versions_to_test = VERSIONS[VERSIONS.index(version):]
111 branches_to_test = []
112 for target_version in versions_to_test:
113 overriden_branch = branch.replace(version, target_version)
114 if target_version != version and \
115 branch_exists(overriden_branch):
116 branches_to_test.append(overriden_branch)
117 break # Don't test merge to black if has overriden pink branch
118 else:
119 branches_to_test.append(BRANCH_FORMAT.format(target_version))
120
121 logging.info('Testing merges in branches %s' % branches_to_test)
122
123 success = True
124 cur_branch = branch
125 for dst_branch in branches_to_test:
126 result = check_merge(dst_branch, cur_branch)
127 if result:
128 logger.info("Merge into %s succeeded!", dst_branch)
129 else:
130 success = False
131 logger.error("Merge into %s failed :(", dst_branch)
132 print()
133 print()
134
135 if not success:
136 sys.exit(1)
137
138
139 if __name__ == "__main__":
140 parser = argparse.ArgumentParser()
141 parser.add_argument('-b', '--branch', default=get_current_branch())
142 parser.add_argument('-l', '--log-level', default='debug')
143 args = parser.parse_args()
144 main(args.branch)
0 """empty message
1
2 Revision ID: 2ca03a8feef5
3 Revises: 8a10ff3926a5
4 Create Date: 2019-01-15 13:02:21.000699+00:00
5
6 """
7 from alembic import op
8 import sqlalchemy as sa
9
10
11 # revision identifiers, used by Alembic.
12 revision = '2ca03a8feef5'
13 down_revision = '8a10ff3926a5'
14 branch_labels = None
15 depends_on = None
16
17
18 def upgrade():
19 op.add_column('workspace', sa.Column('readonly', sa.Boolean(), nullable=False, server_default='False'))
20
21
22 def downgrade():
23 op.drop_column('workspace', 'readonly')
350350 Return the server's json response as a dictionary.
351351 """
352352 host_properties = get_host_properties(host)
353 return server.create_host(workspace_name, command_id, **host_properties)
353 ip = host_properties.pop('ip', None)
354 if not ip:
355 logger.error('Trying to create host without ip')
356 return server.create_host(workspace_name, command_id, ip, **host_properties)
354357
355358
356359 @_ignore_in_changes
746749 # getId will wait until the id is not None
747750 timeout = 1
748751 retries = 1
749 max_retries = 6
752 max_retries = 4
750753 while retries <= max_retries and self.id is None:
751754 if timeout >= 8:
752755 logger.info('Retrying getID timeout {0}'.format(timeout))
2020 tup = (filt[0],filt[1])
2121 services.append(tup)
2222
23 return services
23 return services
24
25 def get_all_protocols():
26 protocols = [
27 'ip',
28 'tcp',
29 'udp',
30 'icmp',
31 'sctp',
32 'hopopt',
33 'igmp',
34 'ggp',
35 'ip-encap',
36 'st',
37 'egp',
38 'igp',
39 'pup',
40 'hmp',
41 'xns-idp',
42 'rdp',
43 'iso-tp4',
44 'dccp',
45 'xtp',
46 'ddp',
47 'idpr-cmtp',
48 'ipv6',
49 'ipv6-route',
50 'ipv6-frag',
51 'idrp',
52 'rsvp',
53 'gre',
54 'ipsec-esp',
55 'ipsec-ah',
56 'skip',
57 'ipv6-icmp',
58 'ipv6-nonxt',
59 'ipv6-opts',
60 'rspf cphb',
61 'vmtp',
62 'eigrp',
63 'ospfigp',
64 'ax.25',
65 'ipip',
66 'etherip',
67 'encap',
68 'pim',
69 'ipcomp',
70 'vrrp',
71 'l2tp',
72 'isis',
73 'fc',
74 'udplite',
75 'mpls-in-ip',
76 'hip',
77 'shim6',
78 'wesp',
79 'rohc',
80 'mobility-header'
81 ]
82
83 for item in protocols:
84 yield item
123123
124124 def __init__(self, item_node):
125125 self.node = item_node
126
127 self.url = self.get_text_from_subnode('StartURL')
128 url_data = urlsplit(self.url)
126 url_data = self.get_url(self.node)
129127
130128 self.protocol = url_data.scheme
131129 self.host = url_data.hostname
162160 level='ERROR')
163161 return None
164162 return host
163
164 def get_url(self, node):
165 url = self.get_text_from_subnode('StartURL')
166 url_data = urlsplit(url)
167 if not url_data.scheme:
168 # Getting url from subnode 'Crawler'
169 url_aux = get_attrib_from_subnode(node, 'Crawler', 'StartUrl')
170 url_data = urlsplit(url_aux)
171
172 return url_data
165173
166174
167175 class Item(object):
+0
-372
plugins/repo/burp/faraday-burp.rb less more
0 #!/usr/bin/ruby
1 ###
2 ## Faraday Penetration Test IDE
3 ## Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
4 ## See the file 'doc/LICENSE' for the license information
5 ###
6 #__author__ = "Francisco Amato"
7 #__copyright__ = "Copyright (c) 2014, Infobyte LLC"
8 #__credits__ = ["Francisco Amato", "Micaela Ranea Sanchez"]
9 #__version__ = "1.4.0"
10 #__maintainer__ = "Francisco Amato"
11 #__email__ = "[email protected]"
12 #__status__ = "Development"
13
14 require 'java'
15 require "xmlrpc/client"
16 require "pp"
17
18
19 PLUGINVERSION="Faraday v1.4 Ruby"
20 #Tested: Burp Professional v1.6.09
21
22 XMLRPC::Config.module_eval do
23 remove_const :ENABLE_NIL_PARSER
24 const_set :ENABLE_NIL_PARSER, true
25 end
26 java_import 'burp.IBurpExtender'
27 java_import 'burp.ITab'
28 java_import 'burp.IHttpListener'
29 java_import 'burp.IProxyListener'
30 java_import 'burp.IScannerListener'
31 java_import 'burp.IExtensionStateListener'
32 java_import 'burp.IExtensionHelpers'
33 java_import 'burp.IContextMenuFactory'
34 java_import 'java.net.InetAddress'
35 java_import 'javax.swing.JMenuItem'
36 java_import 'javax.swing.JCheckBox'
37 java_import 'javax.swing.JPanel'
38 java_import 'javax.swing.GroupLayout'
39 java_import 'javax.swing.event.DocumentListener'
40
41 class SimpleDocumentListener
42
43 # This is how we declare that this class implements the Java
44 # DocumentListener interface in JRuby:
45 include DocumentListener
46
47 attr_accessor :behavior
48
49 def initialize(&behavior)
50 self.behavior = behavior
51 end
52
53 def changedUpdate(event); behavior.call event; end
54 def insertUpdate(event); behavior.call event; end
55 def removeUpdate(event); behavior.call event; end
56
57 end
58
59
60 class BurpExtender
61 include IBurpExtender, IHttpListener, IProxyListener, IScannerListener, IExtensionStateListener,IContextMenuFactory, ITab
62
63 #
64 # implement IBurpExtender
65 #
66
67 def registerExtenderCallbacks(callbacks)
68
69 # keep a reference to our callbacks object
70 @callbacks = callbacks
71
72 # set our extension name
73 callbacks.setExtensionName(PLUGINVERSION)
74
75 # obtain our output stream
76 @stdout = java.io.PrintWriter.new(callbacks.getStdout(), true)
77
78 # create the tab
79
80 @import_current_vulns = javax.swing.JButton.new("Import current vulnerabilities")
81 @import_new_vulns = javax.swing.JCheckBox.new("Import new vulnerabilities")
82 @rpc_server_label = javax.swing.JLabel.new("Faraday RPC server:")
83 @rpc_server = javax.swing.JTextField.new()
84 @rpc_server_label.setLabelFor(@rpc_server)
85 @restore_btn = javax.swing.JButton.new("Restore configuration")
86 @save_btn = javax.swing.JButton.new("Save configuration")
87
88 @rpc_server.getDocument.addDocumentListener(
89 SimpleDocumentListener.new do
90 @server = XMLRPC::Client.new2(@rpc_server.getText())
91 end)
92
93 @restore_btn.addActionListener do |e|
94 restoreConfig()
95 end
96 @save_btn.addActionListener do |e|
97 saveConfig()
98 end
99 @import_current_vulns.addActionListener do |e|
100 importVulns()
101 end
102
103 restoreConfig()
104
105 @tab = javax.swing.JPanel.new()
106
107 @layout = javax.swing.GroupLayout.new(@tab)
108 @tab.setLayout(@layout)
109 @layout.setAutoCreateGaps(true)
110 @layout.setAutoCreateContainerGaps(true)
111 @layout.setHorizontalGroup(
112 @layout.createParallelGroup()
113 .addComponent(@import_current_vulns)
114 .addComponent(@import_new_vulns)
115 .addComponent(@rpc_server_label)
116 .addComponent(@rpc_server)
117 .addComponent(@restore_btn)
118 .addComponent(@save_btn)
119 )
120 @layout.setVerticalGroup(
121 @layout.createSequentialGroup()
122 .addComponent(@import_current_vulns)
123 .addComponent(@import_new_vulns)
124 .addComponent(@rpc_server_label)
125 .addComponent(@rpc_server)
126 .addComponent(@restore_btn)
127 .addComponent(@save_btn)
128 )
129 @layout.linkSize(javax.swing.SwingConstants.VERTICAL, @import_new_vulns, @rpc_server)
130
131 callbacks.addSuiteTab(self)
132
133 @helpers = callbacks.getHelpers()
134
135 @stdout.println(PLUGINVERSION + " Loaded.")
136 @stdout.println("RPCServer: " + @rpc_server.getText())
137 @stdout.println("Import new vulnerabilities detected: " + boolString(@callbacks.loadExtensionSetting("import_new_vulns")))
138 @stdout.println("------")
139
140 # Register a factory for custom context menu items
141 callbacks.registerContextMenuFactory(self)
142
143 # register ourselves as a Scanner listener
144 callbacks.registerScannerListener(self)
145
146 # register ourselves as an extension state listener
147 callbacks.registerExtensionStateListener(self)
148
149 end
150
151
152 #
153 # implement menu
154 #
155
156 # Create a menu item if the appropriate section of the UI is selected
157 def createMenuItems(invocation)
158
159 menu = []
160
161 # Which part of the interface the user selects
162 ctx = invocation.getInvocationContext()
163
164 # Sitemap history, Proxy History, Request views, and Scanner will show menu item if selected by the user
165 #@stdout.println('Menu TYPE: %s\n' % ctx)
166 if ctx == 5 or ctx == 6 or ctx == 2 or ctx == 7
167
168 faradayMenu = JMenuItem.new("Send to Faraday", nil)
169
170 faradayMenu.addActionListener do |e|
171 eventScan(invocation, ctx)
172 end
173
174 menu.push(faradayMenu)
175 end
176
177 return menu
178 end
179
180 #
181
182 # event click function
183 #
184 def eventScan(invocation, ctx)
185
186 #Scanner click
187 if ctx == 7
188 invMessage = invocation.getSelectedIssues()
189 invMessage.each do |m|
190 newScanIssue(m,ctx,true)
191 end
192 else
193 #Others
194 invMessage = invocation.getSelectedMessages()
195 invMessage.each do |m|
196 newScanIssue(m,ctx,true)
197 end
198 end
199 end
200
201 #
202 # implement IScannerListener
203 #
204 def newScanIssue(issue, ctx=nil, import=nil)
205
206 if import == nil && @import_new_vulns.isSelected() == false
207 #ignore new issues
208 return
209 end
210
211 host = issue.getHost()
212 port = issue.getPort().to_s()
213 url = issue.getUrl()
214 resolution = ""
215
216 begin
217 ip = InetAddress.getByName(issue.getHttpService().getHost()).getHostAddress()
218 rescue Exception => e
219 ip = host
220 end
221
222 if ctx == 5 or ctx == 6 or ctx == 2
223 issuename="Analyzing: "
224 severity="Information"
225 desc="This request was manually sent using burp"
226 else
227 desc = issue.getIssueDetail().to_s.empty? ? "" : "Detail\n" + issue.getIssueDetail().to_s
228 background = issue.getIssueBackground().to_s
229 if !background.empty?
230 desc.concat("Background\n").concat(background)
231 end
232 severity = issue.getSeverity().to_s
233 issuename = issue.getIssueName().to_s
234 resolution = issue.getRemediationBackground().to_s
235
236 desc = desc.gsub(/<(\/p|br\/|\/li|ul|ol)>/, "\n").gsub(/<li>/, "* ").gsub(/<\/?[^>]*>/, "")
237 resolution = resolution.gsub(/<(\/p|br\/|\/li)>/, "\n").gsub(/<li>/, "* ").gsub(/<\/?[^>]*>/, "")
238 end
239
240 @stdout.println("New scan issue host: " +host +",name:"+ issuename +",IP:" + ip)
241
242 begin
243 rt = @server.call("devlog", "[BURP] New issue generation")
244
245 h_id = @server.call("createAndAddHost",ip, "unknown", [host])
246 i_id = @server.call("createAndAddInterface",h_id, ip,"00:00:00:00:00:00", ip, "0.0.0.0", "0.0.0.0",[],
247 "0000:0000:0000:0000:0000:0000:0000:0000","00","0000:0000:0000:0000:0000:0000:0000:0000",
248 [],"",host)
249
250 s_id = @server.call("createAndAddServiceToInterface",h_id, i_id, issue.getProtocol(),"tcp",[port],"open")
251
252 path = ""
253 response = ""
254 request = ""
255 method = ""
256 param = ""
257
258 #Menu action
259 if ctx == 5 or ctx == 6 or ctx == 2
260 req = @helpers.analyzeRequest(issue.getRequest())
261
262 param = getParam(req)
263 issuename += "("+issue.getUrl().getPath()[0,20]+")"
264 path = issue.getUrl().to_s
265 request = issue.getRequest().to_s
266 method = req.getMethod().to_s
267 else #Scan event or Menu scan tab
268 unless issue.getHttpMessages().nil? #issues with request #IHttpRequestResponse
269 c = 0
270 issue.getHttpMessages().each do |m|
271 if c == 0
272 req = @helpers.analyzeRequest(m.getRequest())
273 path = m.getUrl().to_s
274 request = m.getRequest().to_s
275 method = req.getMethod().to_s
276 response = m.getResponse().to_s
277
278 param = getParam(req)
279 else
280 desc += "Request (" + c.to_s + "): " + m.getUrl().to_s
281 end
282
283 c = c + 1
284 end
285
286 if c == 0
287 path = issue.getUrl().to_s
288 end
289 end
290 end
291
292 #createAndAddVulnWebToService(host_id, service_id, name, desc, ref, severity, resolution, website, path, request, response,method,pname, params,query,category):
293 v_id = @server.call("createAndAddVulnWebToService",h_id, s_id, issuename,
294 desc,[],severity,resolution,host,path,request,
295 response,method,"",param,"","")
296
297
298 rescue XMLRPC::FaultException => e
299 puts "Error:"
300 puts e.faultCode
301 puts e.faultString
302 end
303 end
304
305 def extensionUnloaded()
306
307 end
308
309 def getTabCaption()
310 return "Faraday"
311 end
312
313 def getUiComponent()
314 return @tab
315 end
316
317 #
318 # convert integer to string
319 #
320 def boolString(value)
321 if value == "0"
322 return "false"
323 else
324 return "true"
325 end
326 end
327
328 #
329 # get param for one url
330 #
331 def getParam(value)
332 param = ""
333 value.getParameters().each do |p|
334 #TODO: Actually Get all parameters, cookies, jason, url, maybe we should get only url,get/post parameters
335 #http://portswigger.net/burp/extender/api/constant-values.html#burp.IParameter.PARAM_BODY
336 param += "%s" % p.getType() + ":" + p.getName() + "=" + p.getValue() + ","
337 end
338 return param
339
340 end
341
342 def restoreConfig(e=nil)
343 @import_new_vulns.setSelected(@callbacks.loadExtensionSetting("import_new_vulns") == "1")
344 @rpc_server.setText(@callbacks.loadExtensionSetting("rpc_server"))
345 if @rpc_server.getText().strip == ""
346 @rpc_server.setText("http://127.0.0.1:9876/")
347 end
348
349 #Connect Rpc server
350 @server = XMLRPC::Client.new2(@rpc_server.getText())
351
352 @stdout.println("Config restored.")
353 end
354
355 def saveConfig(e=nil)
356 @callbacks.saveExtensionSetting("import_new_vulns", @import_new_vulns.isSelected() ? "1" : "0")
357 @callbacks.saveExtensionSetting("rpc_server", @rpc_server.getText())
358 @stdout.println("Config saved.")
359 end
360
361
362 def importVulns()
363 # Get current vulnerabilities
364 @stdout.println("Importing vulns.")
365 rt = @server.call("devlog", "[BURP] Importing issues")
366 @callbacks.getScanIssues(nil).each do |issue|
367 newScanIssue(issue, 1, true)
368 end
369 end
370
371 end
3939 """
4040
4141 def __init__(self, output):
42 self.items = []
43 lists = output.split("\n\n")
4244
43 self.items = []
44 lists = output.split("\n")
45 for item in lists:
46 sub_domain = item.split('\n')
47 if len(sub_domain) == 2:
48 ip = self.clean_ip(sub_domain[1])
49 host = sub_domain[0]
50 self.parse_ip(ip, host)
51 elif len(sub_domain) > 2:
52 host = sub_domain.pop(0)
53 for ip_address in sub_domain:
54 ip = self.clean_ip(ip_address)
55 self.parse_ip(ip, host)
4556
46 for line in lists:
47 mitem = line.split(',')
48 if len(mitem) > 1:
49 item = {'host': mitem[0], 'ip': mitem[1]}
50 self.items.append(item)
57
58 def clean_ip(self, item):
59 ip = item.split(':', 1)
60 return ip[1].strip()
61
62 def parse_ip(self, ip_address, hostname):
63 data = {}
64 exists = False
65 for item in self.items:
66 if ip_address in item['ip']:
67 item['hosts'].append(hostname)
68 exists = True
69
70 if not exists:
71 data['ip'] = ip_address
72 data['hosts'] = [hostname]
73 self.items.append(data)
5174
5275
5376 class DnsmapPlugin(core.PluginBase):
5881 core.PluginBase.__init__(self)
5982 self.id = "Dnsmap"
6083 self.name = "Dnsmap XML Output Plugin"
61 self.plugin_version = "0.0.2"
84 self.plugin_version = "0.3"
6285 self.version = "0.30"
6386 self.options = None
6487 self._current_output = None
6689 self._command_regex = re.compile(
6790 r'^(sudo dnsmap|dnsmap|\.\/dnsmap).*?')
6891
69 self.xml_arg_re = re.compile(r"^.*(-c\s*[^\s]+).*$")
92 self.xml_arg_re = re.compile(r"^.*(-r\s*[^\s]+).*$")
7093
7194 global current_path
7295
7396 self._output_file_path = os.path.join(
7497 self.data_path,
75 "%s_%s_output-%s.xml" % (
98 "%s_%s_output-%s.txt" % (
7699 self.get_ws(),
77100 self.id,
78101 random.uniform(1, 10)
90113 This method will discard the output the shell sends, it will read it
91114 from the xml where it expects it to be present.
92115 """
93
94116 parser = DnsmapParser(output)
95
96117 for item in parser.items:
97 h_id = self.createAndAddHost(item['ip'])
98 self.createAndAddInterface(
99 h_id,
100 item['ip'],
101 ipv4_address=item['ip'],
102 hostname_resolution=item['host'])
118 h_id = self.createAndAddHost(
119 item['ip'],
120 hostnames=item['hosts'])
103121
104122 return True
105123
111129 arg_match = self.xml_arg_re.match(command_string)
112130
113131 if arg_match is None:
114 return "%s -c %s \n" % (command_string, self._output_file_path)
132 return "%s -r %s \\n" % (command_string, self._output_file_path)
115133 else:
116134 return re.sub(arg_match.group(1),
117 r"-c %s" % self._output_file_path,
135 r"-r %s" % self._output_file_path,
118136 command_string)
119137
120138
1414 from collections import defaultdict
1515
1616 from plugins import core
17 from plugins.plugins_utils import filter_services
17 from plugins.plugins_utils import filter_services, get_all_protocols
1818
1919
2020 current_path = os.path.abspath(os.getcwd())
114114 add = False
115115 #if "localhost" in combo:
116116 if combo.count("|") > 1:
117 # Service with url, protocol and perhaps name
117118 items_service = combo.split('|')
118 if not items_service[0] in local_services and not items_service[0].startswith(':'):
119 elements_ip_port = items_service[0].split(':')
120 count = items_service[0].count(':')
121 protocol = items_service[1]
122 name = items_service[2]
119 if not self.local_service(items_service, local_services):
120 # self.aux_items will be an auxiliar list. We will use it...
121 # ...for poping the url and the protocol so that the last element...
122 # ... of the list, will be the name of the service
123 self.aux_items = filter(None, items_service)
124 elements_ip_port, count = self.get_ip_and_port(self.aux_items, remove_from_list=True)
125 protocol = self.get_protocol()
126 name = self.aux_items[0]
123127 add = True
124128
125129 if name == '-':
126130 details = self.search_service(elements_ip_port[1])
127131 name = details['name']
128132 elif combo.count('|') == 1:
133 # Service only with url
129134 items_service = combo.split('|')
130 if not items_service[0] in local_services and not items_service[0].startswith(':'):
131 count = items_service[0].count(':')
132 elements_ip_port = items_service[0].split(':')
135 if not self.local_service(items_service, local_services):
136 elements_ip_port, count = self.get_ip_and_port(items_service)
133137 details = self.search_service(elements_ip_port[1])
134138 protocol = details['protocol']
135139 name = details['name']
143147 name = details['name']
144148 add = True
145149
146 if add == True:
150 if add:
147151 ip, port = self.colon_count(count, elements_ip_port, items_service)
148152 elements_dict = {
149153 "ip":ip,
154158 return elements_dict
155159 else:
156160 return None
161
162 def local_service(self, service_data, local_services):
163 ip = self.get_ip_and_port(service_data)[0][0]
164 local = True
165 if not ip in local_services and not ip.startswith(':'):
166 local = False
167
168 return local
169
170 def get_ip_and_port(self, service_data, remove_from_list=False):
171 url_data = [url for url in service_data if ':' in url][0]
172 count = url_data.count(':')
173 ip_port = url_data.split(':')
174
175 if remove_from_list:
176 self.aux_items.remove(url_data)
177
178 return ip_port, count
179
180 def get_protocol(self):
181 # network_listen_port variables are different in .log and .dat reports
182 # .log: tcp4|127.0.0.1:5985|zabbix_age|
183 # .dat: 127.0.0.1:5985|tcp4|zabbix_age|
184 # This method will check if the protocol (from the function get_all_protocols())
185 # matches with the protocol that network_listen_port contains
186 protocols = get_all_protocols()
187 for item in protocols:
188 protocol = [p for p in self.aux_items if item in p.lower()]
189 if protocol:
190 self.aux_items.remove(protocol[0])
191 return protocol[0]
157192
158193 def search_service(self, port):
159194 srv = filter_services()
113113 if t.get('host-ip'):
114114 ip = t.get('host-ip')
115115
116 if not ip:
117 if not t.get_ips():
118 continue
119 ip = t.get_ips().pop()
120
116121 h_id = self.createAndAddHost(ip, t.get('operating-system'), hostnames=[host])
117122
118123 if self._isIPV4(ip):
119124 i_id = self.createAndAddInterface(
120 h_id, ip, mac, ipv4_address=ip, hostname_resolution=host)
125 h_id, ip, mac, ipv4_address=ip, hostname_resolution=[host])
121126 else:
122127 i_id = self.createAndAddInterface(
123 h_id, ip, mac, ipv6_address=ip, hostname_resolution=host)
128 h_id, ip, mac, ipv6_address=ip, hostname_resolution=[host])
124129
125130 srv = {}
126131 web = False
1414 import sys
1515 import socket
1616 import urllib
17 from bs4 import BeautifulSoup
1718
1819 try:
1920 import xml.etree.cElementTree as ET
112113 self.port = host.group(11)
113114
114115 self.name = self.get_text_from_subnode("type")
116 self.desc = self.get_text_from_subnode("description")
115117 self.severity = self.re_map_severity(self.get_text_from_subnode("severity"))
116118 self.certainty = self.get_text_from_subnode("certainty")
117119 self.method = self.get_text_from_subnode("vulnerableparametertype")
118120 self.param = self.get_text_from_subnode("vulnerableparameter")
119121 self.paramval = self.get_text_from_subnode("vulnerableparametervalue")
122 self.reference = self.get_text_from_subnode("externalReferences")
123 self.resolution = self.get_text_from_subnode("actionsToTake")
120124 self.request = self.get_text_from_subnode("rawrequest")
121125 self.response = self.get_text_from_subnode("rawresponse")
122126 if self.response:
123127 self.response = self.response.encode("ascii",errors="backslashreplace")
124128 if self.request:
125129 self.request = self.request.encode("ascii",errors="backslashreplace")
130 if self.reference:
131 self.reference = self.reference.encode("ascii",errors="backslashreplace")
132
126133
127134 self.kvulns = []
128135 for v in self.node.findall("knownvulnerabilities/knownvulnerability"):
142149 self.capec = self.get_text_from_subnode("CAPEC")
143150 self.pci = self.get_text_from_subnode("PCI")
144151 self.pci2 = self.get_text_from_subnode("PCI2")
152 self.node = item_node.find("classification/CVSS")
153 self.cvss = self.get_text_from_subnode("vector")
145154
146155 self.ref = []
147156 if self.cwe:
148157 self.ref.append("CWE-" + self.cwe)
149158 if self.owasp:
150159 self.ref.append("OWASP-" + self.owasp)
151
152 self.desc = ""
153 self.desc += "\nKnowVulns: " + \
160 if self.reference:
161 self.ref.extend(list(set(re.findall('https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', self.reference))))
162 if self.cvss:
163 self.ref.append(self.cvss)
164
165 self.data = ""
166 self.data += "\nKnowVulns: " + \
154167 "\n".join(self.kvulns) if self.kvulns else ""
155 self.desc += "\nWASC: " + self.wasc if self.wasc else ""
156 self.desc += "\nPCI: " + self.pci if self.pci else ""
157 self.desc += "\nPCI2: " + self.pci2 if self.pci2 else ""
158 self.desc += "\nCAPEC: " + self.capec if self.capec else ""
159 self.desc += "\nPARAM: " + self.param if self.param else ""
160 self.desc += "\nPARAM VAL: " + \
168 self.data += "\nWASC: " + self.wasc if self.wasc else ""
169 self.data += "\nCertainty: " + self.certainty if self.certainty else ""
170 self.data += "\nPCI: " + self.pci if self.pci else ""
171 self.data += "\nPCI2: " + self.pci2 if self.pci2 else ""
172 self.data += "\nCAPEC: " + self.capec if self.capec else ""
173 self.data += "\nPARAM: " + self.param if self.param else ""
174 self.data += "\nPARAM VAL: " + \
161175 repr(self.paramval) if self.paramval else ""
162 self.desc += "\nExtra: " + "\n".join(self.extra) if self.extra else ""
176 self.data += "\nExtra: " + "\n".join(self.extra) if self.extra else ""
163177
164178 def get_text_from_subnode(self, subnode_xpath_expr):
165179 """
210224 for i in parser.items:
211225 if first:
212226 ip = self.resolve(i.hostname)
213 h_id = self.createAndAddHost(ip)
214 i_id = self.createAndAddInterface(
215 h_id, ip, ipv4_address=ip, hostname_resolution=i.hostname)
216
217 s_id = self.createAndAddServiceToInterface(h_id, i_id, str(i.port),
218 str(i.protocol),
227 h_id = self.createAndAddHost(ip, hostnames=[ip])
228
229 s_id = self.createAndAddServiceToHost(h_id, str(i.port),
230 protocol = str(i.protocol),
219231 ports=[str(i.port)],
220232 status="open")
221
222 n_id = self.createAndAddNoteToService(
223 h_id, s_id, "website", "")
224 n2_id = self.createAndAddNoteToNote(
225 h_id, s_id, n_id, i.hostname, "")
226233 first = False
227
228 v_id = self.createAndAddVulnWebToService(h_id, s_id, i.name, ref=i.ref, website=i.hostname,
229 severity=i.severity, desc=i.desc, path=i.url, method=i.method,
230 request=i.request, response=i.response, pname=i.param)
234
235 v_id = self.createAndAddVulnWebToService(h_id, s_id, i.name, ref=i.ref, website=i.hostname,
236 severity=i.severity, desc=BeautifulSoup(i.desc, "lxml").text,
237 path=i.url, method=i.method, request=i.request, response=i.response,
238 resolution=BeautifulSoup(i.resolution, "lxml").text,pname=i.param, data=i.data)
231239
232240 del parser
233241
234242 def processCommandString(self, username, current_path, command_string):
235243 return None
236
237 def setHost(self):
238 pass
239
240244
241245 def createPlugin():
242246 return NetsparkerPlugin()
1616 import socket
1717 import sqlite3
1818 import sys
19 from urlparse import urlparse
1920 from BaseHTTPServer import BaseHTTPRequestHandler
2021 from StringIO import StringIO
2122 from urlparse import urlparse
4647 __status__ = "Development"
4748
4849
50 # This is the value of the HASHDB_MILESTONE_VALUE constant
51 # in the lib/core/settings.py file of sqlmap.
52 # If that value is changed in a newer version of SQLMap, it means that the
53 # hashdb mechanism has backwards-incompatible changes that probably will
54 # break our plugin, so the plugin will show an error and abort
55 SUPPORTED_HASHDB_VERSIONS = {
56 "dPHoJRQYvs", # 1.0.11
57 "BZzRotigLX", # 1.2.8
58 }
59
60
4961 class Database(object):
5062
5163 def __init__(self, database):
8395 self.id = "Sqlmap"
8496 self.name = "Sqlmap"
8597 self.plugin_version = "0.0.3"
86 self.version = "1.0.8.15#dev"
98 self.version = "1.2.8"
8799 self.framework_version = "1.0.0"
88100 self._current_output = None
89101 self.url = ""
141153 Helper function for restoring session data from HashDB
142154 """
143155
144 key = "%s%s%s" % (self.url or "%s%s" % (
145 self.hostname, self.port), key, self.HASHDB_MILESTONE_VALUE)
156 if self.HASHDB_MILESTONE_VALUE == 'dPHoJRQYvs':
157 # Support for old SQLMap versions
158 key = "%s%s%s" % (self.url or "%s%s" % (
159 self.hostname, self.port), key, self.HASHDB_MILESTONE_VALUE)
160 else:
161 url = urlparse(self.url)
162 key = '|'.join([
163 url.hostname,
164 url.path.strip('/'),
165 key,
166 self.HASHDB_MILESTONE_VALUE
167 ])
168
146169 retVal = ''
147
148170 hash_ = self.hashKey(key)
149171
150172 if not retVal:
395417 self.log('Remember set your Sqlmap Path Setting!... Abort plugin.', 'ERROR')
396418 return
397419
420 if HASHDB_MILESTONE_VALUE not in SUPPORTED_HASHDB_VERSIONS:
421 self.log(
422 "Your version of SQLMap is not supported with this plugin. "
423 "Please use an older version of SQLMap (the suggested one "
424 "is \"{}\"). Also, we suggest you to open issue in our GitHub "
425 "issue tracker: https://github.com/infobyte/faraday/issues/".format(self.version),
426 'ERROR')
427 return
428
398429 self.HASHDB_MILESTONE_VALUE = HASHDB_MILESTONE_VALUE
399430 self.HASHDB_KEYS = HASHDB_KEYS
400431 self.UNICODE_ENCODING = UNICODE_ENCODING
459490 self.hostname,
460491 '')
461492
462 db_port = 80
493 db_port = 0
463494 for item in self.db_port.keys():
464495 if dbms_version.find(item) >= 0:
465496 db_port = self.db_port[item]
9292 self.id = "Sslyze"
9393 self.name = "Sslyze Plugin"
9494 self.plugin_version = "0.0.1"
95 self.version = "1.4.2"
95 self.version = "2.0.6"
9696 self.framework_version = "1.0.0"
9797 self.options = None
9898 self._current_output = None
77 Flask-Security>=3.0.0
88 flask-session>=0.3.1
99 flask>=1.0
10 future>=0.17.1
1011 IPy>=0.83
1112 marshmallow>=2.15.3
1213 Pillow>=4.2.1
2122 sqlalchemy_schemadisplay>=1.3
2223 tqdm>=4.15.0
2324 twisted>=18.7.0
24 #webargs==5.0.0 fails with Python 2
25 webargs==4.4.1
25 webargs>=5.1.0
2626 marshmallow-sqlalchemy
2727 git+https://github.com/infobyte/filteralchemy@dev#egg=filteralchemy
2828 filedepot>=0.5.0
3232 websocket-client>=0.46.0
3333 attrs>=17.4.0
3434 Flask-Restless==0.17.0
35 simplejson>=3.16.0
2424 'object': "regex=^MS17-010",
2525 'conditions': ["creator=Nessus"],
2626 'actions': ["--UPDATE:refs=https://docs.microsoft.com/en-us/security-updates/securitybulletins/2017/ms17-010"]
27 },
28
29 {
30 'id': 'APPLY_MS_REFS_{{id}}',
31 'model': 'Vulnerability',
32 'object': "regex=^{{tag}}",
33 'conditions': ["creator=Nessus"],
34 'actions': ["--UPDATE:refs=https://docs.microsoft.com/en-us/security-updates/securitybulletins/{{year}}/{{tag}}"],
35 'values': [{'tag': 'ms17-010', 'year': '2017', 'id': 'DYNAMIC'}, {'tag': 'ms18-010', 'year': '2018', 'id': 'DYNAMIC'}]
36 },
37
38 {
39 'id': 'APPLY_REFS_ACCORDING_SEVERITY_{{sev}}',
40 'model': 'Vulnerability',
41 'object': "severity={{sev}}",
42 'actions': ["--UPDATE:refs=https://docs.microsoft.com/en-us/security-updates/securitybulletins/{{year}}/{{tag}}"],
43 'values': [{'tag': 'ms17-010', 'year': '2017', 'sev': 'low'}, {'tag': 'ms18-010', 'year': '2018', 'sev': 'med'}]
2744 }
2845 ]
1919 from email.mime.text import MIMEText
2020 import requests
2121 import json
22 import ast
2223 from config.configuration import getInstanceConfiguration
2324 from persistence.server import models
2425 from persistence.server import server
3637 CONF = getInstanceConfiguration()
3738
3839
40 mail_from = ''
41 mail_password = ''
42 mail_protocol = 'smtp.gmail.com'
43 mail_port = 587
44
45
3946 def send_mail(to_addr, subject, body):
40 from_addr = '[email protected]'
47 global mail_from, mail_password, mail_protocol, mail_port
48 from_addr = mail_from
4149 msg = MIMEMultipart()
4250 msg['From'] = from_addr
4351 msg['To'] = to_addr
4553
4654 msg.attach(MIMEText(body, 'plain'))
4755 try:
48 server_mail = smtplib.SMTP('smtp.gmail.com', 587)
56 server_mail = smtplib.SMTP(mail_protocol, mail_port)
4957 server_mail.starttls()
50 server_mail.login(from_addr, "faradaySearcher.2018")
58 server_mail.login(from_addr, mail_password)
5159 text = msg.as_string()
5260 server_mail.sendmail(from_addr, to_addr, text)
5361 server_mail.quit()
395403 return False
396404
397405 if isinstance(temp_value, list):
398 if value not in temp_value and str(value) not in temp_value and int(value) not in temp_value:
399 return False
406 if value not in temp_value and str(value) not in temp_value:
407 if not isinstance(value, int):
408 return False
409 elif int(value) not in temp_value:
410 return False
400411 return True
401412
402413 if isinstance(temp_value, bool):
522533 return True
523534
524535
536 def replace_rule(rule, value_item):
537 if value_item is None:
538 return rule
539
540 rule_str = json.dumps(rule)
541 r = re.findall("\{\{(.*?)\}\}", rule_str)
542 _vars = list(set(r))
543 for var in _vars:
544 value = value_item[var]
545 rule_str = rule_str.replace('{{'+var+'}}', value)
546
547 return ast.literal_eval(rule_str)
548
549
525550 def process_vulnerabilities(ws, vulns, _server):
526551 logger.debug("--> Start Process vulnerabilities")
527 for rule in rules:
528 if rule['model'] == 'Vulnerability':
529 vulnerabilities = get_models(ws, vulns, rule)
530 if 'fields' in rule:
531 process_models_by_similarity(ws, vulnerabilities, rule, _server)
532 else:
533 _objs_value = None
534 if 'object' in rule:
535 _objs_value = rule['object']
536 objects = get_object(vulnerabilities, _objs_value)
537 if objects is not None and len(objects) != 0:
538 if 'conditions' in rule:
539 if can_execute_action(vulnerabilities, rule['conditions']):
552 for rule_item in rules:
553 if rule_item['model'] == 'Vulnerability':
554 count_values = 1
555 values = [None]
556 if 'values' in rule_item and len(rule_item['values']) > 0:
557 values = rule_item['values']
558 count_values = len(values)
559
560 for index in range(count_values):
561 rule = replace_rule(rule_item, values[index])
562 vulnerabilities = get_models(ws, vulns, rule)
563 if 'fields' in rule:
564 process_models_by_similarity(ws, vulnerabilities, rule, _server)
565 else:
566 _objs_value = None
567 if 'object' in rule:
568 _objs_value = rule['object']
569 objects = get_object(vulnerabilities, _objs_value)
570 if objects is not None and len(objects) != 0:
571 if 'conditions' in rule:
572 if can_execute_action(vulnerabilities, rule['conditions']):
573 execute_action(ws, objects, rule, _server)
574 else:
540575 execute_action(ws, objects, rule, _server)
541 else:
542 execute_action(ws, objects, rule, _server)
543576 logger.debug("<-- Finish Process vulnerabilities")
544577
545578
611644 parser.add_argument('-u', '--user', help='Faraday user', required=False, default="")
612645 parser.add_argument('-p', '--password', help='Faraday password', required=False, default="")
613646 parser.add_argument('-o', '--output', help='Choose a custom output directory', required=False)
647 parser.add_argument('-e', '--email', help='Custom email', required=False, default="[email protected]")
648 parser.add_argument('-ep', '--email_pass', help='Email password', required=False)
649 parser.add_argument('-mp', '--mail_protocol', help='Email protocol', required=False, default="smtp.gmail.com")
650 parser.add_argument('-pp', '--port_protocol', help='Port protocol', required=False, default=587)
614651 parser.add_argument('-l', '--log', help='Choose a custom log level', required=False)
615652 args = parser.parse_args()
616653
646683 loglevel = 'debug'
647684 if args.log:
648685 loglevel = args.log
686
687 global mail_from, mail_password, mail_protocol, mail_port
688
689 if args.email:
690 mail_from = args.email
691
692 if args.email_pass:
693 mail_password = args.email_pass
694
695 if args.mail_protocol:
696 mail_protocol = args.mail_protocol
697
698 if args.port_protocol:
699 mail_port = args.port_protocol
649700
650701 for d in [output, 'log/']:
651702 if not os.path.isdir(d):
712763 os.remove(lockf)
713764 exit(0)
714765
715 # except Exception as errorMsg:
716 # logger.error(errorMsg)
717 # os.remove(lockf)
718 # exit(0)
766 except Exception as errorMsg:
767 logger.error(errorMsg)
768 os.remove(lockf)
769 exit(0)
719770
720771
721772 if __name__ == "__main__":
77 ###
88
99 import re
10 import json
1011 import logging
1112 from rules import *
1213
8283 return True
8384
8485
86 def validate_values(values, rule, rule_id):
87 r = re.findall("\{\{(.*?)\}\}", json.dumps(rule))
88 _vars = list(set(r))
89 keys = []
90 for index, item in enumerate(values):
91 if index != 0:
92 if len(values[index - 1]) != len(values[index]):
93 logger.error("Each value item must be equal in rule: %s" % rule_id)
94 return False
95 keys = item.keys()
96
97 for var in _vars:
98 if var not in keys:
99 logger.error("Variable '%s' should has a value in rule: %s" % (var, rule_id))
100 return False
101 return True
102
103
85104 def validate_action(actions):
86105 if len(actions) == 0:
87106 return False
133152 return False
134153 return True
135154
155 if key == 'values':
156 return validate_function(dictionary[key], dictionary, rule_id)
157
136158 if not validate_function(dictionary[key]):
137159 logger.error("ERROR: Key %s has an invalid value in rule: %s" % (key, rule_id))
138160 return False
168190 if not validate('actions', rule, validate_action, rule_id):
169191 return False
170192
193 if not validate('values', rule, validate_values, rule_id, mandatory=False):
194 return False
195
171196 logger.info('<-- Rules OK')
172197 return True
345345 route_prefix = '/v2/ws/<workspace_name>/'
346346 base_args = ['workspace_name'] # Required to prevent double usage of <workspace_name>
347347
348 def _get_workspace(self, workspace_name, to_edit=True):
348 def _get_workspace(self, workspace_name):
349349 try:
350350 ws = Workspace.query.filter_by(name=workspace_name).one()
351 if to_edit and not ws.active:
351 if not ws.active:
352352 flask.abort(403, "Disabled workspace: %s" % workspace_name)
353353 except NoResultFound:
354354 flask.abort(404, "No such workspace: %s" % workspace_name)
357357 def _get_base_query(self, workspace_name):
358358 base = super(GenericWorkspacedView, self)._get_base_query()
359359 return base.join(Workspace).filter(
360 Workspace.id == self._get_workspace(workspace_name, to_edit=False).id)
360 Workspace.id == self._get_workspace(workspace_name).id)
361361
362362 def _get_object(self, object_id, workspace_name, eagerload=False):
363363 self._validate_object_id(object_id)
375375 """Overriden to pass the workspace name to the schema"""
376376 context.update(kwargs)
377377 return context
378
379 def before_request(self, name, *args, **kwargs):
380 sup = super(GenericWorkspacedView, self)
381 if hasattr(sup, 'before_request'):
382 sup.before_request(name, *args, **kwargs)
383 if (self._get_workspace(kwargs['workspace_name']).readonly and
384 flask.request.method not in ['GET', 'HEAD', 'OPTIONS']):
385 flask.abort(403, "Altering a readonly workspace is not allowed")
378386
379387
380388 class ListMixin(object):
4242 credentials = fields.Integer(attribute='credentials_count', dump_only=True)
4343 metadata = SelfNestedField(MetadataSchema())
4444 type = fields.Function(lambda obj: 'Service', dump_only=True)
45 summary = fields.String(dump_only=True)
4546
4647 def load_ports(self, value):
4748 if not isinstance(value, list):
88 FilterSet,
99 operators,
1010 )
11 from marshmallow import fields, ValidationError
11 from marshmallow import fields, ValidationError, Schema, post_load
1212 from marshmallow.validate import OneOf
1313
1414 import server.utils.logger
2020 PaginatedMixin,
2121 ReadWriteView,
2222 )
23 from server.models import VulnerabilityTemplate
24 from server.schemas import PrimaryKeyRelatedField, SeverityField
23 from server.models import VulnerabilityTemplate, Vulnerability
24 from server.schemas import PrimaryKeyRelatedField, SeverityField, SelfNestedField
2525
2626 vulnerability_template_api = Blueprint('vulnerability_template_api', __name__)
2727 logger = server.utils.logger.get_logger(__name__)
28
29
30 class ImpactSchema(Schema):
31 accountability = fields.Boolean(attribute='impact_accountability', default=False)
32 availability = fields.Boolean(attribute='impact_availability', default=False)
33 confidentiality = fields.Boolean(attribute='impact_confidentiality', default=False)
34 integrity = fields.Boolean(attribute='impact_integrity', default=False)
2835
2936
3037 class VulnerabilityTemplateSchema(AutoSchema):
3643 references = fields.Method('get_references', deserialize='load_references', required=True)
3744 refs = fields.List(fields.String(), dump_only=True, attribute='references')
3845 desc = fields.String(dump_only=True, attribute='description')
46 data = fields.String(attribute='data')
47 impact = SelfNestedField(ImpactSchema())
48 easeofresolution = fields.String(
49 attribute='ease_of_resolution',
50 validate=OneOf(Vulnerability.EASE_OF_RESOLUTIONS),
51 allow_none=True)
52 policyviolations = fields.List(fields.String,
53 attribute='policy_violations')
3954
4055 class Meta:
4156 model = VulnerabilityTemplate
4257 fields = ('id', '_id', '_rev', 'cwe', 'description', 'desc',
43 'exploitation', 'name', 'references', 'refs', 'resolution')
58 'exploitation', 'name', 'references', 'refs', 'resolution',
59 'impact', 'easeofresolution', 'policyviolations', 'data')
4460
4561 def get_references(self, obj):
4662 return ', '.join(map(lambda ref_tmpl: ref_tmpl.name, obj.reference_template_instances))
5975 if any(len(ref) == 0 for ref in references):
6076 raise ValidationError('Empty name detected in reference')
6177 return references
78
79 @post_load
80 def post_load_impact(self, data):
81 # Unflatten impact (move data[impact][*] to data[*])
82 impact = data.pop('impact', None)
83 if impact:
84 data.update(impact)
85 return data
6286
6387
6488 class VulnerabilityTemplateFilterSet(FilterSet):
77 from base64 import b64encode, b64decode
88
99 import flask
10 import wtforms
1011 from filteralchemy import Filter, FilterSet, operators
1112 from flask import request
1213 from flask import Blueprint
1314 from flask_classful import route
1415 from flask_restless.search import search
16 from flask_wtf.csrf import validate_csrf
1517 from marshmallow import Schema, fields, post_load, ValidationError
1618 from marshmallow.validate import OneOf
1719 from sqlalchemy.orm import aliased, joinedload, selectin_polymorphic, undefer
104106 policyviolations = fields.List(fields.String,
105107 attribute='policy_violations')
106108 refs = fields.List(fields.String(), attribute='references')
107 issuetracker = fields.Method(serialize='get_issuetracker')
109 issuetracker = fields.Method(serialize='get_issuetracker', dump_only=True)
108110 parent = fields.Method(serialize='get_parent', deserialize='load_parent', required=True)
109111 parent_type = MutableField(fields.Method('get_parent_type'),
110112 fields.String(),
278280 return query.filter(model.id == value)
279281
280282
283 class StatusCodeFilter(Filter):
284 def filter(self, query, model, attr, value):
285 return query.filter(model.status_code == value)
286
287
281288 class TargetFilter(Filter):
282289 def filter(self, query, model, attr, value):
283290 return query.filter(model.target_host_ip.ilike("%" + value + "%"))
377384 allow_none=True))
378385 pname = Filter(fields.String(attribute='parameter_name'))
379386 query = Filter(fields.String(attribute='query_string'))
387 status_code = StatusCodeFilter(fields.Int())
380388 params = Filter(fields.String(attribute='parameters'))
381389 status = Filter(fields.Function(
382390 deserialize=lambda val: 'open' if val == 'opened' else val,
570578 res['groups'] = [convert_group(group) for group in res['groups']]
571579 return res
572580
573 @route('/<vuln_id>/attachment/', methods=['POST'])
581 @route('/<int:vuln_id>/attachment/', methods=['POST'])
574582 def post_attachment(self, workspace_name, vuln_id):
583 try:
584 validate_csrf(request.form.get('csrf_token'))
585 except wtforms.ValidationError:
586 flask.abort(403)
575587 vuln_workspace_check = db.session.query(VulnerabilityGeneric, Workspace.id).join(
576588 Workspace).filter(VulnerabilityGeneric.id == vuln_id,
577589 Workspace.name == workspace_name).first()
626638 web_vulns_data = []
627639 return self._envelope_list(normal_vulns_data + web_vulns_data)
628640
629 @route('/<vuln_id>/attachment/<attachment_filename>/', methods=['GET'])
641 @route('/<int:vuln_id>/attachment/<attachment_filename>/', methods=['GET'])
630642 def get_attachment(self, workspace_name, vuln_id, attachment_filename):
631643 vuln_workspace_check = db.session.query(VulnerabilityGeneric, Workspace.id).join(
632644 Workspace).filter(VulnerabilityGeneric.id == vuln_id,
655667 else:
656668 flask.abort(404, "Vulnerability not found")
657669
658 @route('/<vuln_id>/attachment/<attachment_filename>/', methods=['DELETE'])
670 @route('/<int:vuln_id>/attachment/<attachment_filename>/', methods=['DELETE'])
659671 def delete_attachment(self, workspace_name, vuln_id, attachment_filename):
660672 vuln_workspace_check = db.session.query(VulnerabilityGeneric, Workspace.id).join(
661673 Workspace).filter(
5858 PrimaryKeyRelatedField('name', many=True, dump_only=True),
5959 fields.List(fields.String)
6060 )
61 active = fields.Boolean(dump_only=True)
6162
6263 create_date = fields.DateTime(attribute='create_date',
6364 dump_only=True)
7071 model = Workspace
7172 fields = ('_id', 'id', 'customer', 'description', 'active',
7273 'duration', 'name', 'public', 'scope', 'stats',
73 'create_date', 'update_date')
74 'create_date', 'update_date', 'readonly')
7475
7576 @post_load
7677 def post_load_duration(self, data):
106107 objects.append(workspace_stat)
107108 return self._envelope_list(self._dump(objects, kwargs, many=True))
108109
110 def _get_querystring_boolean_field(self, field_name, default=None):
111 try:
112 val = bool(json.loads(flask.request.args[field_name]))
113 except (KeyError, ValueError):
114 val = default
115 return val
116
109117 def _get_base_query(self, object_id=None):
110 try:
111 confirmed = bool(json.loads(flask.request.args['confirmed']))
112 except (KeyError, ValueError):
113 confirmed = None
114 try:
115 active = bool(json.loads(flask.request.args['active']))
116 query = Workspace.query_with_count(
117 confirmed,
118 active=active,
119 workspace_name=object_id)
120 except (KeyError, ValueError):
121 query = Workspace.query_with_count(
122 confirmed,
123 workspace_name=object_id)
118 confirmed = self._get_querystring_boolean_field('confirmed')
119 active = self._get_querystring_boolean_field('active')
120 readonly = self._get_querystring_boolean_field('readonly')
121 query = Workspace.query_with_count(
122 confirmed,
123 active=active,
124 readonly=readonly,
125 workspace_name=object_id)
124126 return query
125127
126128 def _get_object(self, object_id, eagerload=False, **kwargs):
128130 Given the object_id and extra route params, get an instance of
129131 ``self.model_class``
130132 """
131 confirmed = flask.request.values.get('confirmed', None)
132 if confirmed:
133 try:
134 confirmed = bool(int(confirmed))
135 except ValueError:
136 if confirmed.lower() == 'false':
137 confirmed = False
138 elif confirmed.lower() == 'true':
139 confirmed = True
133 confirmed = self._get_querystring_boolean_field('confirmed')
134 active = self._get_querystring_boolean_field('active')
140135 self._validate_object_id(object_id)
141136 query = db.session.query(Workspace).filter_by(name=object_id)
137 if active is not None:
138 query = query.filter_by(active=active)
142139 query = query.options(
143140 with_expression(
144141 Workspace.vulnerability_web_count,
187184 self._report_ppath = os.path.join(self._report_path, "process")
188185 self._report_upath = os.path.join(self._report_path, "unprocessed")
189186
187 if not os.path.exists(CONF.getReportPath()):
188 os.mkdir(CONF.getReportPath())
189
190190 if not os.path.exists(self._report_path):
191191 os.mkdir(self._report_path)
192192
220220 db.session.commit()
221221 return changed
222222
223 @route('/<workspace_id>/change_readonly/', methods=["PUT"])
224 def change_readonly(self, workspace_id):
225 self._get_object(workspace_id).change_readonly()
226 db.session.commit()
227 return self._get_object(workspace_id).readonly
228
223229
224230 WorkspaceView.register(workspace_api)
55 import os
66 import string
77 import datetime
8 from future.builtins import range # __future__
89 from os.path import join, expanduser
910 from random import SystemRandom
1011
1415
1516 try:
1617 # py2.7
17 from configparser import ConfigParser, NoSectionError, NoOptionError
18 from configparser import ConfigParser, NoSectionError, NoOptionError, DuplicateSectionError
1819 except ImportError:
1920 # py3
20 from ConfigParser import ConfigParser, NoSectionError, NoOptionError
21 from ConfigParser import ConfigParser, NoSectionError, NoOptionError, DuplicateSectionError
2122
2223 import flask
2324 from flask import Flask, session, g
5152 os.mkdir(default_path)
5253 config = ConfigParser()
5354 config.read(server.config.LOCAL_CONFIG_FILE)
54 config.add_section('storage')
55 config.set('storage', 'path', default_path)
55 try:
56 config.add_section('storage')
57 config.set('storage', 'path', default_path)
58 except DuplicateSectionError:
59 logger.info('Duplicate section storage. skipping.')
5660 with open(server.config.LOCAL_CONFIG_FILE, 'w') as configfile:
5761 config.write(configfile)
5862
154158 config = ConfigParser()
155159 config.read(LOCAL_CONFIG_FILE)
156160 rng = SystemRandom()
157 secret_key = "".join([rng.choice(string.ascii_letters + string.digits) for _ in xrange(25)])
161 secret_key = "".join([rng.choice(string.ascii_letters + string.digits) for _ in range(25)])
158162 app.config['SECRET_KEY'] = secret_key
159 config.set('faraday_server', 'secret_key', secret_key)
163 try:
164 config.set('faraday_server', 'secret_key', secret_key)
165 except NoSectionError:
166 config.add_section('faraday_server')
167 config.set('faraday_server', 'secret_key', secret_key)
160168 with open(LOCAL_CONFIG_FILE, 'w') as configfile:
161169 config.write(configfile)
162170
3737 field_display_name = click.prompt('Display name')
3838 field_type = click.prompt('Field type (int, str, list)', type=click.Choice(['int', 'str', 'list']))
3939 custom_fields = db.session.query(CustomFieldsSchema)
40
41 #Checks the name of the fields wont be a duplicate
42 for custom_field in custom_fields:
43 if field_name == custom_field.field_name \
44 or field_display_name == custom_field.field_display_name:
45 print('Custom field already exists, skipping')
46 sys.exit(1)
47
4048 current_used_orders = set()
49
4150 if custom_fields.count():
4251 print('Custom field current order')
4352 for custom_field in custom_fields:
5968 invalid_field_order = True
6069 continue
6170 invalid_field_order = False
62 confirmation = click.prompt('New CustomField will be added to vulnerability -> Order {order} ({0},{1},{2}) <-, confirm to continue (yes/no)'.format(field_name, field_display_name, field_type, order=field_order))
71 confirmation = click.prompt('New CustomField will be added to vulnerability -> Order {order} ({0},{1},{2}) <-, confirm to continue (yes/no)'\
72 .format(field_name, field_display_name, field_type, order=field_order))
6373 if not confirmation:
6474 sys.exit(1)
6575
1111 import sys
1212 import click
1313 import psycopg2
14 from future.builtins import range # __future__
1415 from random import SystemRandom
1516 from tempfile import TemporaryFile
1617 from subprocess import Popen, PIPE
2425 FARADAY_BASE_CONFIG_XML,
2526 FARADAY_BASE,
2627 )
28 from server.utils.database import is_unique_constraint_violation
2729
2830 try:
2931 # py2.7
124126 already_created = False
125127 try:
126128 engine.execute("INSERT INTO \"faraday_user\" (username, name, password, "
127 "is_ldap, active, last_login_ip, current_login_ip, role) VALUES ('faraday', 'Administrator', "
128 "'{0}', false, true, '127.0.0.1', '127.0.0.1', 'admin');".format(random_password))
129 except sqlalchemy.exc.IntegrityError:
130 # when re using database user could be created previusly
131 already_created = True
132 print(
133 "{yellow}WARNING{white}: Faraday administrator user already exists.".format(
134 yellow=Fore.YELLOW, white=Fore.WHITE))
129 "is_ldap, active, last_login_ip, current_login_ip, role, state_otp) VALUES ('faraday', 'Administrator', "
130 "'{0}', false, true, '127.0.0.1', '127.0.0.1', 'admin', 'disabled');".format(random_password))
131 except sqlalchemy.exc.IntegrityError as ex:
132 if is_unique_constraint_violation(ex):
133 # when re using database user could be created previously
134 already_created = True
135 print(
136 "{yellow}WARNING{white}: Faraday administrator user already exists.".format(
137 yellow=Fore.YELLOW, white=Fore.WHITE))
138 else:
139 print(
140 "{yellow}WARNING{white}: Can't create administrator user.".format(
141 yellow=Fore.YELLOW, white=Fore.WHITE))
142 raise
135143 if not already_created:
136144
137145 self._save_user_xml(random_password)
174182
175183 def generate_random_pw(self, pwlen):
176184 rng = SystemRandom()
177 return "".join([rng.choice(string.ascii_letters + string.digits) for _ in xrange(pwlen)])
185 return "".join([rng.choice(string.ascii_letters + string.digits) for _ in range(pwlen)])
178186
179187 def _configure_new_postgres_user(self, psql_log_file):
180188 """
238246 postgres_command = []
239247
240248 print('Creating database {0}'.format(database_name))
241 command = postgres_command + ['createdb', '-O', username, database_name]
249 command = postgres_command + ['createdb', '-E', 'utf8', '-O', username, database_name]
242250 p = Popen(command, stderr=psql_log_file, stdout=psql_log_file, cwd='/tmp')
243251 p.wait()
244252 return_code = p.returncode
127127 section = ssl
128128 elif section_name == 'storage':
129129 section = storage
130 else:
131 return
130132 section.parse(__parser)
131133
132134
1111 import logging
1212 import datetime
1313 import multiprocessing
14
14 from future.builtins import range # __future__
1515
1616 import requests
1717 from requests.exceptions import HTTPError, RequestException
600600 rng = SystemRandom()
601601 password = "".join(
602602 [rng.choice(string.ascii_letters + string.digits) for _ in
603 xrange(12)])
603 range(12)])
604604 creator, created = get_or_create(session, User, username=username)
605605 if created:
606606 creator.active = False
11591159 policy_violation_id = Column(Integer, ForeignKey('policy_violation_template.id'), primary_key=True)
11601160
11611161 policy_violation = relationship("PolicyViolationTemplate", backref="policy_violation_template_associations", foreign_keys=[policy_violation_id])
1162 vulnerability = relationship("VulnerabilityTemplate", backref="policy_violation_template_vulnerability_associations",
1162 vulnerability = relationship("VulnerabilityTemplate", backref=backref("policy_violation_template_vulnerability_associations", cascade="all, delete-orphan"),
11631163 foreign_keys=[vulnerability_id])
11641164
11651165
13171317 customer = BlankColumn(String(250)) # TBI
13181318 description = BlankColumn(Text)
13191319 active = Column(Boolean(), nullable=False, default=True) # TBI
1320 readonly = Column(Boolean(), nullable=False, default=False) # TBI
13201321 end_date = Column(DateTime(), nullable=True)
13211322 name = NonBlankColumn(String(250), unique=True, nullable=False)
13221323 public = Column(Boolean(), nullable=False, default=False) # TBI
13381339 cascade="all, delete-orphan")
13391340
13401341 @classmethod
1341 def query_with_count(cls, confirmed, active=None, workspace_name=None):
1342 def query_with_count(cls, confirmed, active=True, readonly=None, workspace_name=None):
13421343 """
13431344 Add count fields to the query.
13441345
13671368 workspace.customer AS workspace_customer,
13681369 workspace.description AS workspace_description,
13691370 workspace.active AS workspace_active,
1371 workspace.readonly AS workspace_readonly,
13701372 workspace.end_date AS workspace_end_date,
13711373 workspace.name AS workspace_name,
13721374 workspace.public AS workspace_public,
14041406 if active is not None:
14051407 filters.append(" workspace.active = :active ")
14061408 params['active'] = active
1409 if readonly is not None:
1410 filters.append(" workspace.readonly = :readonly ")
1411 params['readonly'] = readonly
14071412 if workspace_name:
14081413 filters.append(" workspace.name = :workspace_name ")
14091414 params['workspace_name'] = workspace_name
14111416 query += ' WHERE ' + ' AND '.join(filters)
14121417 #query += " GROUP BY workspace.id "
14131418 query += " ORDER BY workspace.name ASC"
1414 return db.engine.execute(text(query), params)
1419 return db.session.execute(text(query), params)
14151420
14161421 def set_scope(self, new_scope):
14171422 return set_children_objects(self, new_scope,
14281433 # else:
14291434 # raise Cannot exceed or return false
14301435
1431
14321436 def deactivate(self):
14331437 if self.active is not False:
14341438 self.active = False
14351439 return True
14361440 return False
1441
1442 def change_readonly(self):
1443 self.readonly = not self.readonly
14371444
14381445
14391446 class Scope(Metadata):
310310 def is_unique_constraint_violation(exception):
311311 from server.models import db
312312 if db.engine.dialect.name != 'postgresql':
313 # Not implemened for RDMS other than postgres, we can't live without
314 # this, it is just an extra check
313 # Not implemened for RDMS other than postgres, we can live without
314 # this since it is just an extra check
315315 return True
316316 assert isinstance(exception.orig.pgcode, str)
317317 return exception.orig.pgcode == UNIQUE_VIOLATION
520520
521521 .last-item-field {
522522 margin-bottom: 25px;
523 }
524
525
526 /* ANGULAR SIMPLE PAGINATION */
527
528 .simple-pagination p {
529 display: inline-block;
530 line-height: 24px;
531 margin: 0 10px 0 0;
532 }
533
534 .simple-pagination__button {
535 background: #fff;
536 border: none;
537 padding: 0px 6px;
538 }
539
540 button[disabled], html input[disabled] {
541 cursor: default!important;
542 color: #ddd;
543 }
544
545 .simple-pagination__page-limit__option:after {
546 content: ' | ';
547 }
548
549 .simple-pagination__page-limit .active {
550 font-weight: bold;
551 }
552
553 .simple-pagination {
554 text-align: center;
555 margin-bottom: 15px;
556 }
557
558 .toogle-img-container {
559 width: 46px;
523560 }
100100 <script type="text/javascript" src="scripts/commons/directives/osintLink.js"></script>
101101 <script type="text/javascript" src="scripts/commons/directives/file.js"></script>
102102 <script type="text/javascript" src="scripts/commons/directives/fileModel.js"></script>
103 <script type="text/javascript" src="scripts/commons/directives/angular-simple-pagination.js"></script>
103104 <script type="text/javascript" src="scripts/commons/controllers/modal.js"></script>
104105 <script type="text/javascript" src="scripts/commons/controllers/headerCtrl.js"></script>
105106 <script type="text/javascript" src="scripts/commons/controllers/commercialCtrl.js"></script>
1111 'filter', 'angular-clipboard', 'ngCookies', 'cfp.hotkeys', 'chart.js',
1212 'ui.grid', 'ui.grid.selection', 'ui.grid.grouping', 'ngSanitize',
1313 'ui.grid.pagination', 'ui.grid.pinning', 'angularMoment', 'ui-notification',
14 'ui.grid.resizeColumns'])
14 'ui.grid.resizeColumns', 'angularSimplePagination'])
1515 .constant("BASEURL", (function() {
1616 var url = window.location.origin + "/";
1717 return url;
99 </div>
1010 </div>
1111 <div class="forbidden-error">
12 <h3>You don't have the permissions or It is either read-protected (workspace disabled)</h3>
12 <h3>You don't have the permissions or It is either read-protected (readonly workspace)</h3>
1313 </div>
1414 </div>
1515 </div><!-- #reports-main --></div><!-- .right-main -->
0 'use strict';
1
2 angular.module('angularSimplePagination', []).directive('simplePagination', SimplePagination);
3
4 function SimplePagination() {
5 return {
6 restrict: 'E',
7 scope: {
8 currentPage: '=',
9 offset: '=',
10 pageLimit: '=',
11 pageLimits: '=',
12 total: '=',
13 onUpdate: '&'
14 },
15 bindToController: true,
16 controller: SimplePaginationController,
17 controllerAs: 'pagination',
18 template: '\n <div class="simple-pagination">\n <p class="simple-pagination__items">Showing {{pagination.pageLimit}} out of {{pagination.total}}</p>\n <p>\n <button ng-click="pagination.previousPage()" ng-disabled="pagination.currentPage <= 0" class="simple-pagination__button simple-pagination__button--prev">\n &#10094;\n </button>\n <span class="simple-pagination__pages">{{pagination.currentPage + 1}} of {{pagination.getTotalPages()}}</span>\n <button ng-click="pagination.nextPage()" ng-disabled="pagination.currentPage === (pagination.getTotalPages() - 1)" class="simple-pagination__button simple-pagination__button--next">\n &#10095;\n </button>\n </p>\n <p class="simple-pagination__page-limit">\n <span class="simple-pagination__page-limit__option" ng-repeat="limit in pagination.pageLimits" ng-if="limit < pagination.total">\n <a href="" ng-click="pagination.setItemsPerPages(limit)" ng-class="{\'active\': pagination.isCurrentPageLimit(limit)}">{{limit}}</a>\n </span>\n <span>\n <a href="" ng-click="pagination.setItemsPerPages(pagination.total)" ng-class="{\'active\': pagination.isCurrentPageLimit(pagination.total)}">All</a>\n </span>\n </p>\n </div>\n '
19 };
20 }
21
22 function SimplePaginationController() {
23 var self = this;
24
25 self.currentPage = self.currentPage || 0;
26 self.pageLimit = self.pageLimit || self.pageLimits[0];
27
28 self.setItemsPerPages = function (max) {
29 self.pageLimit = max >= self.total ? self.total : max;
30 self.currentPage = 0;
31 self.offset = 0;
32 invokeCallback();
33 };
34
35 self.nextPage = function () {
36 self.currentPage += 1;
37 self.offset = self.currentPage * self.pageLimit;
38 invokeCallback();
39 };
40
41 self.previousPage = function () {
42 self.currentPage -= 1;
43 self.offset = self.currentPage * self.pageLimit;
44 invokeCallback();
45 };
46
47 self.getTotalPages = function () {
48 return Math.ceil(self.total / self.pageLimit);
49 };
50
51 self.isCurrentPageLimit = function (value) {
52 return self.pageLimit == value;
53 };
54
55 function invokeCallback() {
56 if (self.onUpdate) {
57 self.onUpdate();
58 }
59 }
60 }
1313 <table class="table-v3 licenses-list table table-responsive">
1414 <thead>
1515 <tr class="ui-grid-header">
16 <th class="ui-grid-cell-contents ui-grid-header-cell">Read only</th>
1617 <th class="ui-grid-cell-contents ui-grid-header-cell">Active</th>
1718 <th class="ui-grid-cell-contents ui-grid-header-cell">Name</th>
1819 <th class="ui-grid-cell-contents ui-grid-header-cell">Vulns</th>
2324 <tbody>
2425 <tr ng-repeat="ws in workspaces">
2526 <td class="ui-grid-cell-contents active-toggle">
26 <span ng-click="activeToggle(ws)" class="active-toggle-container" ng-class="{ disabled:ws.active === false }" uib-tooltip="{{(ws.active === true) ? 'Disable workspace' : 'Enable workspace'}}" tooltip-placement="right">
27 <img ng-src="{{ (ws.active) ? 'images/icon-list-confirmed.svg' : 'images/icon-list-notconfirmed.svg'}}" class="confirm-icon" ng-style="{ 'opacity': (ws.active === true) ? '1' : '0.7' }" />
28 </span>
27 <div class="toogle-img-container">
28 <span ng-click="readonlyToggle(ws)" class="active-toggle-container" ng-class="{ disabled:ws.readonly === false }" uib-tooltip="{{(ws.readonly === false) ? 'Workspace with all permissions' : 'Workspace read only'}}" tooltip-placement="right">
29 <img ng-src="{{ (ws.readonly == true) ? 'images/icon-list-confirmed.svg' : 'images/icon-list-notconfirmed.svg'}}" class="confirm-icon" ng-style="{ 'opacity': (ws.readonly === true) ? '1' : '0.7' }" />
30 </span>
31 </div>
32 </td>
33
34 <td class="ui-grid-cell-contents active-toggle">
35 <div class="toogle-img-container">
36 <span ng-click="activeToggle(ws)" class="active-toggle-container" ng-class="{ disabled:ws.active === false }" uib-tooltip="{{(ws.active === true) ? 'Disable workspace' : 'Enable workspace'}}" tooltip-placement="right">
37 <img ng-src="{{ (ws.active) ? 'images/icon-list-confirmed.svg' : 'images/icon-list-notconfirmed.svg'}}" class="confirm-icon" ng-style="{ 'opacity': (ws.active === true) ? '1' : '0.7' }" />
38 </span>
39 </div>
2940 </td>
3041 <td class="ui-grid-cell-contents">
3142 <span class="onhover upsize" ng-click="redirect(ws.name)">
417417 var putUrl = APIURL + "ws/" + wsName + "/deactivate/";
418418 return send_data(putUrl, undefined, false, "PUT");
419419 }
420
421 ServerAPI.readOnlyToogle = function (wsName) {
422 var putUrl = APIURL + "ws/" + wsName + "/change_readonly/";
423 return send_data(putUrl, undefined, false, "PUT");
424 }
425
420426
421427 ServerAPI.getWorkspaceSummary = function (wsName, confirmed) {
422428
99
1010 <div class="reports col-md-12 col-sm-12 col-xs-12">
1111 <div class="button-control justify-flex-start col-md-12 col-sm-12 col-xs-12">
12
1213 <div class="control-wrapper control-new">
13 <button id="new" type="button" class="btn btn-success btn-new" ng-title="{{workspaceData.active ? 'New credential' : 'Read-only. Workspace disabled'}}" ng-if="parentObject.parent_type" ng-click="new()" ng-disabled="!workspaceData.active">
14 <button id="new" type="button" class="btn btn-success btn-new" ng-title="{{workspaceData.readonly == false? 'New credential' : 'Read-only. Workspace disabled'}}" ng-if="parentObject.parent_type" ng-click="new()" ng-disabled="workspaceData.readonly == true">
1415 New
1516 </button>
1617 </div>
1718 <div class="control-wrapper control-edit">
18 <button type="button" class="btn edit" title="{{workspaceData.active ? 'Edit selected credentials' : 'Read-only. Workspace disabled'}}" ng-disabled="selectedCredentials().length != 1 || !workspaceData.active" ng-click="edit()">
19 <button type="button" class="btn edit" title="{{workspaceData.readonly == false ? 'Edit selected credentials' : 'Read-only. Workspace disabled'}}" ng-disabled="selectedCredentials().length != 1 || workspaceData.readonly == true" ng-click="edit()">
1920 <img src="images/icon-toolbar-edit.svg" class="edit-icon" />
2021 </button>
2122 </div>
2223 <div class="control-wrapper">
23 <button id="delete" type="button" class="btn" title="{{workspaceData.active ? 'Delete selected credentials' : 'Read-only. Workspace disabled'}}" ng-disabled="selectedCredentials().length == 0 || !workspaceData.active" ng-click="delete()">
24 <button id="delete" type="button" class="btn" title="{{workspaceData.readonly == false ? 'Delete selected credentials' : 'Read-only. Workspace disabled'}}" ng-disabled="selectedCredentials().length == 0 || workspaceData.readonly == true" ng-click="delete()">
2425 <img src="images/icon-toolbar-delete.svg" class="delete-icon" />
2526 </button>
2627 </div>
1313
1414 // Get last 15 commands
1515 var init = function () {
16
17 $scope.settings = {
18 currentPage: 0,
19 offset: 0,
20 pageLimit: 5,
21 pageLimits: ['3', '5', '10', '20', '30', '50', '80', '100']
22 };
23
1624 if ($routeParams.wsId !== undefined) {
1725 $scope.workspace = $routeParams.wsId;
18
19 $scope.isExpanded = false;
20 $scope.hideEmpty = false;
2126
2227 collapse();
2328
3742 };
3843
3944 var collapse = function () {
40 $scope.cmdLimit = 5;
45 $scope.settings.pageLimit = 5;
4146 $scope.isExpanded = false;
42 $scope.hideEmpty = false;
47 $scope.hideEmpty = true;
4348 angular.element('#first-row-panel').css('display', 'inherit');
4449 angular.element('#activities-container-row').addClass('mt-md');
4550 };
4651
4752 var expand = function () {
48 $scope.cmdLimit = 15; // Should be a constant
53 $scope.settings.pageLimit = 15;
4954 $scope.isExpanded = true;
55 $scope.hideEmpty = true;
5056 angular.element('#first-row-panel').css('display', 'none');
5157 angular.element('#activities-container-row').removeClass('mt-md');
5258 };
5561 return cmd.hosts_count === 0 && cmd.services_count === 0 && cmd.vulnerabilities_count === 0;
5662 };
5763
64 $scope.getValidCount = function () {
65 var count = 0;
66 for(var i = 0; i < vm.commands.length; i++){
67 if (!$scope.isEmpty(vm.commands[i])) count ++
68 }
69 return count;
70 };
71
5872 dashboardSrv.registerCallback(init);
5973 init();
6074 }]);
1919 $scope.commands = commands;
2020 });
2121 }
22
23 $scope.settings = {
24 currentPage: 0,
25 offset: 0,
26 pageLimit: 5,
27 pageLimits: ['5', '10', '20', '30', '50', '80', '100']
28 };
2229 };
2330
2431 // toggles sort field and order
2121 $scope.loadData();
2222 }, true);
2323 }
24
25 $scope.settings = {
26 currentPage: 0,
27 offset: 0,
28 pageLimit: 5,
29 pageLimits: ['3', '5', '10', '20', '30', '50', '80', '100']
30 };
2431 };
2532
2633 $scope.loadData = function() {
00 <article class='panel left-big-box' ng-controller="activityFeedCtrl as activityFeed">
1 <header ng-init="cmdLimit = 5">
1 <header>
22 <h2>Activity Feed
33 <span class="glyphicon glyphicon-info-sign" uib-tooltip="Faraday feed"></span>
44
2929
3030
3131 </header>
32 <div ng-if="activityFeed.commands.length === 0" class="no-info-overlay">
32 <div ng-if="activityFeed.commands.length === 0 || (getValidCount() === 0 && hideEmpty == true)" class="no-info-overlay">
3333 <p class="no-info-text">No activities found yet.</p>
3434 </div>
3535 <div class="ph-xl">
3636 <table id="commands" ng-if="activityFeed.commands.length > 0" class="tablesorter table table-striped">
3737 <tbody>
38 <tr ng-repeat="cmd in activityFeed.commands | orderBy : '-date' | limitTo:cmdLimit"
38 <tr ng-repeat="cmd in activityFeed.commands | orderBy : '-date' | limitTo:settings.pageLimit:settings.offset"
3939 ng-show="!isEmpty(cmd) || !hideEmpty">
4040 <td align="left" ng-class="{'hide-border-top':$first}">
4141 <span ng-if="cmd.import_source == 'report'">
6666 </tr>
6767 </tbody>
6868 </table>
69
70 <simple-pagination ng-if="isExpanded == true && hideEmpty == false"
71 current-page="settings.currentPage"
72 offset="settings.offset"
73 page-limit="settings.pageLimit"
74 page-limits="settings.pageLimits"
75 total="activityFeed.commands.length">
76 </simple-pagination>
77
6978 </div>
7079 </article>
99 <div class="ph-xl">
1010 <table id="" ng-if="commands.length > 0" class="tablesorter table table-striped last-vuln text-left">
1111 <thead>
12 <tr>
13 <th class="text-left"><span ng-click="cmdToggleSort('user')">By</span></th>
14 <th class="text-left"><span ng-click="cmdToggleSort('command')">Command</span></th>
15 <th class="text-left"><span ng-click="cmdToggleSort('date')">Start Date</span></th>
16 <th class="text-left"><span ng-click="cmdToggleSort('duration')">Duration</span></th>
17 </tr>
12 <tr>
13 <th class="text-left"><span ng-click="cmdToggleSort('user')">By</span></th>
14 <th class="text-left"><span ng-click="cmdToggleSort('command')">Command</span></th>
15 <th class="text-left"><span ng-click="cmdToggleSort('date')">Start Date</span></th>
16 <th class="text-left"><span ng-click="cmdToggleSort('duration')">Duration</span></th>
17 </tr>
1818 </thead>
1919 <tbody>
20 <tr ng-repeat="cmd in commands | orderObjectBy:cmdSortField:cmdSortReverse track by $index">
21 <td>
22 <b><p uib-tooltip="{{cmd.ip}}">{{cmd.user}}@{{cmd.hostname}}</p></b>
23 </td>
24 <td class="wrapword">
25 <span ng-if="cmd.import_source == 'report'">Import {{(cmd.tool == 'unknown' ? cmd.command : cmd.tool) + ': ' + cmd.params}}</span>
26 <span ng-if="cmd.import_source == 'shell'">{{cmd.command + ' ' + cmd.params}}</span>
27 </td>
28 <td am-time-ago="cmd.date" am-preprocess="utc"></td>
29 <td ng-bind="cmd.duration || 'undefined'"></td>
30 </tr>
20 <tr ng-repeat="cmd in commands | limitTo:settings.pageLimit:settings.offset | orderObjectBy:cmdSortField:cmdSortReverse track by $index ">
21 <td>
22 <b><p uib-tooltip="{{cmd.ip}}">{{cmd.user}}@{{cmd.hostname}}</p></b>
23 </td>
24 <td class="wrapword">
25 <span ng-if="cmd.import_source == 'report'">Import {{(cmd.tool == 'unknown' ? cmd.command : cmd.tool) + ': ' + cmd.params}}</span>
26 <span ng-if="cmd.import_source == 'shell'">{{cmd.command + ' ' + cmd.params}}</span>
27 </td>
28 <td am-time-ago="cmd.date" am-preprocess="utc"></td>
29 <td ng-bind="cmd.duration || 'undefined'"></td>
30 </tr>
3131 </tbody>
3232 </table>
33
34 <simple-pagination
35 current-page="settings.currentPage"
36 offset="settings.offset"
37 page-limit="settings.pageLimit"
38 page-limits="settings.pageLimits"
39 total="commands.length">
40 </simple-pagination>
41
3342 </div>
3443 </article>
1818 </tr>
1919 </thead>
2020 <tbody>
21 <tr ng-repeat="vuln in vulns">
21 <tr ng-repeat="vuln in vulns | limitTo:settings.pageLimit:settings.offset">
2222 <td><a href="" ng-click="navigate('/status/ws/'+workspace+'/search/severity='+vuln.severity)"><span class="label vuln fondo-{{vuln.severity}}">{{vuln.severity | uppercase}}</span></a></td>
2323 <td>
2424 <a href="" ng-click="navigate('/status/ws/'+workspace+'/search/target='+vuln.target)">{{vuln.target}}</a>
3030 </tr>
3131 </tbody>
3232 </table>
33
34 <simple-pagination
35 current-page="settings.currentPage"
36 offset="settings.offset"
37 page-limit="settings.pageLimit"
38 page-limits="settings.pageLimits"
39 total="vulns.length">
40 </simple-pagination>
41
3342 </div>
3443 </article>
269269 resolve: {
270270 service: function() {
271271 return $scope.selectedServices();
272 },
273 workspace: function () {
274 return $scope.workspaceData;
272275 }
273276 }
274277 });
88 <div id="reports-main" class="fila clearfix">
99 <div class="button-control col-md-12 col-sm-12 col-xs-12">
1010 <div class="control-wrapper control-new">
11 <button type="button" class="btn btn-success btn-new" title="{{workspaceData.active == false ? 'Read-only. Workspace disabled': 'New hosts'}}" ng-click="new()" ng-disabled="workspaceData.active === false">
11 <button type="button" class="btn btn-success btn-new" title="{{workspaceData.readonly == true ? 'Read-only. Workspace disabled': 'New hosts'}}" ng-click="new()" ng-disabled="workspaceData.readonly === true">
1212 New
1313 </button>
1414 </div>
1515 <div class="control-wrapper control-edit">
16 <button type="button" class="btn btn-default edit" title="{{workspaceData.active == false ? 'Read-only. Workspace disabled': 'Edit selected hosts'}}" ng-disabled="selectedHosts().length > 1 || workspaceData.active === false" ng-click="edit()">
16 <button type="button" class="btn btn-default edit" title="{{workspaceData.readonly == true ? 'Read-only. Workspace disabled': 'Edit selected hosts'}}" ng-disabled="selectedHosts().length > 1" ng-click="edit()">
1717 <img src="images/icon-toolbar-edit.svg" class="edit-icon" />
1818 </button>
1919 </div>
2020 <div class="control-wrapper">
21 <button type="button" class="btn btn-default" title="{{workspaceData.active == false ? 'Read-only. Workspace disabled': 'Delete selected items'}}" ng-click="delete()" ng-disabled="workspaceData.active === false">
21 <button type="button" class="btn btn-default" title="{{workspaceData.readonly == true ? 'Read-only. Workspace disabled': 'Delete selected items'}}" ng-click="delete()" ng-disabled="workspaceData.readonly === true">
2222 <img src="images/icon-toolbar-delete.svg" class="delete-icon" />
2323 </button>
2424 </div>
7979 <div class="modal-footer">
8080 <div class="modal-button">
8181 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
82 <button class="btn btn-success" ng-disabled="form.$invalid" ng-click="ok()">OK</button>
82 <button class="btn btn-success" ng-disabled="form.$invalid || workspace.readonly == true" ng-click="ok()">OK</button>
8383 </div>
8484 </div>
8585 </form>
22 <!-- See the file 'doc/LICENSE' for the license information -->
33
44 <div class="modal-header">
5 <h4 class="modal-title">This workspace does not exist. Select one of the list</h4>
5 <h3 class="modal-title"><p>This workspace does not exist.</p></h3><h4><p></This> Select one of the list or create a <a ng-click="cancel()" href="#/workspaces">new</a> one.</p></h4>
66 </div>
77 <div class="modal-body">
88 <ul class="ws-list">
33
44 angular.module('faradayApp')
55 .controller('serviceModalEdit',
6 ['$q', '$scope', '$modalInstance', '$routeParams', 'SERVICE_STATUSES', 'service', 'servicesManager', 'commonsFact',
7 function($q, $scope, $modalInstance, $routeParams, SERVICE_STATUSES, service, servicesManager, commonsFact) {
6 ['$q', '$scope', '$modalInstance', '$routeParams', 'SERVICE_STATUSES', 'service', 'servicesManager', 'commonsFact', 'workspace',
7 function($q, $scope, $modalInstance, $routeParams, SERVICE_STATUSES, service, servicesManager, commonsFact, workspace) {
88
99 init = function() {
1010 // current Workspace
1111 var ws = $routeParams.wsId;
12 $scope.workspace = workspace;
1213
1314 if(service.length == 1) {
1415 $scope.data = {
1010 <h3>Services for host {{hostName}}</h3>
1111 <div class="button-control col-md-12 col-sm-12 col-xs-12">
1212 <div class="control-wrapper control-new">
13 <button type="button" class="btn btn-success btn-new" title="New service" ng-click="new()" style="padding:0px 0px" ng-disabled="workspaceData.active === false">
13 <button type="button" class="btn btn-success btn-new" title="{{workspaceData.readonly == true ? 'Read-only. Workspace disabled': 'New service'}}" ng-click="new()" style="padding:0px 0px" ng-disabled="workspaceData.readonly === true">
1414 New service
1515 </button>
1616 </div>
1717 <div class="control-wrapper control-edit">
18 <button type="button" class="btn" title="Edit selected services" ng-click="edit()" ng-disabled="services.length == 0 || workspaceData.active === false">
18 <button type="button" class="btn" title="{{workspaceData.readonly == true ? 'Read-only. Workspace disabled': 'Edit selected services'}}" ng-click="edit()" ng-disabled="services.length == 0">
1919 <img src="images/icon-toolbar-edit.svg" class="edit-icon" />
2020 </button>
2121 </div>
2222 <div class="control-wrapper">
23 <button type="button" class="btn" title="Delete selected services" ng-click="delete()" ng-disabled="services.length == 0 || workspaceData.active === false">
23 <button type="button" class="btn" title="{{workspaceData.readonly == true ? 'Read-only. Workspace disabled': 'Delete selected services'}}" ng-click="delete()" ng-disabled="services.length == 0 || workspaceData.readonly === true">
2424 <img src="images/icon-toolbar-delete.svg" class="delete-icon" />
2525 </button>
2626 </div>
131131 <h3>
132132 Host details
133133 <span style="float:right">
134 <a class="btn btn-sm btn-default" href="#/host/ws/{{workspace}}/hid/{{host.id}}/edit" ng-if="!editing" ng-disabled="workspaceData.active === false">
134 <a class="btn btn-sm btn-default" href="#/host/ws/{{workspace}}/hid/{{host.id}}/edit" ng-if="!editing">
135135 <span class="glyphicon glyphicon-pencil"></span>
136136 Edit
137137 </a>
138 <button type="button" class="btn btn-sm btn-default" title="Delete host" ng-click="deleteHost()" ng-if="!editing" ng-disabled="workspaceData.active === false">
138 <button type="button" class="btn btn-sm btn-default" title="{{workspaceData.readonly == true ? 'Read-only. Workspace disabled': 'Delete host'}}" ng-click="deleteHost()" ng-if="!editing" ng-disabled="workspaceData.readonly === true">
139139 <span class="glyphicon glyphicon-trash"></span>
140140 Delete
141141 </button>
142142 <a class="btn btn-danger" href="#/host/ws/{{workspace}}/hid/{{host.id}}" ng-click="loadHosts()" ng-if="editing && !creating">Cancel</a>
143 <button class="btn btn-success" ng-disabled="form.$invalid" ng-click="ok()" ng-if="editing">OK</button>
143 <button class="btn btn-success" ng-disabled="form.$invalid || workspaceData.readonly == true" ng-click="ok()" ng-if="editing">OK</button>
144144 </span>
145145 </h3>
146146 <div class="form-horizontal">
219219 <div style="float: right">
220220 <a class="btn btn-danger" href="#/host/ws/{{workspace}}/hid/{{host.id}}" ng-click="loadHosts()" ng-if="editing && !creating">Cancel</a>
221221 <a class="btn btn-danger" href="#/hosts/ws/{{workspace}}" ng-if="editing && creating">Cancel</a>
222 <button class="btn btn-success" ng-disabled="form.$invalid" ng-click="ok()" ng-if="editing">OK</button>
222 <button class="btn btn-success" ng-disabled="form.$invalid || workspaceData.readonly == true" ng-click="ok()" ng-if="editing">OK</button>
223223 </div>
224224 </form>
225225 </div>
101101 </div><!-- .modal-body -->
102102 <div class="modal-footer">
103103 <div class="modal-button">
104 <button class="btn btn-success" ng-disabled="form.$invalid" ng-click="ok()">OK</button>
104 <button class="btn btn-success" ng-disabled="form.$invalid || workspace.readonly === true" ng-click="ok()">OK</button>
105105 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
106106 </div>
107107 </div>
55 .controller('modalEditCtrl',
66 ['$modalInstance', '$routeParams', 'EASEOFRESOLUTION', 'STATUSES', 'commonsFact',
77 'BASEURL', 'severities', 'vuln', 'vulnModelsManager', 'vulnsManager', 'referenceFact',
8 'encodeURIComponentFilter', 'customFields',
8 'encodeURIComponentFilter', 'customFields', 'workspace',
99 function ($modalInstance, $routeParams, EASEOFRESOLUTION, STATUSES, commonsFact,
1010 BASEURL, severities, vuln, vulnModelsManager, vulnsManager, referenceFact,
11 encodeURIComponent, customFields) {
11 encodeURIComponent, customFields, workspace) {
1212
1313 var vm = this;
1414
4747 vm.file_name_error = false;
4848
4949 vm.customFields = customFields;
50 vm.workspace = workspace;
5051
5152 vm.data = {
5253 _id: undefined,
6768 severity: undefined,
6869 method: "",
6970 path: "",
71 status_code: undefined,
7072 pname: "",
7173 params: "",
7274 query: "",
7777 params: "",
7878 parents: [], // a tuple with (parent_id, parent_type)
7979 path: "",
80 status_code: undefined,
8081 pname: "",
8182 policyviolations: [],
83 easeofresolution: null,
8284 query: "",
8385 refs: [],
8486 request: "",
102102 }
103103 });
104104
105 $scope.gridApi.selection.on.rowFocusChanged( $scope, function ( rowChanged ) {
106 $cookies.remove("selectedVulns");
107 });
108
105109 $scope.gridApi.pagination.on.paginationChanged($scope, function (pageNumber, pageSize) {
106110 // Save new page size in cookie
107111 $cookies.put("pageSize", pageSize);
128132 $scope.gridApi.core.on.rowsRendered($scope, function() {
129133 resizeGrid();
130134 recalculateLastVisibleColSize();
135 selectRowsByCookie();
131136 });
132137
133138 $scope.gridApi.colResizable.on.columnSizeChanged($scope, function (colDef, deltaChange) {
202207 "status": true,
203208 "website": false,
204209 "path": false,
210 "status_code": false,
205211 "request": false,
206212 "refs": false,
207213 "evidence": false,
235241 "status": "100",
236242 "website": "90",
237243 "path": "90",
244 "status_code": "90",
238245 "request": "90",
239246 "refs": "20",
240247 "_attachments": "100",
278285 angular.element($window).bind("resize", function () {
279286 resizeGrid();
280287 });
281 };
282
288
289 $cookies.remove("selectedVulns");
290 };
291
292
293 var selectRowsByCookie = function () {
294 var selectedVulns = $cookies.getObject("selectedVulns");
295 if (selectedVulns !== undefined) {
296 for (var i = 0; i < selectedVulns.length; i++){
297 for (var j = 0; j < $scope.gridOptions.data.length;j++){
298 if (selectedVulns[i] === $scope.gridOptions.data[j]._id){
299 $scope.gridApi.selection.selectRow($scope.gridOptions.data[j]);
300 }
301 }
302 }
303 }
304 };
283305
284306 var loadCustomFields = function () {
285307 var deferred = $q.defer();
404426 headerCellTemplate: header,
405427 sort: getColumnSort('path'),
406428 visible: $scope.columns["path"],
429 });
430 $scope.gridOptions.columnDefs.push({ name : 'status_code',
431 cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/defaultcolumn.html',
432 headerCellTemplate: header,
433 sort: getColumnSort('status_code'),
434 visible: $scope.columns["status_code"],
407435 });
408436 $scope.gridOptions.columnDefs.push({ name : 'request',
409437 cellTemplate: 'scripts/statusReport/partials/ui-grid/columns/resolutioncolumn.html',
579607 });
580608
581609 return response.filter(function(x){
582 return !angular.equals(x["exploitdb"], []) && !angular.equals(x["metasploit"], [])
610 return !angular.equals(x["exploitdb"], []) || !angular.equals(x["metasploit"], [])
583611 });
584612
585613 }, function(failed) {
818846 },
819847 customFields: function () {
820848 return $scope.customFields;
849 },
850 workspace: function () {
851 return $scope.workspaceData;
821852 }
822853 }
823854 });
850881 resolve: resolve
851882 });
852883 modal.result.then(function(data) {
884 var selectedVulns = [];
853885 $scope.getCurrentSelection().forEach(function(vuln) {
854886 obj = {};
855887 obj[property] = data;
856
888 selectedVulns.push(vuln._id);
857889 if (opts.callback != undefined){
858890 obj = opts.callback(vuln, data);
859891 }
865897 console.log("Error updating vuln " + vuln._id + ": " + errorMsg);
866898 });
867899 });
900
901 // Storage in cookies
902 $cookies.putObject("selectedVulns", selectedVulns);
868903 });
869904 }
870905
314314 </div><!-- .form-group -->
315315 </div>
316316
317 <div class="col-md-4">
317 <div class="col-md-3">
318318 <div class="tab-pane-header">Path</div>
319319 <div class="form-group">
320320 <label class="sr-only control-label" for="vuln-path">Path</label>
323323 </div><!-- .form-group -->
324324 </div>
325325
326 <div class="col-md-4">
326 <div class="col-md-2">
327 <div class="tab-pane-header">Status code</div>
328 <div class="form-group">
329 <label class="sr-only control-label" for="vuln-path">Status code</label>
330 <input type="text" class="form-control" id="vuln-status-code" placeholder="Status code"
331 ng-model="modal.data.status_code"/>
332 </div><!-- .form-group -->
333 </div>
334
335 <div class="col-md-3">
327336 <div class="tab-pane-header">Query</div>
328337 <div class="form-group">
329338 <label class="sr-only control-label" for="vuln-query">Query</label>
379388 </div>
380389
381390 <div id="tab-custom-fields" class="container tab-pane-container tab-pane fade"><br>
391 <div ng-if="modal.customFields.length === 0" class="no-info-overlay" style="margin-bottom: 15px;">
392 <p class="no-info-text">
393 No custom fields were found. To create one refer to our
394 <a href="https://github.com/infobyte/faraday/wiki/Custom-Fields" target="_blank">wiki page</a>.
395 </p>
396 </div>
382397 <div class="col-md-12 margin-bottom-15px">
383398 <div class="col-md-12" ng-repeat="cf in modal.customFields | orderBy : 'field_order'">
384399 <custom-field field="{{cf}}"></custom-field>
391406
392407 <div class="modal-footer">
393408 <div class="modal-button">
394 <button class="btn btn-success" ng-click="modal.ok()" ng-disabled="formEdit.$invalid">OK</button>
409 <button class="btn btn-success" ng-click="modal.ok()" ng-disabled="formEdit.$invalid || modal.workspace.readonly == true">OK</button>
395410 <button class="btn btn-danger" ng-click="modal.cancel()">Cancel</button>
396411 </div>
397412 </div>
8787 </label>
8888 <label class="radio-container"
8989 ng-click="modal.data.type = 'VulnerabilityWeb'"
90 title="Change type to Vulnerability Web">
91 <h5>Vulnerability Web</h5>
90 title="Change type to Web Vulnerability">
91 <h5>Web Vulnerability</h5>
9292 <input type="radio" name="radio" ng-checked="modal.data.type === 'VulnerabilityWeb'">
9393 <span class="checkmark"></span>
9494 </label>
395395 </div><!-- .form-group -->
396396 </div>
397397
398 <div class="col-md-4">
398 <div class="col-md-3">
399399 <div class="tab-pane-header">Path</div>
400400 <div class="form-group">
401401 <label class="sr-only control-label" for="vuln-path">Path</label>
404404 </div><!-- .form-group -->
405405 </div>
406406
407 <div class="col-md-4">
407 <div class="col-md-2">
408 <div class="tab-pane-header">Status code</div>
409 <div class="form-group">
410 <label class="sr-only control-label" for="vuln-path">Status code</label>
411 <input type="text" class="form-control" id="vuln-status-code" placeholder="Status code"
412 ng-model="modal.data.status_code"/>
413 </div><!-- .form-group -->
414 </div>
415
416 <div class="col-md-3">
408417 <div class="tab-pane-header">Query</div>
409418 <div class="form-group">
410419 <label class="sr-only control-label" for="vuln-query">Query</label>
461470 </div>
462471
463472 <div id="tab-custom-fields" class="container tab-pane-container tab-pane fade"><br>
464 <div class="col-md-12 margin-bottom-15px">
473 <div ng-if="modal.customFields.length === 0" class="no-info-overlay" style="margin-bottom: 15px;">
474 <p class="no-info-text">
475 No custom fields were found. To create one refer to our
476 <a href="https://github.com/infobyte/faraday/wiki/Custom-Fields" target="_blank">wiki page</a>.
477 </p>
478 </div>
479 <div class="col-md-12 margin-bottom-15px">
465480 <div class="col-md-12" ng-repeat="cf in modal.customFields | orderBy : 'field_order'">
466481 <custom-field field="{{cf}}"></custom-field>
467482 </div>
468 </div>
483 </div>
469484 </div>
470485 </div>
471486
88 <div id="reports-main" class="fila clearfix">
99 <div class="button-control col-md-12 col-sm-12 col-xs-12">
1010 <div class="control-wrapper control-new">
11 <button type="button" class="btn btn-success btn-new" title="{{workspaceData.active == false ? 'Read-only. Workspace disabled': 'New Vulns'}}" ng-click="new()" ng-disabled="workspaceData.active == false">
11 <button type="button" class="btn btn-success btn-new" title="{{workspaceData.readonly == true ? 'Read-only. Workspace disabled': 'New Vulns'}}" ng-click="new()" ng-disabled="workspaceData.readonly == true">
1212 New
1313 </button>
1414 </div>
1515 <div class="control-wrapper control-edit">
16 <button type="button" class="btn btn-default edit" title="{{workspaceData.active == false ? 'Read-only. Workspace disabled': 'Edit selected vulns'}}" ng-click="edit()" ng-disabled="getCurrentSelection().length != 1 || workspaceData.active == false">
16 <button type="button" class="btn btn-default edit" title="{{workspaceData.readonly == true ? 'Read-only. Workspace disabled': 'Edit selected vulns'}}" ng-click="edit()" ng-disabled="getCurrentSelection().length != 1">
1717 <img src="images/icon-toolbar-edit.svg" class="edit-icon" />
1818 </button>
19 <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="SingleActions" ng-show="getCurrentSelection().length === 1" ng-disabled="workspaceData.active == false">
19 <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="SingleActions" ng-show="getCurrentSelection().length === 1" ng-disabled="workspaceData.readonly == true">
2020 <span class="caret"></span>
2121 </button>
2222 <ul class="dropdown-menu dropdown-menu-right" role="menu">
2323 <li><a class="ws" ng-click="saveAsModel()">Create template</a></li>
2424 <li><a class="ws" ng-click="showExploits()">Search exploits</a></li>
2525 </ul>
26 <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="Actions" ng-hide="getCurrentSelection().length < 2" ng-disabled="workspaceData.active == false">
26 <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" title="Actions" ng-hide="getCurrentSelection().length < 2" ng-disabled="workspaceData.readonly == true">
2727 <span class="caret"></span>
2828 </button>
2929 <ul class="dropdown-menu dropdown-menu-right" role="menu" ng-show="getCurrentSelection().length >= 2">
5555 </ul>
5656 </div>
5757 <div class="control-wrapper">
58 <button type="button" class="btn btn-default" title="{{workspaceData.active == false ? 'Read-only. Workspace disabled': 'Delete selected items'}}" ng-click="delete()" ng-disabled="workspaceData.active == false">
58 <button type="button" class="btn btn-default" title="{{workspaceData.readonly == true? 'Read-only. Workspace disabled': 'Delete selected items'}}" ng-click="delete()" ng-disabled="workspaceData.readonly == true">
5959 <img src="images/icon-toolbar-delete.svg" class="delete-icon" />
6060 </button>
6161 </div>
7878 </button>
7979 </div>
8080 <div class="control-wrapper download-wrapper" ng-hide="fileUploadEnabled === true">
81 <button ng-click="enableFileUpload()" type="button" class="btn btn-default" title="{{workspaceData.active == false ? 'Read-only. Workspace disabled': 'Upload a report'}}" ng-disabled="workspaceData.active === false">
81 <button ng-click="enableFileUpload()" type="button" class="btn btn-default" title="{{workspaceData.readonly == true ? 'Read-only. Workspace disabled': 'Upload a report'}}" ng-disabled="workspaceData.readonly == true">
8282 <img src="images/icon-toolbar-upload.svg"/>
8383 </button>
8484 </div>
112112 var canAddToken = false;
113113 var withSpace = false;
114114
115 for (var i = 0; i < expression.length; i++){
116 switch (expression[i]){
115 for (var i = 0; i < expression.length; i++) {
116 switch (expression[i]) {
117117 case ' ':
118118 withSpace = true;
119119 if (isOpenQuotes === true)
139139 break;
140140
141141 default:
142 if(expression.substr(i, 3) === 'not' && expression.charAt(i-1) === ' '
143 && expression.charAt(i+3) === ' ' && !isOpenQuotes){
144 tokens.push('not');
145 i = i + 2;
146 canAddToken = true;
147 }
148
149 else if(expression.substr(i, 3) === 'and' && expression.charAt(i-1) === ' '
150 && expression.charAt(i+3) === ' ' && !isOpenQuotes){
151 tokens.push('and');
152 canAddToken = true;
153 i = i + 2;
154 }
155
156 else if(expression.substr(i, 2) === 'or' && expression.charAt(i-1) === ' '
157 && expression.charAt(i+2) === ' ' && !isOpenQuotes){
158 tokens.push('or');
159 canAddToken = true;
160 i++;
161 }else{
162 if((!isOpenQuotes && (withSpace || tokens.length === 0)) || canAddToken){
142 if (expression.substr(i, 3) === 'not' && expression.charAt(i - 1) === ' '
143 && expression.charAt(i + 3) === ' ' && !isOpenQuotes) {
144 tokens.push('not');
145 i = i + 2;
146 canAddToken = true;
147 }
148
149 else if (expression.substr(i, 3) === 'and' && expression.charAt(i - 1) === ' '
150 && expression.charAt(i + 3) === ' ' && !isOpenQuotes) {
151 tokens.push('and');
152 canAddToken = true;
153 i = i + 2;
154 }
155
156 else if (expression.substr(i, 2) === 'or' && expression.charAt(i - 1) === ' '
157 && expression.charAt(i + 2) === ' ' && !isOpenQuotes) {
158 tokens.push('or');
159 canAddToken = true;
160 i++;
161 } else {
162 if ((!isOpenQuotes && (withSpace || tokens.length === 0)) || canAddToken) {
163163 tokens.push(expression[i]);
164164 canAddToken = false;
165165 }
207207 }
208208 console.log(JSON.stringify(item));
209209 return item;
210 };
211
212
213 var processName = function (name) {
214 var processedName = "";
215 switch (name) {
216 case 'accountability':
217 case 'availability':
218 case 'confidentiality':
219 case 'integrity':
220 processedName = 'impact_' + name;
221 break;
222 case 'service':
223 processedName = 'service__name';
224 break;
225 case 'easeofresolution':
226 case 'ease_of_resolution':
227 processedName = 'ease_of_resolution';
228 break;
229 case 'web':
230 case 'type':
231 processedName = 'type';
232 break;
233 case 'creator':
234 processedName = 'creator_command_tool';
235 break;
236 case 'policy_violations':
237 case 'policyviolations':
238 processedName = 'policy_violations__name';
239 break;
240 case 'host_os':
241 processedName = 'host__os';
242 break;
243 case 'refs':
244 case 'ref':
245 processedName = 'references__name';
246 break;
247 case 'evidence':
248 processedName = 'evidence__filename';
249 break;
250 case 'params':
251 case 'parameters':
252 processedName = 'parameters';
253 break;
254 case 'pname':
255 case 'parameter_name':
256 processedName = 'parameter_name';
257 break;
258 case 'query':
259 case 'query_string':
260 processedName = 'query_string';
261 break;
262 case 'tags':
263 case 'tag':
264 processedName = 'tags__name';
265 break;
266 default:
267 processedName = name;
268 break;
269 }
270
271 return processedName;
272 };
273
274 var processOperator = function (name, operator) {
275 var processedOperator = "";
276 switch (name) {
277 case 'confirmed':
278 case 'impact_accountability':
279 case 'impact_availability':
280 case 'impact_confidentiality':
281 case 'impact_integrity':
282 case 'ease_of_resolution':
283 case 'type':
284 case 'id':
285 processedOperator = operator !== 'not' ? '==' : '!=';
286 break;
287 case 'severity':
288 case 'target':
289 processedOperator = operator !== 'not' ? 'eq' : '!=';
290 break;
291 case 'service__name':
292 case 'host__os':
293 processedOperator = operator !== 'not' ? 'has' : '!=';
294 break;
295 case 'policy_violations__name':
296 case 'references__name':
297 case 'evidence__filename':
298 case 'tags__name':
299 processedOperator = operator !== 'not' ? 'any' : '!=';
300 break;
301 default:
302 processedOperator = operator !== 'not' ? 'ilike' : '!=';
303 break;
304 }
305
306 return processedOperator;
210307 };
211308
212309 var processTerm = function (term, operator) {
221318 if (array.length === 2) {
222319 var name = array[0];
223320 var val = array[1].replace(/"/g, '');
224 var op = 'like';
225 if (operator !== 'not') {
226 if (name === 'confirmed' || name === 'accountability' || name === 'availability' || name === 'confidentiality' || name === 'integrity') {
227 if (name !== 'confirmed')
228 name = 'impact_' + name;
229
230 op = '==';
231 }
232
233 if (name === 'severity' || name === 'target'){
234 op = 'eq'
235 }
236 } else {
237 if (name === 'accountability' || name === 'availability' || name === 'confidentiality' || name === 'integrity') {
238 name = 'impact_' + name;
239 }
240 op = '!='
241 }
242
321 var op;
322
323 name = processName(name);
324 op = processOperator(name, operator);
243325
244326 if (val === 'info') val = 'informational';
245327 if (val === 'med') val = 'medium';
247329 res.name = name;
248330 res.op = op;
249331 res.val = val;
250 if (op === 'like') {
332 if (op === 'ilike' && name !== 'creator_command_tool') {
251333 res.val = '%' + val + '%';
252334 }
253335 return res
33
44 angular.module('faradayApp')
55 .controller('vulndDbModalEdit',
6 ['$scope', '$modalInstance', 'VulnModel', 'model', 'EXPLOITATIONS',
7 function($scope, $modalInstance, VulnModel, model, EXPLOITATIONS) {
6 ['$scope', '$modalInstance', 'VulnModel', 'model', 'EXPLOITATIONS', 'EASEOFRESOLUTION',
7 function($scope, $modalInstance, VulnModel, model, EXPLOITATIONS, EASEOFRESOLUTION) {
88
99 $scope.data;
1010 $scope.openedStart;
1111 $scope.openedEnd;
12 var EXCLUDED_TOKENS = [""];
1213
1314 var init = function() {
14 $scope.exploitations = EXPLOITATIONS
15 $scope.exploitations = EXPLOITATIONS;
16 $scope.easeofresolution = EASEOFRESOLUTION;
1517 $scope.data = new VulnModel;
1618 $scope.data.set(model);
19 $scope.impact = angular.copy($scope.data.impact);
20 $scope.policyviolations = clearList(angular.copy($scope.data.policyviolations), EXCLUDED_TOKENS);
21 $scope.references = clearList(angular.copy($scope.data.refs), EXCLUDED_TOKENS);
22 $scope.new_policyviolation = "";
23 $scope.new_reference = "";
1724 };
1825
1926 $scope.ok = function() {
27 $scope.data.impact = angular.copy($scope.impact);
28 $scope.data.policyviolations = angular.copy($scope.policyviolations);
29 $scope.data.refs = angular.copy($scope.references);
30 $scope.data.references = $scope.data.refs.join(',');
2031 $modalInstance.close($scope.data);
2132 };
2233
3142 $modalInstance.dismiss('cancel');
3243 };
3344
45 $scope.toggleImpact = function(key) {
46 $scope.impact[key] = !$scope.impact[key];
47 };
48
49 $scope.newPolicyViolation = function() {
50 if ($scope.new_policyviolation !== "") {
51 // we need to check if the policy violation already exists
52 if ($scope.policyviolations.filter(function(policyviolation) {return policyviolation === $scope.new_policyviolation}).length === 0) {
53 $scope.policyviolations.push($scope.new_policyviolation);
54 $scope.new_policyviolation = "";
55 }
56 }
57 };
58
59 $scope.newReference = function() {
60 if ($scope.new_reference !== "") {
61 // we need to check if the reference already exists
62 if ($scope.references.filter(function(reference) {return reference === $scope.new_reference}).length === 0) {
63 $scope.references.push($scope.new_reference);
64 $scope.new_reference = "";
65 }
66 }
67 };
68
69 var clearList = function (list, excludedTokens) {
70 for (var i = 0; i< list.length ; i++){
71 if (excludedTokens.indexOf(list[i]) > -1){
72 list.splice(i, 1);
73 }
74 }
75 return list;
76 };
77
3478 init();
3579 }]);
33
44 angular.module('faradayApp')
55 .controller('vulnModelModalNew',
6 ['$scope', '$modalInstance', 'VulnModel', 'vulnModelsManager', 'EXPLOITATIONS',
7 function($scope, $modalInstance, VulnModel, vulnModelsManager, EXPLOITATIONS) {
6 ['$scope', '$modalInstance', 'VulnModel', 'vulnModelsManager', 'EXPLOITATIONS', 'EASEOFRESOLUTION',
7 function($scope, $modalInstance, VulnModel, vulnModelsManager, EXPLOITATIONS, EASEOFRESOLUTION) {
88
99 $scope.data;
1010 $scope.exploitations;
1212
1313 var init = function() {
1414 $scope.exploitations = EXPLOITATIONS;
15 $scope.easeofresolution = EASEOFRESOLUTION;
1516 $scope.data = new VulnModel;
1617 $scope.models = vulnModelsManager.models;
1718 // $scope.exploitations = ['a'];
3940 $scope.data.model = $scope.other_model;
4041 }
4142
43 if ($scope.data.easeofresolution === ""){
44 $scope.data.easeofresolution = null;
45 }
46
4247 $modalInstance.close($scope.data);
4348 };
4449
4651 $modalInstance.dismiss('cancel');
4752 };
4853
54 $scope.toggleImpact = function(key) {
55 $scope.data.impact[key] = !$scope.data.impact[key];
56 };
57
58 $scope.newPolicyViolation = function() {
59 if ($scope.new_policyviolation !== "") {
60 // we need to check if the policy violation already exists
61 if ($scope.data.policyviolations.filter(function(policyviolation) {return policyviolation === $scope.new_policyviolation}).length === 0) {
62 $scope.data.policyviolations.push($scope.new_policyviolation);
63 $scope.new_policyviolation = "";
64 }
65 }
66 };
67
68 $scope.newReference = function() {
69 if ($scope.new_reference !== "") {
70 // we need to check if the reference already exists
71 if ($scope.data.references.filter(function(reference) {return reference === $scope.new_reference}).length === 0) {
72 $scope.data.references.push($scope.new_reference);
73 $scope.new_reference = "";
74 }
75 }
76 };
77
4978 init();
5079 }]);
126126 var counter = 0;
127127 $scope.loading = true;
128128 datas.forEach(function(data) {
129 if (!data.easeofresolution || data.easeofresolution === "")
130 data.easeofresolution = null;
129131 $scope.insert(data).then(function() {
130132 counter = counter + 1;
131133 if (length == counter) {
3030 </div><!-- .form-group -->
3131 <div class="form-group">
3232 <div class="col-md-12">
33 <label for="data">Data</label>
34 <textarea class="form-control" id="data" placeholder="Data" ng-model="data.data"></textarea>
35 </div>
36 </div><!-- .form-group -->
37 <div class="form-group">
38 <div class="col-md-12">
3339 <label for="resolution">Resolution</label>
3440 <textarea class="form-control" name="resolution" placeholder="Resolution" ng-model="data.resolution"></textarea>
3541 </div>
3642 </div><!-- .form-group -->
3743 <div class="form-group">
3844 <div class="col-md-12">
39 <label for="References">References</label>
40 <textarea class="form-control" name="References" placeholder="References" ng-model="data.references"></textarea>
45 <label for="references">References</label>
46 <div class="input-group margin-bottom-sm">
47 <label class="sr-only" for="references">References</label>
48 <input type="text" class="form-control" id="references" placeholder="References" ng-model="new_reference"/>
49 <span class="input-group-addon cursor" ng-click="newReference()"><i class="fa fa-plus-circle"></i></span>
50 </div>
51 </div>
52 <div class="col-md-12 reference" ng-repeat="reference in references">
53 <div class="input-group margin-bottom-sm">
54 <label class="sr-only" for="ireferences">References</label>
55 <input type="text" class="form-control" id="ireferences" placeholder="References" ng-model="reference" disabled/>
56 <span class="input-group-addon cursor" ng-click="references.splice($index, 1)"><i class="fa fa-minus-circle"></i></span>
57 </div>
4158 </div>
4259 </div><!-- .form-group -->
60
61 <div class="form-group">
62 <div class="col-md-12">
63 <label for="vuln-policyviolations">Policy Violations</label>
64 <div class="input-group margin-bottom-sm">
65 <label class="sr-only" for="vuln-policyviolations">Policy Violations</label>
66 <input type="text" class="form-control" id="vuln-policyviolations" placeholder="Policy Violations" ng-model="new_policyviolation"/>
67 <span class="input-group-addon cursor" ng-click="newPolicyViolation()"><i class="fa fa-plus-circle"></i></span>
68 </div>
69 </div>
70 <div class="col-md-12 reference" ng-repeat="policyviolation in policyviolations">
71 <div class="input-group margin-bottom-sm">
72 <label class="sr-only" for="vuln-policyviolations">Policy Violation</label>
73 <input type="text" class="form-control" id="vuln-policyviolations" placeholder="Policy Violation" ng-model="policyviolation" disabled/>
74 <span class="input-group-addon cursor" ng-click="policyviolations.splice($index, 1)"><i class="fa fa-minus-circle"></i></span>
75 </div>
76 </div>
77 </div><!-- .form-group -->
78
79 <div class="form-group">
80 <div class="col-md-6">
81 <label>Impact</label>
82 <h4><span ng-repeat="(key, value) in impact" class="normal-size" style="cursor: pointer;">
83 <span ng-class="{'label-impact label-default-impact': !value, 'label-impact label-success-impact': value}" ng-click="toggleImpact(key)">{{key}}</span>
84 </span></h4><!-- .normal-size -->
85 </div>
86 <div class="col-md-2"></div>
87 <div class="col-md-4">
88 <label>Ease of Resolution</label>
89 <select class="form-control" ng-model="data.easeofresolution" ng-options="e for e in easeofresolution" style="padding:0px 12px;">
90 <option value=""></option>
91 </select>
92 </div>
93 </div><!-- .form-group -->
94
4395 <div class="form-group">
4496 <div class="col-md-12">
4597 <label for="Explotation">Severity</label>
3030 </div><!-- .form-group -->
3131 <div class="form-group">
3232 <div class="col-md-12">
33 <label for="data">Data</label>
34 <textarea class="form-control" id="data" placeholder="Data" ng-model="data.data"></textarea>
35 </div>
36 </div><!-- .form-group -->
37 <div class="form-group">
38 <div class="col-md-12">
3339 <label for="resolution">Resolution</label>
3440 <textarea class="form-control" name="resolution" placeholder="Resolution" ng-model="data.resolution"></textarea>
3541 </div>
3642 </div><!-- .form-group -->
43
3744 <div class="form-group">
3845 <div class="col-md-12">
39 <label for="References">References</label>
40 <textarea class="form-control" name="References" placeholder="References" ng-model="data.references"></textarea>
46 <label for="references">References</label>
47 <div class="input-group margin-bottom-sm">
48 <label class="sr-only" for="references">References</label>
49 <input type="text" class="form-control" id="references" placeholder="References" ng-model="new_reference"/>
50 <span class="input-group-addon cursor" ng-click="newReference()"><i class="fa fa-plus-circle"></i></span>
51 </div>
52 </div>
53 <div class="col-md-12 reference" ng-repeat="reference in data.references">
54 <div class="input-group margin-bottom-sm">
55 <label class="sr-only" for="ireferences">References</label>
56 <input type="text" class="form-control" id="ireferences" placeholder="References" ng-model="reference" disabled/>
57 <span class="input-group-addon cursor" ng-click="data.references.splice($index, 1)"><i class="fa fa-minus-circle"></i></span>
58 </div>
4159 </div>
4260 </div><!-- .form-group -->
61
62
63 <div class="form-group">
64 <div class="col-md-12">
65 <label for="vuln-policyviolations">Policy Violations</label>
66 <div class="input-group margin-bottom-sm">
67 <label class="sr-only" for="vuln-policyviolations">Policy Violations</label>
68 <input type="text" class="form-control" id="vuln-policyviolations" placeholder="Policy Violations" ng-model="new_policyviolation"/>
69 <span class="input-group-addon cursor" ng-click="newPolicyViolation()"><i class="fa fa-plus-circle"></i></span>
70 </div>
71 </div>
72 <div class="col-md-12 reference" ng-repeat="policyviolation in data.policyviolations">
73 <div class="input-group margin-bottom-sm">
74 <label class="sr-only" for="vuln-policyviolations">Policy Violation</label>
75 <input type="text" class="form-control" id="vuln-policyviolations" placeholder="Policy Violation" ng-model="policyviolation" disabled/>
76 <span class="input-group-addon cursor" ng-click="data.policyviolations.splice($index, 1)"><i class="fa fa-minus-circle"></i></span>
77 </div>
78 </div>
79 </div><!-- .form-group -->
80
81 <div class="form-group">
82 <div class="col-md-6">
83 <label>Impact</label>
84 <h4><span ng-repeat="(key, value) in data.impact" class="normal-size" style="cursor: pointer;">
85 <span ng-class="{'label-impact label-default-impact': !value, 'label-impact label-success-impact': value}" ng-click="toggleImpact(key)">{{key}}</span>
86 </span></h4><!-- .normal-size -->
87 </div>
88 <div class="col-md-2"></div>
89 <div class="col-md-4">
90 <label>Ease of Resolution</label>
91 <select class="form-control" ng-model="data.easeofresolution" ng-options="e for e in easeofresolution" style="padding:0px 12px;">
92 <option value=""></option>
93 </select>
94 </div>
95 </div><!-- .form-group -->
96
4397 <div class="form-group">
4498 <div class="col-md-12">
4599 <label for="Explotation">Severity *</label>
99 <div class="reports col-md-12 col-sm-12 col-xs-12">
1010 <div class="button-control justify-flex-start col-md-12 col-sm-12 col-xs-12">
1111 <div class="control-wrapper control-new">
12 <button id="import" type="button" class="btn btn-success btn-new" title="New host" ng-click="disabledClick || new()">
12 <button id="import" type="button" class="btn btn-success btn-new" title="New vuln template" ng-click="disabledClick || new()">
1313 New
1414 </button>
1515 </div>
5656 <th class="ui-grid-cell-contents ui-grid-header-cell">
5757 <span ng-click="toggleSort('description')">Description</span>
5858 </th>
59 <th class="ui-grid-cell-contents ui-grid-header-cell">
60 <span ng-click="toggleSort('references')">References</span>
61 </th>
62 <th class="ui-grid-cell-contents ui-grid-header-cell">
63 <span ng-click="toggleSort('data')">Data</span>
64 </th>
5965 <th class="ui-grid-cell-contents ui-grid-header-cell">
6066 <span ng-click="toggleSort('resolution')">Resolution</span>
6167 </th>
6268 <th class="ui-grid-cell-contents ui-grid-header-cell">
69 <span ng-click="toggleSort('impact')">Impact</span>
70 </th>
71 <th class="ui-grid-cell-contents ui-grid-header-cell">
6372 <span ng-click="toggleSort('exploitation')">Severity</span>
73 </th>
74 <th class="ui-grid-cell-contents ui-grid-header-cell">
75 <span ng-click="toggleSort('easeofresolution')">Ease of Resolution</span>
76 </th>
77 <th class="ui-grid-cell-contents ui-grid-header-cell">
78 <span ng-click="toggleSort('policyviolations')">Policy Violations</span>
6479 </th>
6580 </tr>
6681 </thead>
7388 <td class="checkbox-select"><input type="checkbox" name="{{model._id}}"/></td>
7489 <td>{{model.name}}</td>
7590 <td>{{model.description}}</td>
91 <td><div ng-repeat="ref in model.refs">{{ref}}</div></td>
92 <td>{{model.data}}</td>
7693 <td>{{model.resolution}}</td>
94 <td><div ng-repeat="(key, value) in model.impact" ng-if="value == true">{{key}}</div></td>
7795 <td>{{model.exploitation}}</td>
96 <td>{{model.easeofresolution}}</td>
97 <td><div ng-repeat="policy in model.policyviolations">{{policy}}</div></td>
7898 </tr>
7999 </tbody>
80100 </table>
1111 this.cwe = "";
1212 this.description = "";
1313 this.desc = "";
14 this.data = "";
1415 this.exploitation = "";
1516 this.name = "";
1617 this.references = [];
1718 this.refs = [];
19 this.data = "";
1820 this.resolution = "";
21 this.impact = {
22 accountability: false,
23 availability: false,
24 confidentiality: false,
25 integrity: false
26 };
27 this.policyviolations = [];
28 this.easeofresolution = "";
1929 if (data) {
2030 if(data.name === undefined || data.name === "") {
2131 throw new Error("Unable to create a Vulnerability Model whithout a name");
2737 VulnModel.prototype = {
2838
2939 public_properties: ['exploitation', 'references', 'name', 'resolution', 'cwe', 'description',
30 'desc', 'id', 'refs'],
40 'desc', 'data', 'id', 'refs', 'impact', 'easeofresolution', 'policyviolations'],
3141
3242 set: function(data) {
3343 var self = this;
1515
1616 var public_properties = [
1717 'method', 'params', 'path', 'pname', 'query',
18 'request', 'response', 'website'
18 'request', 'response', 'website', 'status_code'
1919 ];
2020
2121 WebVuln.prototype = Object.create(Vuln.prototype);
372372 $scope.search = '';
373373 };
374374
375 $scope.readonlyToggle = function (ws) {
376 workspacesFact.readOnlyToogle(ws.name).then(function (resp) {
377 ws.readonly = resp.data;
378 });
379 };
380
375381 $scope.init();
376382 }]);
4343 <table class="table-v3 table table-responsive">
4444 <thead>
4545 <tr class="ui-grid-header">
46 <th class="ui-grid-cell-contents ui-grid-header-cell">Read only</th>
4647 <th class="ui-grid-cell-contents ui-grid-header-cell">Active</th>
4748 <th class="ui-grid-cell-contents ui-grid-header-cell">
4849 <a href="" ng-click="toggleSort('name')">Name</a>
6061 selection-model selection-model-selected-class="multi-selected">
6162
6263 <td class="ui-grid-cell-contents active-toggle">
63 <span ng-click="activeToggle(ws)" class="active-toggle-container" ng-class="{ disabled:ws.active === false }" uib-tooltip="{{(ws.active === true) ? 'Disable workspace' : 'Enable workspace'}}" tooltip-placement="right">
64 <img ng-src="{{ (ws.active) ? 'images/icon-list-confirmed.svg' : 'images/icon-list-notconfirmed.svg'}}" class="confirm-icon" ng-style="{ 'opacity': (ws.active === true) ? '1' : '0.7' }" />
65 </span>
64 <div class="toogle-img-container">
65 <span ng-click="readonlyToggle(ws)" class="active-toggle-container" ng-class="{ disabled:ws.readonly === false }" uib-tooltip="{{(ws.readonly === false) ? 'Workspace with all permissions' : 'Workspace read only'}}" tooltip-placement="right">
66 <img ng-src="{{ (ws.readonly == true) ? 'images/icon-list-confirmed.svg' : 'images/icon-list-notconfirmed.svg'}}" class="confirm-icon" ng-style="{ 'opacity': (ws.readonly === true) ? '1' : '0.7' }" />
67 </span>
68 </div>
69 </td>
70
71 <td class="ui-grid-cell-contents active-toggle">
72 <div class="toogle-img-container">
73 <span ng-click="activeToggle(ws)" class="active-toggle-container" ng-class="{ disabled:ws.active === false }" uib-tooltip="{{(ws.active === true) ? 'Disable workspace' : 'Enable workspace'}}" tooltip-placement="right">
74 <img ng-src="{{ (ws.active) ? 'images/icon-list-confirmed.svg' : 'images/icon-list-notconfirmed.svg'}}" class="confirm-icon" ng-style="{ 'opacity': (ws.active === true) ? '1' : '0.7' }" />
75 </span>
76 </div>
6677 </td>
6778 <td class="ui-grid-cell-contents">
6879 <span class="onhover upsize" ng-click="dashboardRedirect(ws.name)">
127127 return deferred.promise;
128128 };
129129
130 workspacesFact.readOnlyToogle = function(wsName) {
131 var deferred = $q.defer();
132 ServerAPI.readOnlyToogle(wsName).then(function(data){
133 deferred.resolve(data);
134 }, function(err){
135 deferred.reject(err);
136 });
137 return deferred.promise;
138 };
139
130140 return workspacesFact;
131141 }]);
22 in
33 mkShell {
44 buildInputs = with python27Packages;
5 [virtualenv pyopenssl psycopg2 pillow pygobject3
5 [virtualenv pyopenssl psycopg2 pillow pygobject3 pynacl matplotlib lxml ldap
66 gobjectIntrospection gtk3 gnome3.vte ipython
77 ];
88 shellHook = ''
88 import os
99 import sys
1010 import json
11 import random
12 import string
1311 import inspect
14
1512 import pytest
16 import psycopg2
17 from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
18
1913 from factory import Factory
2014 from flask.testing import FlaskClient
2115 from flask_principal import Identity, identity_changed
2620 from server.app import create_app
2721 from server.models import db
2822 from test_cases import factories
29 from server import config
30
23
24
25 TEMPORATY_SQLITE = NamedTemporaryFile()
3126 # Discover factories to automatically register them to pytest-factoryboy and to
3227 # override its session
3328 enabled_factories = []
7570 def pytest_addoption(parser):
7671 # currently for tests using sqlite and memory have problem while using transactions
7772 # we need to review sqlite configuraitons for persistence using PRAGMA.
78 parser.addoption('--use-postgresql', action='store_true',
79 help="Forces the tests to be executed in postgresql "
80 "using server.ini credentials")
81 parser.addoption('--connection-string',
73 parser.addoption('--connection-string', default='sqlite:////{0}'.format(TEMPORATY_SQLITE.name),
8274 help="Database connection string. Defaults to in-memory "
8375 "sqlite if not specified:")
8476 parser.addoption('--ignore-nplusone', action='store_true',
9385 config.option.markexpr = 'not hypothesis'
9486
9587
96 @pytest.fixture(scope='function')
88 @pytest.fixture(scope='session')
9789 def app(request):
98 connection_string = request.config.getoption(
99 '--connection-string')
100 use_postgresql = request.config.getoption(
101 '--use-postgresql')
102 sqlite = False
103 postgres_user, postgres_password = None, None
104
105 if use_postgresql and not connection_string:
106 connection_string = config.database.connection_string
107 if connection_string:
108 postgres_user, postgres_password = connection_string.split('://')[1].split('@')[0].split(':')
109
110 if postgres_user and postgres_password:
111 host = connection_string.split('://')[1].split('@')[1].split('/')[0]
112 con = psycopg2.connect(dbname='postgres',
113 user=postgres_user,
114 host=host,
115 password=postgres_password)
116
117 con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
118 cur = con.cursor()
119 db_name = ''.join(random.SystemRandom().choice(string.ascii_uppercase) for _ in range(20))
120 cur.execute("CREATE DATABASE \"%s\" ;" % db_name)
121 connection_string = 'postgresql+psycopg2://{postgres_user}:{postgres_password}@{host}/{db_name}'.format(
122 postgres_user=postgres_user,
123 postgres_password=postgres_password,
124 host=host,
125 db_name=db_name,
126 )
127 con.close()
128 else:
129 sqlite = True
130 connection_string = 'sqlite:///'
131
132 app = create_app(db_connection_string=connection_string, testing=True)
90 app = create_app(db_connection_string=request.config.getoption(
91 '--connection-string'), testing=True)
13392 app.test_client_class = CustomClient
13493
13594 # Establish an application context before running the tests.
13796 ctx.push()
13897
13998 def teardown():
140 with ctx:
141 db.session.close()
142 db.engine.dispose()
99 TEMPORATY_SQLITE.close()
143100 ctx.pop()
144 if not sqlite:
145 postgres_user, postgres_password = connection_string.split('://')[1].split('@')[0].split(':')
146 host = connection_string.split('://')[1].split('@')[1].split('/')[0]
147 con = psycopg2.connect(dbname='postgres',
148 user=postgres_user,
149 host=host,
150 password=postgres_password)
151
152 con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
153 cur = con.cursor()
154 cur.execute("DROP DATABASE \"%s\" ;" % db_name)
155101
156102 request.addfinalizer(teardown)
157103 app.config['NPLUSONE_RAISE'] = not request.config.getoption(
159105 return app
160106
161107
162 @pytest.fixture(scope='function')
108 @pytest.fixture(scope='session')
163109 def database(app, request):
164110 """Session-wide test database."""
111
112 def teardown():
113 try:
114 db.engine.execute('DROP TABLE vulnerability CASCADE')
115 except Exception:
116 pass
117 try:
118 db.engine.execute('DROP TABLE vulnerability_template CASCADE')
119 except Exception:
120 pass
121 db.drop_all()
165122
166123 # Disable check_vulnerability_host_service_source_code constraint because
167124 # it doesn't work in sqlite
168125 vuln_constraints = db.metadata.tables['vulnerability'].constraints
169 try:
170 vuln_constraints.remove(next(
171 constraint for constraint in vuln_constraints
172 if constraint.name == 'check_vulnerability_host_service_source_code'))
173 except StopIteration:
174 pass
175 db.init_app(app)
126 vuln_constraints.remove(next(
127 constraint for constraint in vuln_constraints
128 if constraint.name == 'check_vulnerability_host_service_source_code'))
129
130 db.app = app
176131 db.create_all()
177132
133 request.addfinalizer(teardown)
178134 return db
179135
180136
214170 for further information
215171 """
216172 connection = database.engine.connect()
173 transaction = connection.begin()
217174
218175 options = {"bind": connection, 'binds': {}}
219176 session = db.create_scoped_session(options=options)
220177
221178 # start the session in a SAVEPOINT...
222 #session.begin_nested()
179 session.begin_nested()
180
181 # then each time that SAVEPOINT ends, reopen it
182 @event.listens_for(session, "after_transaction_end")
183 def restart_savepoint(session, transaction):
184 if transaction.nested and not transaction._parent.nested:
185
186 # ensure that state is expired the way
187 # session.commit() at the top level normally does
188 # (optional step)
189 session.expire_all()
190
191 session.begin_nested()
223192
224193 database.session = session
225194 db.session = session
232201 # Session above (including calls to commit())
233202 # is rolled back.
234203 # be careful with this!!!!!
204 transaction.rollback()
235205 connection.close()
236206 session.remove()
237207
241211
242212 @pytest.fixture
243213 def test_client(app):
214
215 # flask.g is persisted in requests, and the werkzeug
216 # CSRF checker could fail if we don't do this
217 from flask import g
218 try:
219 del g.csrf_token
220 except:
221 pass
222
244223 return app.test_client()
245224
246225
296275 app.config['NPLUSONE_RAISE'] = False
297276 yield
298277 app.config['NPLUSONE_RAISE'] = old
278
279
280 @pytest.fixture
281 def csrf_token(logged_user, test_client):
282 session_response = test_client.get('/session')
283 return session_response.json.get('csrf_token')
0 # This is an ubuntu with system packages required to run faraday
1 # It doesn't install the python dependencies. That is done in
2 # the gitlab CI job to avoid having old versions of packages.
3 # This is used to build registry.gitlab.com/faradaysec/faraday/faraday_testing_base
4 FROM ubuntu
5 RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime
6 RUN echo $TZ > /etc/timezone
7 RUN apt-get update -qy
8 RUN apt-get -y install build-essential ipython python-setuptools python-pip python-dev pkg-config libssl-dev libffi-dev libxml2-dev libxslt1-dev libfreetype6-dev libpng-dev postgresql sudo libsasl2-dev libldap2-dev git
9 RUN apt-get install -y python-dev python-pip
0 [faraday_server]
1 port = 5985
2 bind_address = localhost
3 websocket_port = 9000
4 secret_key = 2nQe7hpNAUPfEb5xSal8K41s3
5
6 [ssl]
7 port = 6985
8 certificate =
9 keyfile =
10
11 [couchdb]
12 host = localhost
13 port = 5984
14 ssl_port = 6984
15 user =
16 password =
17 protocol = http
18
19 [storage]
20 path = /home/lcubo/.faraday/storage
21
1111 class TestAPIInfoEndpoint:
1212
1313 def test_api_info(self, test_client):
14 current_dir = os.path.dirname(os.path.realpath(__file__))
14 current_dir = os.getcwd()
1515 # this is a bug on the info api!
1616 # we require faraday to be a package since we can't import
1717 # from base path when our current working dir is test_cases.
0
1 from test_cases import factories
2
3 class TestLogin():
4 def test_case_bug_with_username(self, test_client, session):
5 """
6 When the user case does not match the one in database,
7 the form is valid but no record was found in the database.
8 """
9
10 susan = factories.UserFactory.create(
11 active=True,
12 username='Susan',
13 password='pepito',
14 role='pentester')
15 session.add(susan)
16 session.commit()
17 # we use lower case username, but in db is Capitalized
18 login_payload = {
19 'email': 'susan',
20 'password': 'pepito',
21 }
22 res = test_client.post('/login', data=login_payload)
23 assert res.status_code == 200
24 assert 'authentication_token' in res.json['response']['user']
1515 @pytest.mark.usefixtures('logged_user')
1616 class TestFileUpload():
1717
18 def test_file_upload(self, test_client, session):
18 def test_file_upload(self, test_client, session, csrf_token):
1919 ws = WorkspaceFactory.create(name="abc")
2020 session.add(ws)
2121 session.commit()
2626
2727 with open(path,'r') as report:
2828 file_contents = report.read()
29 session_response = test_client.get('/session')
30 csrf_token = session_response.json.get('csrf_token')
3129 data = {
3230 'file' : (BytesIO(file_contents), 'nmap_report.xml'),
3331 'csrf_token' : csrf_token
7674 assert res.status_code == 403
7775
7876
79 def test_request_with_workspace_deactivate(self, test_client, session):
77 def test_request_with_workspace_deactivate(self, test_client, session, csrf_token):
8078 ws = WorkspaceFactory.create(name="abc")
8179 ws.active = False
8280 session.add(ws)
8987 with open(path,'r') as report:
9088 file_contents = report.read()
9189
92 session_response = test_client.get('/session')
93 csrf_token = session_response.json.get('csrf_token')
9490 data = {
9591 'file' : (BytesIO(file_contents), 'nmap_report.xml'),
9692 'csrf_token' : csrf_token
15831583 res = test_client.post(self.url(), data=data)
15841584 assert res.status_code == 201
15851585
1586 def test_add_attachment_to_vuln(self, test_client, session, host_with_hostnames):
1586 def test_add_attachment_to_vuln(self, test_client, session, csrf_token,
1587 host_with_hostnames):
15871588 ws = WorkspaceFactory.create(name='abc')
15881589 session.add(ws)
15891590 vuln = VulnerabilityFactory.create(workspace=ws)
15941595 'file' : (BytesIO(file_contents), 'borrar.txt')
15951596 }
15961597 headers = {'Content-type': 'multipart/form-data'}
1598
15971599 res = test_client.post(
15981600 '/v2/ws/abc/vulns/{0}/attachment/'.format(vuln.id),
15991601 data=data, headers=headers, use_json_data=False)
1600 assert res.status_code == 200
1602 assert res.status_code == 403 # Missing CSRF protection
1603
1604 data = {
1605 'file' : (BytesIO(file_contents), 'borrar.txt'),
1606 'csrf_token': csrf_token
1607 }
1608 res = test_client.post(
1609 '/v2/ws/abc/vulns/{0}/attachment/'.format(vuln.id),
1610 data=data, headers=headers, use_json_data=False)
1611 assert res.status_code == 200 # Now it should work
1612
16011613 file_id = session.query(Vulnerability).filter_by(id=vuln.id).first().evidence[0].content['file_id']
16021614 depot = DepotManager.get()
16031615 assert file_contents == depot.get(file_id).read()
1616
1617 def test_add_attachment_to_vuln_fails_readonly(self, test_client, session, host_with_hostnames):
1618 ws = WorkspaceFactory.create(name='abc')
1619 session.add(ws)
1620 vuln = VulnerabilityFactory.create(workspace=ws)
1621 session.add(vuln)
1622 session.commit()
1623 file_contents = 'my file contents'
1624 data = {
1625 'file' : (BytesIO(file_contents), 'borrar.txt')
1626 }
1627 headers = {'Content-type': 'multipart/form-data'}
1628
1629 ws.readonly = True
1630 session.commit()
1631
1632 res = test_client.post(
1633 '/v2/ws/abc/vulns/{0}/attachment/'.format(vuln.id),
1634 data=data, headers=headers, use_json_data=False)
1635 assert res.status_code == 403
1636 query_test = session.query(Vulnerability).filter_by(id=vuln.id).first().evidence
1637 assert query_test == []
16041638
16051639 def test_delete_attachment_from_vuln(self, test_client, session, host_with_hostnames):
16061640 session.commit() # flush host_with_hostnames
16331667 query_test = session.query(Vulnerability).filter_by(id=vuln_id).first().evidence
16341668 assert query_test == []
16351669
1670 def test_delete_attachment_from_vuln_fails_readonly(self, test_client, session, host_with_hostnames):
1671 session.commit() # flush host_with_hostnames
1672 ws_name = host_with_hostnames.workspace.name
1673 attachment = NamedTemporaryFile()
1674 file_content = 'test file'
1675 attachment.write(file_content)
1676 attachment.seek(0)
1677 vuln = _create_post_data_vulnerability(
1678 name='Testing vuln',
1679 vuln_type='Vulnerability',
1680 parent_id=host_with_hostnames.id,
1681 parent_type='Host',
1682 refs=[],
1683 policyviolations=[],
1684 attachments=[attachment]
1685 )
1686 res = test_client.post('/v2/ws/{workspace}/vulns/'\
1687 .format(workspace=ws_name), data=vuln)
1688 assert res.status_code == 201
1689
1690 self.workspace.readonly = True
1691 session.commit()
1692
1693 filename = attachment.name.split('/')[-1]
1694 vuln_id = res.json['_id']
1695 res = test_client.delete(
1696 '/v2/ws/{workspace}/vulns/{vulnerability}/attachment/{file_name}/'
1697 .format(workspace=ws_name, vulnerability=vuln_id, file_name=filename)
1698 )
1699 assert res.status_code == 403
1700
1701 query_test = session.query(Vulnerability).filter_by(id=vuln_id).first().evidence
1702 assert len(query_test) == 1
1703 assert query_test[0].filename == filename
1704
16361705 @pytest.mark.skip("NplusOneError")
16371706 def test_vuln_filter(self, test_client, session, workspace):
16381707 new_host = HostFactory.create(workspace=workspace)
17171786 res = test_client.post(self.url(), data=raw_data)
17181787 assert res.json['messages']['_schema'][0] == 'Unknown parent type'
17191788
1720 def test_add_empty_attachment(self, test_client, session, workspace):
1789 def test_add_empty_attachment(self, test_client, session, workspace, csrf_token):
17211790 vuln = VulnerabilityFactory.create(workspace=workspace)
17221791 session.add(vuln)
17231792 session.commit()
17241793
17251794 res = test_client.post(
17261795 '/v2/ws/{ws_name}/vulns/{id}/attachment/'
1727 .format(ws_name=workspace.name,id=vuln.id))
1796 .format(ws_name=workspace.name,id=vuln.id),
1797 data={'csrf_token': csrf_token},
1798 headers={'Content-Type': 'multipart/form-data'},
1799 use_json_data=False)
17281800 assert res.status_code == 400
17291801
17301802 def test_get_attachment_with_invalid_workspace_and_vuln(self, test_client):
7878 assert res.status_code == 200
7979 assert len(res.json['data']) == OBJECT_COUNT
8080
81 def test_can_list_readonly(self, test_client, session):
82 self.workspace.readonly = True
83 session.commit()
84 res = test_client.get(self.url())
85 assert res.status_code == 200
8186
8287 class RetrieveTestsMixin:
8388
112117 object_id = res.json['id']
113118 obj = self.model.query.get(object_id)
114119 assert obj.workspace == self.workspace
120
121 def test_create_fails_readonly(self, test_client):
122 self.workspace.readonly = True
123 db.session.commit()
124 data = self.factory.build_dict(workspace=self.workspace)
125 res = test_client.post(self.url(),
126 data=data)
127 db.session.commit()
128 assert res.status_code == 403
129 assert self.model.query.count() == OBJECT_COUNT
130
115131
116132 def test_create_inactive_fails(self, test_client):
117133 self.workspace.deactivate()
163179 assert res.json[updated_field] == getattr(self.first_object,
164180 updated_field)
165181
182 def test_update_an_object_readonly_fails(self, test_client):
183 self.workspace.readonly = True
184 db.session.commit()
185 for unique_field in self.unique_fields:
186 data = self.factory.build_dict()
187 old_field = getattr(self.objects[0], unique_field)
188 old_id = getattr(self.objects[0], 'id')
189 res = test_client.put(self.url(self.first_object), data=data)
190 db.session.commit()
191 assert res.status_code == 403
192 assert self.model.query.count() == OBJECT_COUNT
193 assert old_field == getattr(self.model.query.filter(self.model.id == old_id).one(), unique_field)
194
166195 def test_update_inactive_fails(self, test_client):
167196 self.workspace.deactivate()
168197 db.session.commit()
207236 assert was_deleted(self.first_object)
208237 assert self.model.query.count() == OBJECT_COUNT - 1
209238
239 def test_delete_readonly_fails(self, test_client, session):
240 self.workspace.readonly = True
241 session.commit()
242 res = test_client.delete(self.url(self.first_object))
243 assert res.status_code == 403 # No content
244 assert not was_deleted(self.first_object)
245 assert self.model.query.count() == OBJECT_COUNT
246
210247 def test_delete_inactive_fails(self, test_client):
211248 self.workspace.deactivate()
212249 db.session.commit()
33 import subprocess
44 from datetime import datetime
55 from server.utils import daemonize
6 import server.config
7
8 try:
9 import ConfigParser
10 except ImportError:
11 import configparser as ConfigParser
612
713
814 def test_start_and_kill_faraday_server():
2127 if server_port > 6500:
2228 raise Exception('No free ports could be found')
2329
30 if 'POSTGRES_DB' in os.environ:
31 # I'm on gitlab ci runner
32 # I will overwrite server.ini
33 connection_string = 'postgresql+psycopg2://{username}:{password}@postgres/{database}'.format(
34 username=os.environ['POSTGRES_USER'],
35 password=os.environ['POSTGRES_PASSWORD'],
36 database=os.environ['POSTGRES_DB'],
37 )
38 faraday_config = ConfigParser.SafeConfigParser()
39 config_path = os.path.expanduser('~/.faraday/config/server.ini')
40 faraday_config.read(config_path)
41 try:
42 faraday_config.add_section('database')
43 except ConfigParser.DuplicateSectionError:
44 pass
45 faraday_config.set('database', 'connection_string', connection_string)
46 with open(config_path, 'w') as faraday_config_file:
47 faraday_config.write(faraday_config_file)
48 manage_script = os.path.join(current_path, '..', 'manage.py')
49 command = ['/usr/bin/env', 'python2.7', manage_script, 'create-tables']
50 subproc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd= os.path.join(current_path, '..'))
51 subproc.wait()
52 std, err = subproc.communicate()
53 assert subproc.returncode == 0, ('Create tables failed!', std, err)
54
2455 server_script = os.path.join(current_path, '..', 'faraday-server.py')
25 command = ['/usr/bin/env', 'python2.7', server_script, '--port', '{0}'.format(server_port)]
56 command = ['/usr/bin/env', 'python2.7', server_script, '--port', '{0}'.format(server_port), '--debug']
2657 subproc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
2758 start = datetime.now()
2859 while subproc.returncode is None:
2960 now = datetime.now()
3061 delta = now - start
31 if delta.seconds > 40:
62 if delta.seconds > 140:
3263 raise UserWarning('Faraday server test timeout!')
33 if delta.seconds > 4:
64 if delta.seconds > 30:
3465 subproc.send_signal(signal.SIGTERM)
3566 subproc.wait()
3667 subproc.poll()
3869 subproc.poll()
3970 time.sleep(0.1)
4071 out, err = subproc.communicate()
41 assert subproc.returncode == 0, err
72 if subproc.returncode != 0:
73 log_path = os.path.expanduser('~/.faraday/logs/faraday-server.log')
74 with open(log_path, 'r') as log_file:
75 print(log_file.read())
76 assert subproc.returncode == 0, (out, err, command, server_script)
7575
7676
7777 def test_exists_and_content():
78 f = open(os.path.join(os.getcwd(),"..","VERSION"),"r")
78 f = open("VERSION", "r")
7979 line1 = f.readline().rstrip()
8080 assert f.read() == ''
8181 assert isPEP440(line1)