Codebase list python-faraday / 792a42d
New upstream version 3.10.0 Sophie Brun 4 years ago
457 changed file(s) with 11417 addition(s) and 6737 deletion(s). Raw diff Collapse all Expand all
7777
7878 # Editable install hoook
7979 _install
80 faraday_plugins/
66 PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
77 APT_CACHE_DIR: "$CI_PROJECT_DIR/apt-cache"
88 DEBIAN_FRONTEND: noninteractive
9 IMAGE_TAG: '/bin/egrep -o "[0-9]\.[0-9]\.[0-9]" faraday/__init__.py'
9 IMAGE_TAG: 'egrep -o "[0-9]\.([0-9]|[0-9][0-9])\.[0-9]" faraday/__init__.py'
10
1011 ## ENV_VARS LIST
1112 # FULL_TEST = Test all jobs
1213 # BUILD_TEST = Test default and build jobs
2627 - post_testing
2728 - build_faraday
2829 - build
30 - distro_testing
2931
3032 services:
3133 - postgres:latest
5456 - git config --global user.email "[email protected]"
5557 - git config --global user.name "Mergerbot"
5658 - python3 merge-conflict-detector.py
57 except:
58 variables:
59 - $BUILD_TEST
6059
6160 pylint:
6261 tags:
6362 - faradaytests
64 image: registry.gitlab.com/faradaysec/faraday/faraday_testing_base # I just need an image with python-dev and python-pip
63 image: registry.gitlab.com/faradaysec/integrationrepo
6564 stage: pre_testing
6665 script:
67 - apt-get update -qy
68 - apt-get install -y -o dir::cache::archives="$APT_CACHE_DIR" build-essential ipython python-setuptools python-pip python-dev libssl-dev libffi-dev pkg-config libxml2-dev libxslt1-dev libfreetype6-dev libpng-dev
69 - apt-get install -y -o dir::cache::archives="$APT_CACHE_DIR" libpq-dev python-pip python-dev gir1.2-gtk-3.0 gir1.2-vte-2.91 python-gobject zsh curl
70 - apt-get install -y -o dir::cache::archives="$APT_CACHE_DIR" gobject-introspection libgirepository1.0-dev libcairo2-dev python-cairo libgdk-pixbuf2.0-dev
71 - apt-get install -y -o dir::cache::archives="$APT_CACHE_DIR" libcanberra-gtk-module libcanberra-gtk* libxml2-dev libxslt1-dev gir1.2-gtk-3.0 gir1.2-vte-2.91
72 - pip install virtualenv
73 - virtualenv -p python2 faraday_venv
66 - pip3 install virtualenv
67 - virtualenv -p python3 faraday_venv
7468 - source faraday_venv/bin/activate
7569 - pip install --upgrade -r requirements.txt
76 - python setup.py install
70 - python3 setup.py install
7771 # pylint slow issue https://github.com/PyCQA/pylint/issues/2765
7872 - pip install pylint isort==4.3.4
7973 - pylint --rcfile=.pylintrc faraday --ignore=faraday_venv | tee pylint.txt
8276 artifacts:
8377 paths:
8478 - pylint.svg
79 - pylint3.svg
8580 except:
8681 variables:
8782 - $BUILD_TEST
8883
89 py3checker:
90 tags:
91 - faradaytests
92 image: python:3
93 stage: testing
94 script:
95 - pip install pylint
96 - python3 py3-checker.py -o py3_checker_result.txt
97 artifacts:
98 paths:
99 - py3_checker_result.txt
100 expire_in: 1 week
101 except:
102 variables:
103 - $BUILD_TEST
104
10584 postgresql_test:
106 image: registry.gitlab.com/faradaysec/faraday/faraday_testing_base
85 image: registry.gitlab.com/faradaysec/integrationrepo
10786 tags:
10887 - faradaytests
10988 stage: testing
11089 coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
11190 script:
112 - apt-get update -qy
113 - apt-get install -o dir::cache::archives="$APT_CACHE_DIR" -y gobject-introspection libgirepository1.0-dev libcairo2-dev python-cairo libpq-dev
114 - pip install virtualenv
115 - virtualenv -p python2 faraday_venv
91 - export LC_ALL=C.UTF-8
92 - export LANG=C.UTF-8
93 - pip3 install virtualenv
94 - virtualenv -p python3 faraday_venv
11695 - source faraday_venv/bin/activate
11796 - pip install pip -U
118 #- pip install --upgrade -r requirements_server.txt
11997 - pip install --upgrade -r requirements.txt
120 - python setup.py install
98 - python3 setup.py install
12199 - pip install --upgrade responses pytest-xdist pytest-cov
122100 - pip install --upgrade -r requirements_dev.txt
123101 - mkdir -p ~/.faraday/config
124102 - cp tests/data/server.ini ~/.faraday/config
125 #- 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
126103 - mkdir run_from
127104 - cd run_from && pytest ../tests -v --capture=sys --cov=../faraday/server --color=yes --disable-warnings --connection-string=postgresql+psycopg2://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres/$POSTGRES_DB
128105 artifacts:
134111 - $BUILD_TEST
135112
136113 sqlite_test:
137 image: registry.gitlab.com/faradaysec/faraday/faraday_testing_base
114 image: registry.gitlab.com/faradaysec/integrationrepo
138115 tags:
139116 - faradaytests
140117 stage: testing
141118 coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
142119 script:
143 - apt-get update -qy
144 - apt-get install -y -o dir::cache::archives="$APT_CACHE_DIR" gobject-introspection libgirepository1.0-dev libcairo2-dev python-cairo libpq-dev
145 - pip install virtualenv
146 - virtualenv -p python2 faraday_venv
120 - export LC_ALL=C.UTF-8
121 - export LANG=C.UTF-8
122 - pip3 install virtualenv
123 - virtualenv -p python3 faraday_venv
147124 - source faraday_venv/bin/activate
148125 - pip install pip -U
149 #- pip install --upgrade -r requirements_server.txt
150126 - pip install --upgrade -r requirements.txt
151 - python setup.py install
127 - python3 setup.py install
152128 - pip install --upgrade responses pytest-xdist pytest-cov
153129 - pip install --upgrade -r requirements_dev.txt
154130 - mkdir -p ~/.faraday/config
163139 variables:
164140 - $BUILD_TEST
165141
166 generate_release_file:
167 image: python:3
168 stage: post_testing
169 allow_failure: true
170 script:
171 - apt-get update -qy
172 - apt-get install -y -o dir::cache::archives="$APT_CACHE_DIR" python-dev python-pip
173 - pip install packaging
174 - cd CHANGELOG && python3 changelog.py
175 artifacts:
176 paths:
177 - CHANGELOG/RELEASE.md
178 only:
179 variables:
180 - $FULL_DOC == "True"
181 - $RELEASE_FILE == "True"
182 - $CI_COMMIT_REF_NAME =~ /^.*\/(master)$/
183 - $BUILD_TEST
184 - $FULL_TEST
185 - $DAILY_TEST
186
142
143 build_nix_python3:
144 image: nixorg/nix
145 stage: build_faraday
146 script:
147 - nix-channel --add https://nixos.org/channels/nixos-19.09 nixpkgs
148 - nix-channel --update
149 - nix-build buildpth.nix
150 - nix-build '<nixpkgs>' -A gnome3.vte -o result-vte
151 - nix-build '<nixpkgs>' -A gtk3 -o result-gtk
152 - tar cf /py3.tar $(nix-store --query --requisites $(readlink result) $(readlink result-vte)) $(readlink result-gtk)
153 - mkdir /opt
154 - nix-env -i findutils postgresql
155 - "nix-shell -p python37Packages.virtualenv --command 'virtualenv /opt/faraday'"
156 - source /opt/faraday/bin/activate
157 - "nix-shell -p libxml2 libxslt zlib cairo gobject-introspection glib pkgconfig --run 'SOURCE_DATE_EPOCH=$(date +%s) /opt/faraday/bin/pip install -r requirements.txt'"
158 - python setup.py install
159 - cp result /opt/faraday/lib/python3.7/site-packages/nix.pth
160 - mv /opt/faraday/bin/faraday-client /opt/faraday/bin/_faraday_client_novte
161 - 'nix-shell -p makeWrapper --command "makeWrapper /opt/faraday/bin/_faraday_client_novte /opt/faraday/bin/faraday-client --prefix GI_TYPELIB_PATH : $(find /nix/store -name "girepository-1.0" | tr "\n" ":")"'
162 - 'nix-shell -p makeWrapper --command "makeWrapper /opt/faraday/bin/python /opt/faraday/bin/test --prefix GI_TYPELIB_PATH : $(find /nix/store -name "girepository-1.0" | tr "\n" ":")"'
163 - /opt/faraday/bin/test -c "import gi;gi.require_version('Gtk', '3.0');gi.require_version('Vte', '2.91');from gi.repository import Gio, Gtk, GdkPixbuf, Vte, GLib, GObject, Gdk" # Test if GTK will work
164 - tar rvf /py3.tar /opt/faraday
165 - mv /py3.tar $CI_PROJECT_DIR
166 artifacts:
167 name: python3
168 paths:
169 - py3.tar
170 expire_in: 15 days # in the future we don't need to expire this.
171 only:
172 variables:
173 - $CI_COMMIT_REF_NAME =~ /^.*\/(master)$/
174 - $CI_COMMIT_TAG
175 - $BUILD_TEST
176 - $FULL_TEST
177 - $DAILY_TEST
187178
188179 generate_build_file:
189 image: registry.gitlab.com/faradaysec/faraday/faraday_testing_pink
180 image: registry.gitlab.com/faradaysec/integrationrepo
190181 stage: build_faraday
191182 script:
192 - apt-get update -qy
193 - apt-get install -y rsync
194183 - "/bin/mkdir faraday_copy"
195184 - "/usr/bin/rsync -aq --exclude 'faraday_copy' --exclude '.cache' . faraday_copy"
196185 - "/bin/tar -zcf faraday.tar.gz faraday_copy"
209198
210199
211200 generate_deb:
201 image: registry.gitlab.com/faradaysec/integrationrepo
212202 stage: build
213203 before_script:
214 - 'which ssh-agent || ( apt-get -y update && apt-get -y install openssh-client )'
215 - eval "$(ssh-agent -s)"
216 - mkdir -p ~/.ssh
217 - echo "$SSH_PRIVATE_KEY_OTHER_REPOS_GITLAB" >> ~/.ssh/id_rsa && chmod 0600 ~/.ssh/id_rsa
218 - chmod 700 ~/.ssh
219 - ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
220 script:
221 - apt-get update -qy
222 - apt-get -y install virtualenv git libcairo2-dev libgirepository1.0-dev gobject-introspection ruby ruby-dev rubygems build-essential build-essential libssl-dev libffi-dev libxml2-dev libxslt1-dev libfreetype6-dev libpng-dev libsasl2-dev libldap2-dev libkrb5-dev pandoc python2.7 python-dev pkg-config libpq-dev gir1.2-gtk-3.0 gir1.2-vte-2.91 zsh curl pandoc
223 - curl https://bootstrap.pypa.io/get-pip.py | python
224 - pip install virtualenv-tools
204 - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/faradaysec/faraday-linux-installers-builder.git
205 - mv py3.tar /
206 - cd /; tar xf py3.tar; cd -
207
208 script:
225209 - mkdir build_installer
226210 - cp -a faraday.tar.gz build_installer/.
227211 - cd build_installer
228212 - /bin/tar zxf faraday.tar.gz
229 - ls
230213 - cd faraday_copy
231 - git clone [email protected]:faradaysec/faraday-linux-installers-builder.git
214 - cp -r /nix .
215 - mv ../../faraday-linux-installers-builder .
232216 - cd faraday-linux-installers-builder
233217 - git clone https://github.com/jordansissel/fpm.git
234218 - cd fpm
235 - git apply ../fpm-patchs/fpm.virtualenv_compile.patch
219 - git checkout d7b466787d17581bc723e474ecf6e18f48226031
220 - git apply ../fpm-patchs/fpm.virtualenv.patch
236221 - make gem
237222 - gem install --no-ri --no-rdoc fpm-1.11.0.gem
238223 - cd ../../
241226 - sh faraday-linux-installers-builder/build.sh $(eval $IMAGE_TAG) client deb
242227 - mv faraday-client_amd64.deb ../../faraday-client_amd64.deb
243228 dependencies:
244 - generate_build_file
229 - generate_build_file
230 - build_nix_python3
245231 artifacts:
246232 name: 'faraday_$CI_COMMIT_REF_NAME.deb'
247233 paths:
248234 - "faraday-server_amd64.deb"
249235 - "faraday-client_amd64.deb"
250236 expire_in: 15 days
237 only:
238 variables:
239 - $CI_COMMIT_REF_NAME =~ /^.*\/(master)$/
240 - $CI_COMMIT_TAG
241 - $BUILD_TEST
242 - $FULL_TEST
243 - $DAILY_TEST
244
245
246 smoke_test_deb:
247 allow_failure: true
248 stage: distro_testing # TODO improve
249 image: ubuntu:18.04
250 dependencies:
251 - generate_deb
252 script:
253 - apt-get update -y
254 - apt install -y sudo curl
255 - apt-get install -y ./faraday-server_amd64.deb
256 - which faraday-manage
257 - faraday-manage show-urls
258 - export FARADAY_HOME=/home/faraday
259 - /opt/faraday/bin/faraday-server || true # create .faraday
260 - "echo '[database]' >>~faraday/.faraday/config/server.ini"
261 - echo "connection_string = postgresql+psycopg2://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres/$POSTGRES_DB" >>~faraday/.faraday/config/server.ini
262 - cat ~faraday/.faraday/config/server.ini
263 - faraday-manage create-tables
264 - /opt/faraday/bin/faraday-server &
265 - sleep 5
266 - curl -v http://localhost:5985/_api/v2/info
267 - faraday-manage status-check
268 - kill $(cat ~faraday/.faraday/faraday-server-port-5985.pid)
269 - jobs
251270 only:
252271 variables:
253272 - $CI_COMMIT_REF_NAME =~ /^.*\/(master)$/
261280 image: centos:7
262281 before_script:
263282 - yum -y upgrade
264 - yum -y install which
283 - yum -y install which git
265284 - yum -y install epel-release
266 - 'which ssh-agent || (yum -y update && yum -y install openssh-clients)'
267 - eval "$(/usr/bin/ssh-agent -s)"
268 - mkdir -p ~/.ssh
269 - echo "$SSH_PRIVATE_KEY_OTHER_REPOS_GITLAB" >> ~/.ssh/id_rsa && chmod 0600 ~/.ssh/id_rsa
270 - chmod 700 ~/.ssh
271 - /usr/bin/ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts
272 script:
273 - yum -y install git ruby ruby-devel rubygems gobject-introspection-devel curl zsh mailcap libffi-devel python-devel openssl-devel openldap-devel libxslt-devel libxml2-devel libxslt-devel freetype-devel libjpeg-devel gtk+-devel gtk3-devel gtk2-devel pandoc
285 - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/faradaysec/faraday-linux-installers-builder.git
286 - mv py3.tar /
287 - cd /; tar xf py3.tar; cd -
288 - yum -y install ruby ruby-devel rubygems gobject-introspection-devel curl zsh mailcap libffi-devel openssl-devel openldap-devel libxslt-devel libxml2-devel libxslt-devel freetype-devel libjpeg-devel gtk+-devel gtk3-devel gtk2-devel postgresql-devel
274289 - yum groups -y install "Development Tools"
275 - curl https://bootstrap.pypa.io/get-pip.py | python
290 - yum -y install centos-release-scl
291 - yum -y install rh-python36
292 - source /opt/rh/rh-python36/enable
276293 - pip install virtualenv
277 - pip install virtualenv-tools
294 - pip install virtualenv-tools3
295 script:
278296 - mkdir build_installer
279297 - cp -a faraday.tar.gz build_installer/.
280298 - cd build_installer
281299 - /bin/tar zxf faraday.tar.gz
282 - ls
283300 - cd faraday_copy
284 - git clone [email protected]:faradaysec/faraday-linux-installers-builder.git
301 - cp -r /nix .
302 - mv ../../faraday-linux-installers-builder .
285303 - cd faraday-linux-installers-builder
286304 - git clone https://github.com/jordansissel/fpm.git
287305 - cd fpm
288 - git apply ../fpm-patchs/fpm.virtualenv_compile.patch
306 - git checkout d7b466787d17581bc723e474ecf6e18f48226031
307 - git apply ../fpm-patchs/fpm.virtualenv.patch
289308 - make gem
290309 - gem install --no-ri --no-rdoc fpm-1.11.0.gem
291310 - cd ../../
295314 - mv faraday-client_amd64.rpm ../../faraday-client_amd64.rpm
296315 dependencies:
297316 - generate_build_file
317 - build_nix_python3
298318 artifacts:
299319 name: 'faraday_$CI_COMMIT_REF_NAME.rpm'
300320 paths:
309329 - $FULL_TEST
310330 - $DAILY_TEST
311331
312 binary_files:
313 image: python:2.7.16
332 macos_pkg:
333 tags:
334 - macos
314335 stage: build
315 script:
316 - apt-get update -qy
317 - apt-get install -y -o dir::cache::archives="$APT_CACHE_DIR" build-essential ipython python-setuptools python-pip python-dev libssl-dev libffi-dev pkg-config libxml2-dev libxslt1-dev libfreetype6-dev libpng-dev
318 - apt-get install -y -o dir::cache::archives="$APT_CACHE_DIR" libpq-dev python-pip python-dev gir1.2-gtk-3.0 gir1.2-vte-2.91 python-gobject zsh curl
319 - apt-get install -y -o dir::cache::archives="$APT_CACHE_DIR" gobject-introspection libgirepository1.0-dev libcairo2-dev python-cairo libgdk-pixbuf2.0-dev
320 - apt-get install -y -o dir::cache::archives="$APT_CACHE_DIR" libcanberra-gtk-module libcanberra-gtk* libxml2-dev libxslt1-dev gir1.2-gtk-3.0 gir1.2-vte-2.91
321 - pip install virtualenv
322 - virtualenv -p python2 faraday_venv
323 - source faraday_venv/bin/activate
324 - pip install pyinstaller
325 - pip install --upgrade -r requirements.txt
326 - python setup.py install
327 - pip install .[gtk]
328 - pyinstaller -F -w --onefile --icon=faraday/faraday/server/www/favicon.ico start_server.spec
329 - pyinstaller -F -w --onefile --icon=faraday/faraday/server/www/favicon.ico manage.spec
330 - pyinstaller -F -w --onefile --icon=faraday/faraday/server/www/favicon.ico start_client.spec
331 - pyinstaller -F -w --onefile --icon=faraday/faraday/server/www/favicon.ico fplugin.spec
332 - pyinstaller -F -w --onefile --icon=faraday/faraday/server/www/favicon.ico searcher.spec
333 - mkdir output
334 - mv dist/start_client output/faraday-client
335 - mv dist/start_server output/faraday-server
336 - mv dist/manage output/faraday-manage
337 - mv dist/fplugin output/faraday-fplugin
338 - mv dist/searcher output/faraday-searcher
339 - tar -zcvf linux-binaries.tar.gz output
336 before_script:
337 - echo 'export PATH="/usr/local/opt/openssl/bin:$PATH"' >> ~/.bash_profile
338 - export LDFLAGS="-L/usr/local/opt/openssl/lib"
339 - export CPPFLAGS="-I/usr/local/opt/openssl/include"
340 - export PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig"
341 script:
342 - pip3 install virtualenv
343 - pip3 install virtualenv-tools3
344 - export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:/usr/local/opt/libffi/lib/pkgconfig"
345 - mkdir build_installer
346 - cp -a faraday.tar.gz build_installer/.
347 - cd build_installer
348 - /usr/bin/tar zxf faraday.tar.gz
349 - cd faraday_copy
350 - git clone [email protected]:faradaysec/faraday-linux-installers-builder.git
351 - cd faraday-linux-installers-builder
352 - git clone https://github.com/jordansissel/fpm.git
353 - cd fpm
354 - git checkout d7b466787d17581bc723e474ecf6e18f48226031
355 - git apply ../fpm-patchs/fpm.virtualenv.patch
356 - make gem
357 - export GEM_HOME="$HOME/.gem"
358 - export GEM_PATH=$HOME/.gem
359 - export PATH=$PATH:$HOME/.gem/bin
360 - gem install fpm-1.11.0.gem
361 - cd ../../
362 - sh faraday-linux-installers-builder/build.sh $(eval $IMAGE_TAG) server osxpkg
363 - mv faraday-server_amd64.pkg ../../Faraday.pkg
340364 artifacts:
341365 name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-binaries"
342366 paths:
343 - linux-binaries.tar.gz
367 - Faraday.pkg
344368 expire_in: 1 week
345369 only:
346370 variables:
350374 - $FULL_TEST
351375 - $DAILY_TEST
352376
353 macos_dmg:
354 tags:
355 - macos
356 stage: build
357 allow_failure: true
358 script:
359 - pip install virtualenv
360 - virtualenv -p python2 faraday_venv
361 - source faraday_venv/bin/activate
362 - export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:/usr/local/opt/libffi/lib/pkgconfig"
363 - pip install pyinstaller
364 - pip install --upgrade -r requirements.txt
365 - python setup.py install
366 - pip install .[gtk]
367 - pyinstaller -F -w --onefile --onefile --icon=faraday/faraday/server/www/favicon.ico start_server.spec
368 - pyinstaller -F -w --onefile --onefile --icon=faraday/faraday/server/www/favicon.ico manage.spec
369 - pyinstaller -F -w --onefile --onefile --icon=faraday/faraday/server/www/favicon.ico start_client.spec
370 - pyinstaller -F -w --onefile --icon=faraday/faraday/server/www/favicon.ico fplugin.spec
371 - pyinstaller -F -w --onefile --icon=faraday/faraday/server/www/favicon.ico seacher.spec
372 - mkdir output
373 - mv dist/start_client output/faraday-client
374 - mv dist/start_server output/faraday-server
375 - mv dist/manage output/faraday-manage
376 - mv dist/fplugin output/faraday-fplugin
377 - mv dist/searcher output/faraday-searcher
378 - hdiutil create /tmp/tmp.dmg -ov -volname "Faraday" -fs HFS+ -srcfolder "./output"
379 - hdiutil convert /tmp/tmp.dmg -format UDZO -o Faraday.dmg
380 artifacts:
381 name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME-binaries"
382 paths:
383 - Faraday.dmg
384 expire_in: 1 week
385 only:
386 variables:
387 - $CI_COMMIT_REF_NAME =~ /^.*\/(master)$/
388 - $CI_COMMIT_TAG
389 - $BUILD_TEST
390 - $FULL_TEST
391 - $DAILY_TEST
392
393 documentation:
394 image: python:3
395 stage: post_testing
396 script:
397 - apt-get update -qy
398 - apt-get install -y -o dir::cache::archives="$APT_CACHE_DIR" python-pip python-dev
399 - pip install sphinx
400 - cd doc && make html
401 artifacts:
402 paths:
403 - doc/_build/html
404 only:
405 variables:
406 - $FULL_DOC
407 - $DOC
408 - $CI_COMMIT_REF_NAME =~ /^.*\/(dev|master)$/
409
410
411377 test_hypothesis:
412378 tags:
413379 - hypothesis
414 image: registry.gitlab.com/faradaysec/faraday/faraday_testing_base
380 image: registry.gitlab.com/faradaysec/integrationrepo
415381 stage: testing
416382 allow_failure: true
417383 script:
418 - apt-get update -qy
419 - pip install virtualenv
420 - virtualenv -p python2 faraday_venv
384 - pip3 install virtualenv
385 - virtualenv -p python3 faraday_venv
421386 - source faraday_venv/bin/activate
422 #- pip install --upgrade -r requirements_server.txt
423 - python setup.py install
387 - python3 setup.py install
424388 - pip install --upgrade responses pytest-xdist pytest-cov
425389 - pip install --upgrade -r requirements_dev.txt
426390 - mkdir -p ~/.faraday/config
427391 - cp tests/data/server.ini ~/.faraday/config
428 #- 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
429392 - pytest tests -v --cov=faraday/server/api --connection-string=postgresql+psycopg2://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres/$POSTGRES_DB -m hypothesis
430393 only:
431394 variables:
432395 - $FULL_TEST
433396 - $HYPO_TEST
434397 - $DAILY_TEST
398
399 agent_integration:
400 stage: post_testing
401 script:
402 - apt-get update -qy
403 - apt-get install curl -y
404 - "curl -X POST -F token=$INTEGRATION_TOKEN -F ref=master -F \"variables[FARADAY_REF]=$CI_COMMIT_REF_NAME\" https://gitlab.com/api/v4/projects/15311100/trigger/pipeline"
405 only:
406 variables:
407 - $CI_COMMIT_REF_NAME =~ /^.*\/(dev|master)$/
408 - $INTEGRATION
5353 # --enable=similarities". If you want to run only the classes checker, but have
5454 # no Warning level messages displayed, use"--disable=all --enable=classes
5555 # --disable=W"
56 disable=print-statement,
57 parameter-unpacking,
58 unpacking-in-except,
59 old-raise-syntax,
60 backtick,
61 long-suffix,
62 old-ne-operator,
63 old-octal-literal,
64 import-star-module-level,
65 non-ascii-bytes-literal,
66 invalid-unicode-literal,
56 disable=blacklisted-name,
57 input-builtin,
58 no-absolute-import,
59 invalid-name,
60 missing-docstring,
61 empty-docstring,
62 unneeded-not,
63 singleton-comparison,
64 misplaced-comparison-constant,
65 unidiomatic-typecheck,
66 consider-using-enumerate,
67 consider-iterating-dictionary,
68 bad-classmethod-argument,
69 bad-mcs-method-argument,
70 bad-mcs-classmethod-argument,
71 single-string-used-for-slots,
72 line-too-long,
73 too-many-lines,
74 trailing-whitespace,
75 missing-final-newline,
76 trailing-newlines,
77 multiple-statements,
78 superfluous-parens,
79 bad-whitespace,
80 mixed-line-endings,
81 unexpected-line-ending-format,
82 bad-continuation,
83 wrong-spelling-in-comment,
84 wrong-spelling-in-docstring,
85 invalid-characters-in-docstring,
86 multiple-imports,
87 wrong-import-order,
88 ungrouped-imports,
89 wrong-import-position,
90 old-style-class,
91 len-as-condition,
92 syntax-error,
93 unrecognized-inline-option,
94 bad-option-value,
95 init-is-generator,
96 return-in-init,
97 function-redefined,
98 not-in-loop,
99 return-outside-function,
100 yield-outside-function,
101 return-arg-in-generator,
102 nonexistent-operator,
103 duplicate-argument-name,
104 abstract-class-instantiated,
105 bad-reversed-sequence,
106 too-many-star-expressions,
107 invalid-star-assignment-target,
108 star-needs-assignment-target,
109 nonlocal-and-global,
110 continue-in-finally,
111 nonlocal-without-binding,
112 used-prior-global-declaration,
113 method-hidden,
114 access-member-before-definition,
115 no-method-argument,
116 no-self-argument,
117 invalid-slots-object,
118 assigning-non-slot,
119 invalid-slots,
120 inherit-non-class,
121 inconsistent-mro,
122 duplicate-bases,
123 non-iterator-returned,
124 unexpected-special-method-signature,
125 invalid-length-returned,
126 relative-beyond-top-level,
127 used-before-assignment,
128 undefined-variable,
129 undefined-all-variable,
130 invalid-all-object,
131 no-name-in-module,
132 unbalanced-tuple-unpacking,
133 unpacking-non-sequence,
134 bad-except-order,
135 raising-bad-type,
136 bad-exception-context,
137 misplaced-bare-raise,
138 raising-non-exception,
139 notimplemented-raised,
140 catching-non-exception,
141 slots-on-old-class,
142 super-on-old-class,
143 bad-super-call,
144 missing-super-argument,
145 no-member,
146 not-callable,
147 assignment-from-no-return,
148 no-value-for-parameter,
149 too-many-function-args,
150 unexpected-keyword-arg,
151 redundant-keyword-arg,
152 missing-kwoa,
153 invalid-sequence-index,
154 invalid-slice-index,
155 assignment-from-none,
156 not-context-manager,
157 invalid-unary-operand-type,
158 unsupported-binary-operation,
159 repeated-keyword,
160 not-an-iterable,
161 not-a-mapping,
162 unsupported-membership-test,
163 unsubscriptable-object,
164 unsupported-assignment-operation,
165 unsupported-delete-operation,
166 invalid-metaclass,
167 logging-unsupported-format,
168 logging-format-truncated,
169 logging-too-many-args,
170 logging-too-few-args,
171 bad-format-character,
172 truncated-format-string,
173 mixed-format-string,
174 format-needs-mapping,
175 missing-format-string-key,
176 too-many-format-args,
177 too-few-format-args,
178 bad-str-strip-call,
179 yield-inside-async-function,
180 not-async-context-manager,
181 fatal,
182 astroid-error,
183 parse-error,
184 method-check-failed,
67185 raw-checker-failed,
68186 bad-inline-option,
69187 locally-disabled,
72190 suppressed-message,
73191 useless-suppression,
74192 deprecated-pragma,
75 apply-builtin,
76 basestring-builtin,
77 buffer-builtin,
78 cmp-builtin,
79 coerce-builtin,
80 execfile-builtin,
81 file-builtin,
82 long-builtin,
83 raw_input-builtin,
84 reduce-builtin,
85 standarderror-builtin,
86 unicode-builtin,
87 xrange-builtin,
88 coerce-method,
89 delslice-method,
90 getslice-method,
91 setslice-method,
92 no-absolute-import,
93 old-division,
94 dict-iter-method,
95 dict-view-method,
96 next-method-called,
97 metaclass-assignment,
98 indexing-exception,
99 raising-string,
100 reload-builtin,
101 oct-method,
102 hex-method,
103 nonzero-method,
104 cmp-method,
105 input-builtin,
106 round-builtin,
107 intern-builtin,
108 unichr-builtin,
109 map-builtin-not-iterating,
110 zip-builtin-not-iterating,
111 range-builtin-not-iterating,
112 filter-builtin-not-iterating,
113 using-cmp-argument,
114 eq-without-hash,
115 div-method,
116 idiv-method,
117 rdiv-method,
118 exception-message-attribute,
119 invalid-str-codec,
120 sys-max-int,
121 bad-python3-import,
122 deprecated-string-function,
123 deprecated-str-translate-call,
124 deprecated-itertools-function,
125 deprecated-types-field,
126 next-method-defined,
127 dict-items-not-iterating,
128 dict-keys-not-iterating,
129 dict-values-not-iterating,
130 deprecated-operator-function,
131 deprecated-urllib-function,
132 xreadlines-attribute,
133 deprecated-sys-function,
134 exception-escape,
135 comprehension-escape,
136 superfluous-parens,
137 line-too-long,
138 missing-docstring,
139 bad-whitespace,
140 invalid-name,
193 c-extension-no-member,
194 literal-comparison,
195 no-self-use,
196 no-classmethod-decorator,
197 no-staticmethod-decorator,
198 cyclic-import,
199 duplicate-code,
200 too-many-ancestors,
201 too-many-instance-attributes,
202 too-few-public-methods,
203 too-many-public-methods,
204 too-many-return-statements,
205 too-many-branches,
206 too-many-arguments,
207 too-many-locals,
208 too-many-statements,
209 too-many-boolean-expressions,
210 consider-merging-isinstance,
211 too-many-nested-blocks,
212 simplifiable-if-statement,
213 redefined-argument-from-local,
214 no-else-return,
215 consider-using-ternary,
216 trailing-comma-tuple,
217 stop-iteration-return,
218 simplify-boolean-expression,
219 inconsistent-return-statements,
220 dangerous-default-value,
221 pointless-statement,
222 pointless-string-statement,
223 expression-not-assigned,
224 unnecessary-pass,
225 unnecessary-lambda,
226 duplicate-key,
227 deprecated-lambda,
228 assign-to-new-keyword,
229 useless-else-on-loop,
230 exec-used,
231 eval-used,
232 confusing-with-statement,
233 using-constant-test,
234 lost-exception,
235 assert-on-tuple,
236 attribute-defined-outside-init,
237 bad-staticmethod-argument,
238 protected-access,
239 arguments-differ,
240 signature-differs,
241 abstract-method,
242 super-init-not-called,
243 no-init,
244 non-parent-init-called,
245 useless-super-delegation,
246 unnecessary-semicolon,
247 mixed-indentation,
248 lowercase-l-suffix,
249 deprecated-module,
250 relative-import,
251 import-self,
252 misplaced-future,
253 fixme,
254 invalid-encoded-data,
255 global-variable-undefined,
256 global-variable-not-assigned,
257 global-at-module-level,
258 unused-variable,
141259 unused-argument,
142 bad-continuation,
143 too-few-public-methods,
144 abstract-method,
260 redefined-outer-name,
261 redefined-builtin,
262 redefine-in-handler,
263 undefined-loop-variable,
264 cell-var-from-loop,
265 bare-except,
266 broad-except,
267 nonstandard-exception,
268 binary-op-exception,
269 raising-format-tuple,
270 property-on-old-class,
271 keyword-arg-before-vararg,
272 logging-not-lazy,
145273 logging-format-interpolation,
146 unused-variable,
147 fixme,
148 wrong-import-order,
149 protected-access,
150 no-member,
151 undefined-variable,
152 cell-var-from-loop,
153 too-many-format-args,
154 no-name-in-module,
155 unidiomatic-typecheck,
156 redefined-outer-name,
157 no-else-return,
158 broad-except,
159 no-self-use,
160 attribute-defined-outside-init,
161 ungrouped-imports,
162 too-many-instance-attributes,
274 bad-format-string-key,
275 unused-format-string-key,
276 bad-format-string,
277 missing-format-argument-key,
278 format-combined-specification,
279 missing-format-attribute,
280 invalid-format-index,
281 anomalous-backslash-in-string,
282 anomalous-unicode-escape-in-string,
283 bad-open-mode,
284 boolean-datetime,
285 redundant-unittest-assert,
286 deprecated-method,
287 bad-thread-instantiation,
288 shallow-copy-environ,
163289 unused-format-string-argument,
164 too-many-branches,
165 too-many-statements,
166 deprecated-lambda,
167 len-as-condition,
168 no-self-argument,
169 singleton-comparison,
170 too-many-lines,
171 too-few-format-args,
172 no-value-for-parameter,
173 old-style-class,
174 no-init,
175 bare-except,
176 return-in-init,
177 too-many-arguments,
178 inconsistent-return-statements,
179 keyword-arg-before-vararg,
180 anomalous-backslash-in-string,
181 trailing-newlines,
182 wrong-import-position,
183 arguments-differ,
184 too-many-ancestors,
185 pointless-string-statement,
186 bad-super-call,
187 literal-comparison,
188 trailing-whitespace,
189290 global-statement,
190 redefined-argument-from-local,
191 too-many-boolean-expressions,
192 unneeded-not,
193 expression-not-assigned,
194 literal-comparison,
195 duplicate-code,
196 cyclic-import,
197 notimplemented-raised,
198 super-init-not-called,
199 lost-exception,
200 unused-wildcard-import,
201 relative-import,
202 redefine-in-handler,
203 useless-else-on-loop,
204 too-many-function-args,
205 redundant-keyword-arg,
206 simplifiable-if-statement,
207 unsubscriptable-object,
208 multiple-statements,
209 empty-docstring,
210 too-many-nested-blocks,
211 consider-iterating-dictionary
212 too-many-public-methods,
213 signature-differs,
214 redefined-builtin,
215 logging-not-lazy,
216 not-callable,
217 too-many-locals,
218 c-extension-no-member,
219 pointless-statement,
220 too-many-public-methods,
221 consider-iterating-dictionary,
222 c-extension-no-member,
223 unnecessary-semicolon,
224 multiple-imports,
225 dangerous-default-value,
226 too-many-return-statements,
227 too-many-public-methods,
228 not-an-iterable,
229 no-method-argument,
230 global-variable-not-assigned,
231 deprecated-method,
232 consider-using-enumerate,
233 access-member-before-definition,
234 missing-final-newline,
235 undefined-loop-variable,
236 global-variable-undefined,
237 useless-super-delegation,
238 useless-super-delegation,
239 method-hidden,
240 unused-import,
241
242
291 unused-import
243292
244293 # Enable the message, report, category or checker with the given id(s). You can
245294 # either give multiple identifier separated by comma (,) or put this option
246295 # multiple time (only on the command line, not in the configuration file where
247296 # it should appear only once). See also the "--disable" option for examples.
248 #enable=c-extension-no-member
297 enable=print-statement,
298 parameter-unpacking,
299 unpacking-in-except,
300 old-raise-syntax,
301 backtick,
302 long-suffix,
303 old-ne-operator,
304 old-octal-literal,
305 import-star-module-level,
306 non-ascii-bytes-literal,
307 apply-builtin,
308 basestring-builtin,
309 buffer-builtin,
310 cmp-builtin,
311 coerce-builtin,
312 execfile-builtin,
313 file-builtin,
314 long-builtin,
315 raw_input-builtin,
316 reduce-builtin,
317 standarderror-builtin,
318 unicode-builtin,
319 xrange-builtin,
320 coerce-method,
321 delslice-method,
322 getslice-method,
323 setslice-method,
324 old-division,
325 dict-iter-method,
326 dict-view-method,
327 next-method-called,
328 metaclass-assignment,
329 indexing-exception,
330 raising-string,
331 reload-builtin,
332 oct-method,
333 hex-method,
334 nonzero-method,
335 cmp-method,
336 round-builtin,
337 intern-builtin,
338 unichr-builtin,
339 map-builtin-not-iterating,
340 zip-builtin-not-iterating,
341 range-builtin-not-iterating,
342 filter-builtin-not-iterating,
343 using-cmp-argument,
344 eq-without-hash,
345 div-method,
346 idiv-method,
347 rdiv-method,
348 exception-message-attribute,
349 invalid-str-codec,
350 sys-max-int,
351 bad-python3-import,
352 deprecated-string-function,
353 deprecated-str-translate-call,
354 deprecated-itertools-function,
355 deprecated-types-field,
356 next-method-defined,
357 dict-items-not-iterating,
358 dict-keys-not-iterating,
359 dict-values-not-iterating
249360
250361
251362 [REPORTS]
0 Dec 19th, 2019
0 * Use Python 3 instead of Python 2 in the Faraday Server
1 * Add ability to manage agents with multiple executors
2 * Agents can be run with custom arguments
3 * Improved processing of uploaded reports. Now it is much faster!
4 * Add custom fields of type `choice`
5 * Fix vuln status transition in bulk create API (mark closed vulns as re-opened when they are triggered again)
6 * Fix bug when using non-existent workspaces in Faraday GTK Client
7 * Set service name as required in the Web UI
8 * Validate the start date of a workspace is not greater than the end date
9 * Fix command API when year is invalid
10 * When SSL misconfigurations cause websockets to fails it doesn't block server from starting
11 * Check for invalid service port number in the Web UI
12 * Fix dashboard tooltips for vulnerability
13 * Fix bug when GTK client lost connection to the server
14 * Fix style issues in "Hosts by Service" modal of the dashboard
15 * Add API for bulk delete of vulnerabilities
16 * Add missing vuln attributes to exported CSV
17 * `faraday-manage support` now displays the Operating System version
18 * Notify when `faraday-manage` can't run becasue of PostgreSQL HBA config error
77 New features in the latest update
88 =====================================
99
10
11 3.10 [Dec 19th, 2019]:
12 ---
13 * Use Python 3 instead of Python 2 in the Faraday Server
14 * Add ability to manage agents with multiple executors
15 * Agents can be run with custom arguments
16 * Improved processing of uploaded reports. Now it is much faster!
17 * Add custom fields of type `choice`
18 * Fix vuln status transition in bulk create API (mark closed vulns as re-opened when they are triggered again)
19 * Fix bug when using non-existent workspaces in Faraday GTK Client
20 * Set service name as required in the Web UI
21 * Validate the start date of a workspace is not greater than the end date
22 * Fix command API when year is invalid
23 * When SSL misconfigurations cause websockets to fails it doesn't block server from starting
24 * Check for invalid service port number in the Web UI
25 * Fix dashboard tooltips for vulnerability
26 * Fix bug when GTK client lost connection to the server
27 * Fix style issues in "Hosts by Service" modal of the dashboard
28 * Add API for bulk delete of vulnerabilities
29 * Add missing vuln attributes to exported CSV
30 * `faraday-manage support` now displays the Operating System version
31 * Notify when `faraday-manage` can't run becasue of PostgreSQL HBA config error
1032
1133 3.9.3 [Nov 12th, 2019]:
1234 ---
0 from __future__ import absolute_import
1
02 import os
13 import packaging.version
24
6769 level = LEVEL # if not level_passed else level_pased
6870 main(level)
6971
72 # I'm Py3
0 extract export csv to reuse for reports
0 ## Download
0 ## About
1
2 Faraday introduces a new concept - IPE (Integrated Penetration-Test Environment) a multiuser Penetration test IDE. Designed for distributing, indexing, and analyzing the data generated during a security audit.
3
4 > Made for true pentesters!
5
6 Faraday was made to let you take advantage of the available tools in the community in a truly multiuser way.
7
8 Designed for simplicity, users should notice no difference between their own terminal application and the one included in Faraday. Developed with a specialized set of functionalities, users improve their own work. Do you remember the last time you programmed without an IDE? What IDEs are to programming, Faraday is to pentesting.
9
10 ![GUI - GTK](https://raw.github.com/wiki/infobyte/faraday/images/client/gtk_main_window.png)
11
12 Faraday crunches the data you load into different visualizations that are useful to managers and pentesters alike.
13
14 ![GUI - Web](https://raw.github.com/wiki/infobyte/faraday/images/dashboard/dashboard.png)
15
16 To read about the latest features check out the [release notes](https://github.com/infobyte/faraday/blob/master/RELEASE.md)!
17
18
19 ## Quickstart
120
221 Refer to the [releases page](https://github.com/infobyte/faraday/releases) for the latest pre-made installers for all supported operating systems.
322
4 ## About
5
6 Faraday introduces a new concept - IPE (Integrated Penetration-Test Environment) a multiuser Penetration test IDE. Designed for distributing, indexing, and analyzing the data generated during a security audit.
7
8 > Made for true pentesters!
9
10 Faraday was made to let you take advantage of the available tools in the community in a truly multiuser way.
11
12 Designed for simplicity, users should notice no difference between their own terminal application and the one included in Faraday. Developed with a specialized set of functionalities, users improve their own work. Do you remember the last time you programmed without an IDE? What IDEs are to programming, Faraday is to pentesting.
13
14 ![GUI - GTK](https://raw.github.com/wiki/infobyte/faraday/images/client/gtk_main_window.png)
15
16 Faraday crunches the data you load into different visualizations that are useful to managers and pentesters alike.
17
18 ![GUI - Web](https://raw.github.com/wiki/infobyte/faraday/images/dashboard/dashboard.png)
19
20 To read about the latest features check out the [release notes](https://github.com/infobyte/faraday/blob/master/RELEASE.md)!
21
22 ## Getting Started!
23
2423 Check out our documentation for detailed information on how to install Faraday in all of our supported platforms:
2524
2625 ![Supported Os](https://raw.github.com/wiki/infobyte/faraday/images/platform/supported.png)
2726
2827 To begin the installation process, check out our [Installation Wiki](https://github.com/infobyte/faraday/wiki/Installation-Community).
2928
29 ## Development
30
31 You need Python 3.6+ and postgres to run the faraday server.
32
33 ### Install OS Dependencies
34
35 You need python 3.6+ and postgres. E.g. in Ubuntu
36
37 ```
38 apt install postgresql python3.6
39 ```
40
41 Make sure postgres is up and running before the next steps.
42
43
44
45 ### Install Python dependencies
46
47 get the latest source
48
49 ```
50 git clone https://github.com/infobyte/faraday
51 ```
52
53 create a virtual environment and install Python dependencies. For example:
54
55 ```
56 cd faraday
57 python3 -m venv .venv
58 source .venv/bin/activate
59 python3 setup.py develop
60
61 ```
62
63 ### Run the server
64
65 In the virtual environment, initialize the faraday database:
66
67 ```
68 sudo faraday-manage initdb
69 ```
70
71 This will give you a randomly generated password to log into the web UI.
72 Now you can start the server
73
74 ```
75 faraday-server
76 ```
77
78 In your browser, now you can go to localhost:5985 and login with "faraday" as username, and the password generated in the initdb step.
79
80
3081 ## New Features!
82
3183 All of Faraday's latest features and updates are always available on our [blog](http://blog.infobytesec.com/search/label/english).
32 There are new entries every few weeks, don't forget to check out our amazing new improvements on it's last entry!
84 There are new entries every few weeks, don't forget to check out our amazing new improvements on its latest entry!
3385
3486
3587 ## Plugins list
4294
4395 [Read more about Plugins](http://github.com/infobyte/faraday/wiki/Plugin-List).
4496
97
4598 ## Features
4699
47100 ### Workspaces
101
48102 Information is organized into various **Workspaces**. Each Workspace contains a pentest team's assignments and all the intel that is discovered.
49103
50104 ### Conflicts
105
51106 If two plugins produce clashing information for an individual element, a conflict that the user will have to resolve is generated. An example is if **user1** incorporates host *127.0.0.1 OS:Linux* and **user2** incorporates *127.0.0.1 OS: Linux Ubuntu 13.10*.
52107
53108 On our [GTK interface](https://github.com/infobyte/faraday/wiki/Usage#gtk-gui) there's a button on the bottom right corner of the main window displaying the number of conflicts in the current workspace. To resolve them, just click on the button and a window will open where you can edit the conflicting objects and select which one to keep.
72127 Read more about the [Faraday Plugin](https://github.com/infobyte/faraday/wiki/faraday-plugin).
73128
74129 ### Notifications
130
75131 Updating objects on other Faraday instances result in notifications on your
76132 Faraday GTK Client.
77133
79135
80136
81137 ### CSV Exporting
138
82139 Faraday supports CSV Exporting from its WEB UI.
83140 [More information](Exporting-the-information)
84141
77 New features in the latest update
88 =====================================
99
10
11 3.10 [Dec 19th, 2019]:
12 ---
13 * Use Python 3 instead of Python 2 in the Faraday Server
14 * Add ability to manage agents with multiple executors
15 * Agents can be run with custom arguments
16 * Improved processing of uploaded reports. Now it is much faster!
17 * Add custom fields of type `choice`
18 * Fix vuln status transition in bulk create API (mark closed vulns as re-opened when they are triggered again)
19 * Fix bug when using non-existent workspaces in Faraday GTK Client
20 * Set service name as required in the Web UI
21 * Validate the start date of a workspace is not greater than the end date
22 * Fix command API when year is invalid
23 * When SSL misconfigurations cause websockets to fails it doesn't block server from starting
24 * Check for invalid service port number in the Web UI
25 * Fix dashboard tooltips for vulnerability
26 * Fix bug when GTK client lost connection to the server
27 * Fix style issues in "Hosts by Service" modal of the dashboard
28 * Add API for bulk delete of vulnerabilities
29 * Add missing vuln attributes to exported CSV
30 * `faraday-manage support` now displays the Operating System version
31 * Notify when `faraday-manage` can't run becasue of PostgreSQL HBA config error
1032
1133 3.9.3 [Nov 12th, 2019]:
1234 ---
0 with (import <nixpkgs> {});
1
2 stdenv.mkDerivation {
3 name = "faraday-nix.pth";
4 packages = with python37Packages; [virtualenv pip pyopenssl psycopg2 pillow pygobject3 pynacl matplotlib lxml ldap autobahn gssapi setproctitle simplejson pycairo ];
5 builder = ./buildpth.sh;
6 }
0 for package in $packages
1 do
2 echo $package/lib/python3.7/site-packages >>$out
3 done
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 from __future__ import absolute_import
34 import csv
45 import click
56 from collections import OrderedDict
6364
6465 if __name__ == "__main__":
6566 fix_severities()
67 # I'm Py3
1515 # add these directories to sys.path here. If the directory is relative to the
1616 # documentation root, use os.path.abspath to make it absolute, like shown here.
1717 #
18 from __future__ import absolute_import
1819 import os
1920 import sys
2021 sys.path.insert(0, os.path.abspath('..'))
169170
170171
171172
173 # I'm Py3
11 # Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
33
4 __version__ = '3.9.3'
4 __version__ = '3.10.0'
55 __license_version__ = __version__
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 '''
0 """
21 Faraday Penetration Test IDE
32 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
43 See the file 'doc/LICENSE' for the license information
54
6 '''
7
5 """
86 import socket
97 import threading
108 import logging
119 import base64
1210
1311 from flask import Flask, request, jsonify
14 from tornado.wsgi import WSGIContainer
15 from tornado.httpserver import HTTPServer
16 from tornado.ioloop import IOLoop
12 from tornado.wsgi import WSGIContainer # pylint: disable=import-error
13 from tornado.httpserver import HTTPServer # pylint: disable=import-error
14 from tornado.ioloop import IOLoop # pylint: disable=import-error
15 from tornado import gen # pylint: disable=import-error
1716
1817 from faraday.client.model.visitor import VulnsLookupVisitor
1918
3534 global _http_server
3635 global ioloop_instance
3736 if _http_server is not None:
38 ioloop_instance.stop()
39 _http_server.stop()
37 # Code taken from https://github.com/tornadoweb/tornado/issues/1791#issuecomment-409258371
38 async def shutdown():
39 _http_server.stop()
40 await gen.sleep(1)
41 ioloop_instance.stop()
42 ioloop_instance.add_callback_from_signal(shutdown)
4043
4144
4245 def startAPIs(plugin_controller, model_controller, hostname, port):
7881
7982 logging.getLogger("tornado.access").addHandler(logging.getLogger(__name__))
8083 logging.getLogger("tornado.access").propagate = False
81 threading.Thread(target=startServer).start()
82
83
84 class RESTApi(object):
84 threading.Thread(target=startServer, name='restapi-server').start()
85
86
87 class RESTApi:
8588 """ Abstract class for REST Controllers
8689 All REST Controllers should extend this class
8790 in order to get published"""
311314 if 'pid' in json_data.keys():
312315 if 'pwd' in json_data.keys():
313316 try:
314 cmd = base64.b64decode(json_data.get('cmd'))
315 pwd = base64.b64decode(json_data.get('pwd'))
317 cmd = base64.b64decode(json_data.get('cmd')).decode()
318 pwd = base64.b64decode(json_data.get('pwd')).decode()
316319 except:
317320 cmd = ''
318321 pwd = ''
354357 return self.ok("active plugins cleared")
355358
356359
357 class Route(object):
360 class Route:
358361 """ Route class, abstracts information about:
359362 path, handler and methods """
360363 def __init__(self, **kwargs):
361364 for k, v in kwargs.items():
362365 setattr(self, k, v)
366
367
368 # I'm Py3
00 #!/usr/bin/env python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
77 import requests
88 import json
99 import base64
1010
1111
12 class RestApiClient(object):
12 class RestApiClient:
1313 def __init__(self, hostname, port):
1414 self.hostname = hostname
1515 self.port = port
16 self.url = "http://%s:%d/" % (self.hostname, self.port)
16 self.url = f"http://{self.hostname}:{self.port}/"
1717 self.headers = {'Content-type': 'application/json', 'Accept': 'application/json'}
1818
1919
2222 super(ModelRestApiClient, self).__init__(hostname, port)
2323
2424 def _create(self, obj_class_url, **kwargs):
25 url = self.url + ('model/%s' % obj_class_url)
25 url = f"{self.url}model/{obj_class_url}"
2626 data = {}
2727 for k, v in kwargs.items():
2828 data[k] = v
8383 "cred", username=username, password=password, parent_id=parent_id)
8484
8585
86 class PluginControllerAPIClient(object):
86 class PluginControllerAPIClient:
8787 def __init__(self, hostname, port):
8888 self.hostname = hostname
8989 self.port = port
127127 if response.status_code != 200:
128128 return False
129129 return True
130
131
132 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 # I'm Py3
0 #!/usr/bin/env python2.7
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
4 """
95 import requests
106 from tqdm import tqdm
117 from dateutil import parser
7167 vulns = get_vulns_from_workspace(s, url, workspace)
7268 vulns_closed = close_vulns(s, url, workspace, vulns, float(vuln_duration))
7369
74 print "[+] {count} vulnerabilities closed in workspace '{ws}'".format(count=vulns_closed, ws=workspace)
70 print("[+] {count} vulnerabilities closed in workspace '{ws}'".format(count=vulns_closed, ws=workspace))
7571 return 0, None
72
73
74 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66 """
7 from __future__ import absolute_import
78 from __future__ import print_function
89 from faraday.client.persistence.server.server_io_exceptions import ResourceDoesNotExist
910 from faraday.client.persistence.server import models
4748
4849 print ("End of process:", count, "vulnerabilities changed to closed")
4950 return 0, None
51
52
53 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66 """
7 from __future__ import absolute_import
8 from __future__ import print_function
79
810 from faraday.client.model.common import factory
911 from faraday.client.persistence.server import models
5759 return 2, None
5860
5961 return 0, old.getID()
62
63
64 # I'm Py3
0 #!/usr/bin/env python2.7
1 # -*- coding: utf-8 -*-
20 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
64 """
7
85 from faraday.client.model.common import factory
96 from faraday.client.persistence.server import models
107
5350 return 2, None
5451
5552 return 0, old_host.getID()
53
54
55 # I'm Py3
0 #!/usr/bin/env python2.7
1 # -*- coding: utf-8 -*-
20 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
64 """
7
85 from faraday.client.model.common import factory
96 from faraday.client.persistence.server import models
107
2522
2623 parsed_args = parser.parse_args(args)
2724
28 ports = filter(None, parsed_args.ports.split(','))
25 ports = list(filter(None, parsed_args.ports.split(',')))
2926 res_ids = [] #new service or old services ids affected by the command
3027 for port in ports:
3128 params = {
5855 res_ids.append(old.getID())
5956
6057 return 0, res_ids
58
59
60 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66 """
7 from __future__ import absolute_import
8 from __future__ import print_function
79
810 from faraday.client.model.common import factory
911 from faraday.client.persistence.server import models
7981 )
8082
8183 return 0, new[0].getID()
84
85
86 # I'm Py3
0 #!/usr/bin/env python2.7
1 # -*- coding: utf-8 -*-
20 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
64 """
7
85 from faraday.client.model.common import factory
96 from faraday.client.persistence.server import models
107
8582 return 2, None
8683
8784 return 0, old.getID()
85
86
87 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
7 """
8 from __future__ import absolute_import
9 from __future__ import print_function
10
11 from builtins import input
812
913 from faraday.client.persistence.server import models
1014
1923 msg = ("Are you sure you want to delete all hosts in the "
2024 "workspace {}? This action can't be undone [y/n] ".format(
2125 workspace))
22 if raw_input(msg) not in ('y', 'yes'):
26 if input(msg) not in ('y', 'yes'):
2327 return 1, None
2428 for host in models.get_hosts(workspace):
2529 print('Delete Host:' + host.name)
2630 models.delete_host(workspace, host.id)
2731 return 0, None
32
33
34 # I'm Py3
0 #!/usr/bin/env python2.7
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
4 """
95 from faraday.client.persistence.server import models
106 from faraday.utils.user_input import query_yes_no
117
2723 print('Deleted service: ' + service.name)
2824 models.delete_service(workspace, service.id)
2925 return 0, None
26
27
28 # I'm Py3
0 #!/usr/bin/env python2.7
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
4 """
5 from __future__ import absolute_import
6 from __future__ import print_function
7 from builtins import input
88
99 import re
1010 from faraday.client.persistence.server import models
2525 "matching the regex {} in the worspace {}? "
2626 "This action can't be undone [y/n] ".format(
2727 parsed_args.regex, workspace))
28 if raw_input(msg) not in ('y', 'yes'):
28 if input(msg) not in ('y', 'yes'):
2929 return 1, None
3030
3131 for vuln in models.get_all_vulns(workspace):
3333 print("Delete Vuln: " + vuln.name)
3434 models.delete_vuln(workspace, vuln.id)
3535 return 0, None
36
37
38 # I'm Py3
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
8
7 """
8 from __future__ import absolute_import
9 from __future__ import print_function
10
11 from builtins import input
912 import os
1013 import sys
1114 import base64
2326 __prettyname__ = 'FBrute'
2427
2528 SUPPORTED_SERVICES = ["asterisk", "cisco", "cisco-enable", "cvs", "firebird", "ftp", "ftps", "http",
26 "https", "http-proxy", "icq" "imap", "imaps", "irc", "ldap2", "ldap3",
29 "https", "http-proxy", "icq", "imap", "imaps", "irc", "ldap2", "ldap3",
2730 "mssql", "mysql", "nntp", "oracle-listener", "oracle-sid", "pcanywhere",
2831 "pcnfs", "pop3", "pop3s", "postgres", "rdp", "redis", "rexec", "rlogin",
2932 "rsh", "rtsp", "s7-300", "sip", "smb", "smtp", "smtps", "smtp-enum", "snmp",
113116 def input_index(text, leng):
114117 while 1:
115118
116 stdin = raw_input(text+"[0-"+str(leng-1)+"/q]: ")
119 stdin = input(text+"[0-"+str(leng-1)+"/q]: ")
117120
118121 if re.search("[0-9]", stdin) is not None:
119122
120123 if int(stdin) > leng-1 or int(stdin) < 0:
121124 continue
122
123 else:
124 return stdin
125 return stdin
125126
126127 elif stdin == "q":
127128 sys.exit(1)
153154
154155 #Le pido el path de el user dic y el password dic
155156 if dictionary == 0:
156 usernames_dic_path = raw_input("Usernames file: ")
157 passwords_dic_path = raw_input("Passwords file: ")
157 usernames_dic_path = input("Usernames file: ")
158 passwords_dic_path = input("Passwords file: ")
158159 user_define_dictionary = True
159160
160161 else:
234235 else:
235236 sys.exit("Hydra is not installed on the system. Install hydra to continue execution")
236237 return None, None
238
239
240 # I'm Py3
0 #!/usr/bin/env python2.7
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
4 """
5 from past.builtins import cmp
6
7 import functools
88
99 from colorama import Fore
1010 import sys
3939
4040 def main(workspace='', args=None, parser=None):
4141 parser.add_argument('-p', type=int, nargs='+', metavar='port', help='List of ports to filter', default=[])
42 parser.add_argument('services', nargs='*', help='List of service names', default=[]),
42 parser.add_argument('services', nargs='*', help='List of service names', default=[])
4343 parser.add_argument('--columns', help='Comma separated list of columns to show.',
44 default="host,service,ports,protocol,status,host_os", choices=COLUMNS.keys())
44 default="host,service,ports,protocol,status,host_os", choices=list(COLUMNS.keys()))
4545
4646 parser.add_argument('--status', help='Comma separated list of status to filter for.')
4747
7272 if parsed_args.additional_info and not parsed_args.no_filter:
7373 print('Filtering services for ports: ' + ', '.join(map(str, sorted(port_list))))
7474
75 columns = filter(None, parsed_args.columns.split(','))
75 columns = list(filter(None, parsed_args.columns.split(',')))
7676
7777 status_filter = None
7878
7979 if parsed_args.status is not None:
80 status_filter = filter(None, parsed_args.status.split(','))
80 status_filter = list(filter(None, parsed_args.status.split(',')))
8181
8282 lines = []
8383
107107
108108 if parsed_args.sorted:
109109 # Compare lines using the first column (IP)
110 for row in sorted(lines, cmp=lambda l1, l2: cmp(l1[0], l2[0])):
110 for row in sorted(lines, key=functools.cmp_to_key(lambda l1, l2: cmp(l1[0], l2[0]))): # passed from py2 to py2/3, TODO check
111111 print("".join(word.ljust(col_width) for word in row))
112112 else:
113113 for row in lines:
114114 print("".join(word.ljust(col_width) for word in row))
115115
116116 return 0, None
117
118
119 # I'm Py3
0 #!/usr/bin/env python2.7
1 # -*- coding: utf-8 -*-
2
30 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74 """
5 from builtins import input
86
97 import os
108 import sys
1513 import inspect
1614 import argparse
1715 import readline
18 from Queue import Queue
16 from queue import Queue
1917
2018 from faraday.client.plugins import fplugin_utils
2119
5250 session_cookie = login_user(args.url, username, password)
5351 if not session_cookie:
5452 raise UserWarning('Invalid credentials!')
55 else:
56 CONF.setDBUser(username)
57 CONF.setDBSessionCookies(session_cookie)
53
54 CONF.setDBUser(username)
55 CONF.setDBSessionCookies(session_cookie)
5856
5957 if '--' in unknown:
6058 unknown.remove('--')
6967 if not args.interactive:
7068 sys.exit(1)
7169
72 if args.command not in plugins.keys():
70 if args.command not in list(plugins.keys()):
7371 sys.stderr.write(Fore.RED +
7472 ("ERROR: Plugin %s not found.\n" % args.command) +
7573 Fore.RESET)
7876 else:
7977 sys.exit(1)
8078
81 from faraday import client
79 from faraday import client # pylint:disable=import-outside-toplevel
8280 faraday_directory = os.path.dirname(os.path.realpath(os.path.join(client.__file__)))
8381
8482 plugin_path = os.path.join(faraday_directory, "bin/", args.command + '.py')
153151
154152 plugins = fplugin_utils.get_available_plugins()
155153
156 for plugin in sorted(plugins.iterkeys()):
154 for plugin in sorted(plugins.keys()):
157155 epilog += '\t- %s: %s\n' % (plugin, plugins[plugin]['description'])
158156
159157 parser = argparse.ArgumentParser(description=description,
214212
215213 while True:
216214 try:
217 line = raw_input("> ")
215 line = input("> ")
218216
219217 if line.strip() == 'exit':
220218 os._exit(0)
224222 new_args += ['--username', args.username, '--password', args.password]
225223
226224 if '-i' in new_args or '--interactive' in new_args:
227 print 'Already in interactive mode!'
225 print('Already in interactive mode!')
228226 continue
229227
230228 if 'h' in new_args or 'help' in new_args:
241239 last_id = dispatch(parsed_args, new_unknown, parser.format_help(), args.username, args.password) or last_id
242240 # print '$last = %s' % last_id
243241 except (EOFError, KeyboardInterrupt):
244 print 'Bye Bye!'
242 print('Bye Bye!')
245243 sys.exit(0)
246244 except SystemExit:
247245 pass
249247
250248 if __name__ == '__main__':
251249 main()
250
251
252 # I'm Py3
0 #!/usr/bin/env python2.7
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
4 """
85 import re
96 from faraday.client.persistence.server import models
107
2421 print('Hosts that has invalid ip addresses {0}'.format(not_matching_count))
2522
2623 return 0, None
24
25
26 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
2 '''
2 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
6 '''
6 """
7 from __future__ import absolute_import
8 from __future__ import print_function
9
710 import json
811
912 import requests
120123 checkSeverity(v, cwe, parsed_args.severity, workspace, parsed_args.couchdb)
121124
122125 return 0, None
126
127
128 # I'm Py3
0 #!/usr/bin/env python2.7
1
20 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
53 See the file "doc/LICENSE" for the license information
64 """
7
85 import csv
96 from time import mktime
107 from datetime import datetime
3936
4037 obj = {}
4138
42 for key, val in columns.iteritems():
39 for key, val in columns.items():
4340
4441 # Default data
4542 value = {val : ""}
4744 if val == "service_id":
4845 value["parent"] = register["service_id"]
4946
50 if val == "owned" or val == "confirmed":
47 if val in ["owned", "confirmed"]:
5148 value[val] = False
5249
53 elif val == "ports" or val == "hostnames" or val == "refs" or val == "policyviolations":
50 elif val in ["ports", "hostnames", "refs", "policyviolations"]:
5451 value[val] = []
5552
5653 elif key == "service_status":
5754 value[val] = "open"
5855
59 elif key == "vulnerability_status" or key == "vulnerability_web_status":
56 elif key in ["vulnerability_status", "vulnerability_web_status"]:
6057 value[val] = "opened"
6158
62 elif key == "vulnerability_severity" or key == "vulnerability_web_severity":
59 elif key in ["vulnerability_severity", "vulnerability_web_severity"]:
6360 value[val] = "info"
6461
6562 # Copy data to new object
7168 if val == "ports":
7269 value[val] = [register[key]]
7370
74 elif val == "owned" or val == "confirmed":
71 elif val in ["owned", "confirmed"]:
7572 if register[key] == "true":
7673 value[val] = True
7774
7976 value["description"] = register[key]
8077 value["desc"] = register[key]
8178
82 elif val == "refs" or val == "hostnames" or val == "policyviolations":
79 elif val in ["refs", "hostnames", "policyviolations"]:
8380 value[val] = register[key].split(",")
8481
8582 elif key == "service_status":
8683 if register[key].lower() in SERVICE_STATUS:
8784 value[val] = register[key]
8885
89 elif key == "vulnerability_status" or key =="vulnerability_web_status":
86 elif key in ["vulnerability_status", "vulnerability_web_status"]:
9087 if register[key].lower() in VULN_STATUS:
9188 value[val] = register[key]
9289
93 elif key == "vulnerability_severity" or key == "vulnerability_web_severity":
90 elif key in ["vulnerability_severity", "vulnerability_web_severity"]:
9491 if register[key].lower() == 'informational':
9592 register[key] = 'info'
9693 if register[key].lower() == 'medium':
104101 obj.update(value)
105102
106103 # Check if obj is Invalid, return None
107 for key, val in obj.iteritems():
108 if val != [""] and val != [] and val != "" and val != False and val != "info" and val != "opened" and val != "open":
104 for key, val in obj.items():
105 if val not in [[""], [], "", False, "info", "opened", "open"]:
109106 return obj
110107
111108 return None
329326 print("[*]", counter, "new Faraday objects created.")
330327 file_csv.close()
331328 return 0, None
329
330
331 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22
33 """
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
77 """
8
98 import os
109
1110 from faraday.client.model.common import factory
1211 from faraday.client.persistence.server import models
12
13 PCAP_IMPORTED = False
14
15 try:
16 from pcapfile import savefile
17 import pcapfile
18 PCAP_IMPORTED = True
19 except ImportError:
20 pass
21
1322
1423 __description__ = 'Import every host found in a PCAP file for further scanning'
1524 __prettyname__ = 'Import PCAP'
1726
1827 def main(workspace='', args=None, parser=None):
1928
20 parser.add_argument('-s', '--source', nargs='*', help='Filter packets by source'),
21 parser.add_argument('-d', '--dest', nargs='*', help='Filter packets by destination'),
29 if not PCAP_IMPORTED:
30 print('capfile not found, please install it to use this plugin.'
31 ' You can do it executing pip2 install pcapfile in a shell.')
32 return 1, None
33
34 parser.add_argument('-s', '--source', nargs='*', help='Filter packets by source')
35 parser.add_argument('-d', '--dest', nargs='*', help='Filter packets by destination')
2236
2337 parser.add_argument('--dry-run', action='store_true', help='Do not touch the database. Only print the object ID')
2438
2539 parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output from the pcapfile library.')
26 parser.add_argument('pcap', help='Path to the PCAP file'),
40 parser.add_argument('pcap', help='Path to the PCAP file')
2741
2842 parsed_args = parser.parse_args(args)
29
30 try:
31 from pcapfile import savefile
32 import pcapfile
33 except ImportError:
34 print('capfile not found, please install it to use this plugin.' \
35 ' You can do it executing pip2 install pcapfile in a shell.')
36 return 1, None
3743
3844 if not os.path.isfile(parsed_args.pcap):
3945 print("pcap file not found: " % parsed_args.pcap)
100106 print('%s\t%s' % (dst, obj.getID()))
101107
102108 return 0, None
109
110
111 # I'm Py3
0 #!/usr/bin/env python2.7
1
2 '''
0 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
6 '''
7
4 """
85 from faraday.client.persistence.server import models
96
107 __description__ = 'Get all stored credentials'
1714 for credential in models.get_credentials(workspace):
1815 print(credential.username + ' : ' + credential.password)
1916 return 0, None
17
18
19 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66 """
7 from __future__ import absolute_import
8 from __future__ import print_function
79
810 from faraday.client.persistence.server import models
911
1214
1315
1416 def main(workspace='', args=None, parser=None):
15 parser.add_argument('os_filter', nargs='*', help='List of OSs to filter for', default=[]),
17 parser.add_argument('os_filter', nargs='*', help='List of OSs to filter for', default=[])
1618
1719 parsed_args = parser.parse_args(args)
1820
2224 print('%s\t%s' % (host.name, host.os))
2325
2426 return 0, None
27
28
29 # I'm Py3
0 #!/usr/bin/env python2.7
1 '''
0 #!/usr/bin/env python3
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
5 '''
5 """
6 from __future__ import absolute_import
7 from __future__ import print_function
68
79 from faraday.client.persistence.server import models
810
2527 print(host.name)
2628
2729 if parsed_args.sorted:
28 print '\n'.join(sorted(ips))
30 print('\n'.join(sorted(ips)))
2931
3032 return 0, None
33
34
35 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
7 """
8 from __future__ import absolute_import
9 from __future__ import print_function
810
911 __description__ = 'Lists all scanned OSs'
1012 __prettyname__ = 'Get All OSs'
3537 print('%s\t(%d)' % (host, count))
3638
3739 return 0, None
40
41
42 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66 """
7 from __future__ import absolute_import
78 from __future__ import print_function
89 import os
910 import sys
1617
1718 def screenshot(path, protocol, ip, port):
1819 try:
19 from selenium import webdriver
20 from selenium import webdriver # pylint:disable=import-outside-toplevel
2021 except ImportError:
2122 print("Missing dependencies: (selenium). "
2223 "Install it with pip install selenium. ")
4748
4849 if not os.path.exists(path):
4950 print("Invalid Path")
50 exit()
51 sys.exit()
5152
5253 try:
5354 services = models.get_services(workspace)
6869 print(protocol + "://" + ip + ":" + port)
6970 screenshot(path, protocol, ip, port)
7071 return 0, None
72 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66
7 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 from __future__ import absolute_import
7
68 """
79 This module contains the definition of all the CustomEvent's used
810 in the application.
3436 DELETEOBJECT = 8888
3537 UPDATEOBJECT = 9999
3638
37 class CustomEvent(object):
39 class CustomEvent:
3840 def __init__(self, type):
3941 self._type = type
4042 self._time = time.time()
175177 def __init__(self, obj):
176178 CustomEvent.__init__(self, UPDATEOBJECT)
177179 self.obj = obj
180
181
182 # I'm Py3
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
33
4 # I'm Py3
0 #!/usr/bin/python2.7
0 #!/usr/bin/python3
11 # -*- coding: utf-8 -*-
2 '''
2 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66
7 '''
7 """
8 from __future__ import absolute_import
9 from __future__ import print_function
10 from past.builtins import basestring
811
912 import os
1013 import sys
8891
8992 logger = logging.getLogger(__name__)
9093
94
9195 class GuiApp(Gtk.Application, FaradayUi):
9296 """
9397 Creates the application and has the necesary callbacks to FaradayUi
480484 """
481485 GObject.idle_add(loading_workspace, 'show')
482486 try:
483 ws = super(GuiApp, self).openWorkspace(workspace_name)
487 ws = self.openWorkspace(workspace_name)
484488 GObject.idle_add(CONF.setLastWorkspace, ws.name)
485489 GObject.idle_add(CONF.saveConfig)
486490 except Exception as e:
497501 self.select_active_workspace()
498502 return False
499503
500 thread = threading.Thread(target=background_process)
504 thread = threading.Thread(target=background_process, name='background_process')
501505 thread.daemon = True
502506 thread.start()
503507
717721
718722 plugins = fplugin_utils.get_available_plugins()
719723
720 for plugin in sorted(plugins.iterkeys()):
724 for plugin in sorted(plugins.keys()):
721725 gio_action = Gio.SimpleAction.new('fplugin_%s' % plugin, None)
722726 gio_action.connect("activate", self.type_faraday_plugin_command)
723727 self.add_action(gio_action)
871875
872876 def select_plugin():
873877 """Creates a simple dialog with a combo box to select a plugin"""
874 plugins_id = [_id for _id in self.plugin_manager.getPlugins()]
878 plugins_id = list(self.plugin_manager.getPlugins())
875879 plugins_id = sorted(plugins_id, key=lambda s: s.lower())
876880 dialog = Gtk.Dialog("Select plugin", self.window, 0)
877881
974978 command = fplugin_utils.build_faraday_plugin_command(plugin, active_workspace.getName())
975979 fd = terminal.get_pty().get_fd()
976980 os.write(fd, command)
981
982
983 # I'm Py3
0 #!/usr/bin/python2.7
0 #!/usr/bin/python3
11 # -*- coding: utf-8 -*-
2 '''
2 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66
7 '''
7 """
8 from __future__ import absolute_import
9
810 import os
9 import gi
11 import gi # pylint: disable=import-error
1012
1113 from faraday.config.configuration import getInstanceConfiguration
1214 from faraday.client.start_client import FARADAY_CLIENT_BASE
1315
1416 gi.require_version('Gtk', '3.0')
1517
16 from gi.repository import GLib, Gio, Gtk, GObject, Gdk
17 from dialogs import ImportantErrorDialog
18 from gi.repository import GLib, Gio, Gtk, GObject, Gdk # pylint: disable=import-error
19 from faraday.client.gui.gtk.dialogs import ImportantErrorDialog
1820
1921 CONF = getInstanceConfiguration()
2022
337339 is not sure if he wants to exit"""
338340 self.delete_tab()
339341 terminal.start_faraday()
342
343
344 # I'm Py3
0 #!/usr/bin/python2.7
0 #!/usr/bin/python3
11 # -*- coding: utf-8 -*-
2 '''
2 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
88 GObject Instrospection 3.12, 3.16 and 3.20 (Ubuntu 14.04, Brew on Mac OS and
99 Arch, respectively) and VTE API 2.90 and 2.91 (Ubuntu 14.04 has 2.90, last one
1010 is 2.91)
11 '''
11 """
12 from __future__ import absolute_import
1213
13 import gi
14 import gi # pylint: disable=import-error
1415 gi_version = gi.__version__
1516
1617 gi.require_version('Gtk', '3.0')
2122 gi.require_version('Vte', '2.90')
2223 vte_version = '2.90'
2324
24 from gi.repository import Vte, Gtk
25 from gi.repository import Vte, Gtk # pylint: disable=import-error
2526
2627
2728 class CompatibleVteTerminal(Vte.Terminal):
6061
6162 def set_overlay_scrolling(self, boolean):
6263 """Return the set_overlay_scrolling method, if it can."""
63 if gi_version == '3.12.0' or gi_version == '3.14.0':
64 if gi_version in ['3.12.0', '3.14.0']:
6465 return None
6566 else:
6667 return Gtk.ScrolledWindow.set_overlay_scrolling(self, boolean)
68
69
70 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 from __future__ import absolute_import
4
35 import requests
4 from gi.repository import Gtk
6 from gi.repository import Gtk # pylint: disable=import-error
57 from faraday.server.utils.logger import get_logger
68 from functools import wraps
7 from compatibility import CompatibleScrolledWindow as GtkScrolledWindow
9 from faraday.client.gui.gtk.compatibility import CompatibleScrolledWindow as GtkScrolledWindow
810 from faraday.client.persistence.server.server_io_exceptions import ServerRequestException
911
1012 def safe_io_with_server(response_in_emergency):
5961 return scroll_object_wrapper
6062
6163 return scrollable_decorator
64
65
66 # I'm Py3
0 #!/usr/bin/python2.7
0 #!/usr/bin/python3
11 # -*- coding: utf-8 -*-
2 '''
2 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66
7 '''
7 """
8 from __future__ import absolute_import
9 from past.builtins import basestring
10
11
812 import webbrowser
9 import gi
13 import gi # pylint: disable=import-error
1014 import os
11
15 from faraday.client.start_client import FARADAY_CLIENT_BASE
1216 gi.require_version('Gtk', '3.0')
1317
1418 from faraday.client.persistence.server.server import ResourceDoesNotExist
15 from gi.repository import Gtk, GdkPixbuf, Gdk
19 from gi.repository import Gtk, GdkPixbuf, Gdk # pylint: disable=import-error
1620 from faraday.config.configuration import getInstanceConfiguration
1721 from faraday.client.persistence.server.server import is_authenticated, login_user, get_user_info, check_server_url
1822 from faraday.client.model import guiapi
19 from decorators import scrollable
20
21 from compatibility import CompatibleScrolledWindow as GtkScrolledWindow
23 from faraday.client.gui.gtk.decorators import scrollable
24
25 from faraday.client.gui.gtk.compatibility import CompatibleScrolledWindow as GtkScrolledWindow
2226 from faraday.client.plugins import fplugin_utils
2327
2428 CONF = getInstanceConfiguration()
218222
219223 return True
220224
221 if run == Gtk.ResponseType.CANCEL or run == -4:
225 if run in [Gtk.ResponseType.CANCEL, -4]:
222226 # run returns -4 when escape key pressed
223227 self.exit()
224228 return False
456460 and plugin version"""
457461 plugin_info = Gtk.TreeStore(str, str, str, str)
458462
459 for plugin_id, params in self.plugin_settings.iteritems():
463 for plugin_id, params in self.plugin_settings.items():
460464 plugin_info.append(None, [plugin_id,
461465 params["name"],
462466 params["version"], # tool version
491495
492496 models = {}
493497
494 for plugin_id in self.plugin_settings.iteritems():
498 for plugin_id in self.plugin_settings.items():
495499 # iter through the plugins
496500 plugin_info = plugin_id[1] # get dictionary associated to plugin
497501 store = Gtk.ListStore(str, str) # create the store for that plugin
10951099 def safe_wrapper(*args, **kwargs):
10961100 try:
10971101 return func(*args, **kwargs)
1098 except IndexError, ValueError:
1102 except (IndexError, ValueError):
10991103 dialog = errorDialog(self, ("There has been a problem. "
11001104 "The object you clicked on "
11011105 "does not exist anymore."))
15811585 elif original_type == "float":
15821586 raw_prop = float(prop)
15831587
1584 elif original_type == "str" or original_type == "unicode":
1588 elif original_type in ["str", "unicode"]:
15851589 raw_prop = prop
15861590 else:
15871591 raw_prop = prop
17781782 return True
17791783 else:
17801784 return False
1785
1786
1787 # I'm Py3
0 #!/usr/bin/python2.7
0 #!/usr/bin/python3
11 # -*- coding: utf-8 -*-
2 '''
2 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66
7 '''
8 import gi
7 """
8 from __future__ import absolute_import
9 from __future__ import division
10
11 import gi # pylint: disable=import-error
912 import os
1013 import math
1114 import webbrowser
1720 except ValueError:
1821 gi.require_version('Vte', '2.90')
1922
20 from gi.repository import Gtk, Gdk, GLib, Pango, GdkPixbuf, Vte
21
22 from decorators import scrollable
23 from compatibility import CompatibleVteTerminal as VteTerminal
24 from compatibility import CompatibleScrolledWindow as GtkScrolledWindow
23 from gi.repository import Gtk, Gdk, GLib, Pango, GdkPixbuf, Vte # pylint: disable=import-error
24
25 from faraday.client.gui.gtk.decorators import scrollable
26 from faraday.client.gui.gtk.compatibility import CompatibleVteTerminal as VteTerminal
27 from faraday.client.gui.gtk.compatibility import CompatibleScrolledWindow as GtkScrolledWindow
2528
2629
2730 class Terminal(VteTerminal):
3437 """Initialize terminal with infinite scrollback, no bell, connecting
3538 all keys presses to copy_or_past, and starting faraday-terminal
3639 """
37 from faraday.client.start_client import FARADAY_BASE, FARADAY_CLIENT_BASE
40 from faraday.client.start_client import FARADAY_BASE, FARADAY_CLIENT_BASE # pylint:disable=import-outside-toplevel
3841 VteTerminal.__init__(self)
3942 self.set_scrollback_lines(-1)
4043 self.set_audible_bell(0)
149152 def number_of_pages(self):
150153 if self.host_amount_total == 0:
151154 return 1
152 return int(math.ceil(float(self.host_amount_total) / 20))
155 return int(math.ceil(self.host_amount_total / 20.0))
153156
154157 @scrollable(width=160)
155158 def scrollable_view(self):
255258 vuln_count = host.getVulnsAmount()
256259 os_icon, os_str = self.__decide_icon(host.getOS())
257260 display_str = str(host)
258 if str(host.id) not in map(lambda host_data: host_data[0], self.model):
261 if str(host.id) not in [host_data[0] for host_data in self.model]:
259262 host_iter = self.model.append([str(host.id), os_icon, os_str, display_str, vuln_count])
260263 self.host_id_to_iter[host.id] = host_iter
261264 self.host_amount_in_model += 1
266269 """
267270 space_left_in_sidebar = 20 - self.host_amount_in_model
268271 relevant_hosts = hosts[0:space_left_in_sidebar] # just ignore those coming after
269 map(self._add_single_host_to_model, relevant_hosts)
272 [self._add_single_host_to_model(h) for h in relevant_hosts]
270273
271274 def _update_single_host_name_in_model(self, host_id, host_iter):
272275 """Take a host_id and a host_iter. Changes the string representation
286289 in the model. Potentially slow, makes len(hosts) requests to the server.
287290 Return None.
288291 """
289 hosts_ids = map(lambda h: h.id, hosts)
290 relevant_hosts = filter(self._is_host_in_model_by_host_id, hosts_ids)
291 host_iters = map(lambda h: self.host_id_to_iter[h], relevant_hosts)
292 map(self._update_single_host_name_in_model, relevant_hosts, host_iters)
292 hosts_ids = [h.id for h in hosts]
293 relevant_hosts = list(filter(self._is_host_in_model_by_host_id, hosts_ids))
294 host_iters = [self.host_id_to_iter[h] for h in relevant_hosts]
295 list(map(self._update_single_host_name_in_model, relevant_hosts, host_iters))
293296
294297 def _remove_single_host_from_model(self, host_id):
295298 """Remove the host of host_id from the model. Return None.
306309 def remove_relevant_hosts_from_model(self, host_ids):
307310 """Takes a list of host_ids and deletes the one found on the model
308311 from there. Return None."""
309 relevant_host_ids = filter(self._is_host_in_model_by_host_id, host_ids)
310 map(self._remove_single_host_from_model, relevant_host_ids)
312 relevant_host_ids = list(filter(self._is_host_in_model_by_host_id, host_ids))
313 list(map(self._remove_single_host_from_model, relevant_host_ids))
311314
312315 def _modify_vuln_amount_of_single_host_in_model(self, host_id, new_vuln_amount):
313316 """Take a host_id and a new_vuln amount and modify the string representation
337340 one vulnerability from them, according to the plus_one_or_minus_one
338341 function. Return None.
339342 """
340 relevant_host_ids = filter(self._is_host_in_model_by_host_id, host_ids)
341 host_iters = map(lambda h: self.host_id_to_iter[h], relevant_host_ids)
342 vuln_amount_of_those_hosts = map(self._get_vuln_amount_from_model, host_iters)
343 new_vuln_amounts = map(plus_one_or_minus_one, vuln_amount_of_those_hosts)
344 map(self._modify_vuln_amount_of_single_host_in_model, relevant_host_ids, new_vuln_amounts)
343 relevant_host_ids = list(filter(self._is_host_in_model_by_host_id, host_ids))
344 host_iters = [self.host_id_to_iter[h] for h in relevant_host_ids]
345 vuln_amount_of_those_hosts = [self._get_vuln_amount_from_model(h) for h in host_iters]
346 new_vuln_amounts = [plus_one_or_minus_one(h) for h in vuln_amount_of_those_hosts]
347 list(map(self._modify_vuln_amount_of_single_host_in_model, relevant_host_ids, new_vuln_amounts))
345348
346349 def add_relevant_vulns_to_model(self, vulns):
347350 """Takes vulns, a list of vulnerability object, and adds them to the
348351 model by modifying their corresponding hosts in the model. Return None.
349352 """
350 host_ids = [host_id for host_id in map(self._find_host_id, vulns) if host_id is not None]
353 host_ids = [host_id for host_id in [self._find_host_id(v) for v in vulns] if host_id is not None]
351354 self._modify_vuln_amounts_of_hosts_in_model(host_ids, lambda x: x + 1)
352355
353356 def remove_relevant_vulns_from_model(self, vulns_ids):
355358 the model by modifying their corresponding hosts in the model.
356359 Return None.
357360 """
358 host_ids = map(lambda v: v.getID().split(".")[0], vulns_ids)
361 host_ids = [v.getID().split(".")[0] for v in vulns_ids]
359362 self._modify_vuln_amounts_of_hosts_in_model(host_ids, lambda x: x - 1)
360363
361364 def add_host(self, host):
395398 object_type = obj.class_signature
396399 if object_type == 'Host':
397400 self.add_host_after_initial_load(host=obj)
398 if object_type == "Vulnerability" or object_type == "VulnerabilityWeb":
401 if object_type in ["Vulnerability", "VulnerabilityWeb"]:
399402 self.add_vuln(vuln=obj)
400403
401404 def remove_object(self, obj_id, obj_type):
926929 vuln_string = str(vuln_count) + " vulnerabilities."
927930
928931 return host_string, service_string, vuln_string
932
933
934 # I'm Py3
0 #!/usr/bin/python2.7
0 #!/usr/bin/python3
11 # -*- coding: utf-8 -*-
2 '''
2 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66
7 '''
7 """
8 from __future__ import absolute_import
89
910 import time
1011 import threading
1112
1213 from faraday.client.model.guiapi import notification_center
13 from decorators import safe_io_with_server
14 from faraday.client.gui.gtk.decorators import safe_io_with_server
1415 from faraday.client.persistence.server import models, server_io_exceptions
1516
1617
17 class ServerIO(object):
18 class ServerIO:
1819 def __init__(self, active_workspace):
1920 self.__active_workspace = active_workspace
2021 self.stream = None # will be set when active workpsace is set
132133 return False
133134 time.sleep(0.5)
134135
135 get_changes_thread = threading.Thread(target=get_changes)
136 get_changes_thread = threading.Thread(target=get_changes, name='get_changes')
136137 get_changes_thread.daemon = True
137138 get_changes_thread.start()
138139
155156 test_server_thread = threading.Thread(target=test_server_connection)
156157 test_server_thread.daemon = True
157158 test_server_thread.start()
159
160
161 # I'm Py3
00 #!/usr/bin/env python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
7 from __future__ import absolute_import
78
89 from faraday.client.managers.reports_managers import ReportManager
910
1112 CONF = getInstanceConfiguration()
1213
1314
14 class UiFactory(object):
15 class UiFactory:
1516 @staticmethod
1617 def create(model_controller, plugin_manager, workspace_manager, plugin_controller, gui="gtk"):
1718 if gui == "gtk":
18 from faraday.client.gui.gtk.application import GuiApp
19 from faraday.client.gui.gtk.application import GuiApp # pylint:disable=import-outside-toplevel
1920 else:
20 from faraday.client.gui.nogui.application import GuiApp
21 from faraday.client.gui.nogui.application import GuiApp # pylint:disable=import-outside-toplevel
2122
2223 return GuiApp(model_controller, plugin_manager, workspace_manager, plugin_controller)
2324
2425
25 class FaradayUi(object):
26 class FaradayUi:
2627 def __init__(self, model_controller, plugin_manager,
2728 workspace_manager, plugin_controller, gui="gtk"):
2829 self.model_controller = model_controller
8586 except Exception as e:
8687 raise e
8788 return ws
89
90
91 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 from __future__ import absolute_import
67
78 import logging
89 import threading
3839 self._widgets_lock.release()
3940 except:
4041 self.handleError(record)
42
43
44 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66
7 # I'm Py3
00 #!/usr/bin/env python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
7 from __future__ import absolute_import
78
89 import time
910
4041 "You may try and go to ~/.faraday/config/user.xml "
4142 "to set a valid api_uri and last_workspace")
4243 get_logger(self).error(str(e))
43 return -1
44 valid = False
45 for i in range(4):
46 workspace = raw_input("Please write the correct, Workspace): ")
47 try:
48 ws = super(GuiApp, self).openWorkspace(workspace)
49 valid = True
50 break
51 except Exception as err:
52 get_logger(self).error(
53 ("Your last workspace %s is not accessible, "
54 "check configuration.") % workspace)
55 get_logger(self).error(
56 "You may try and go to ~/.faraday/config/user.xml "
57 "to set a valid api_uri and last_workspace")
58 get_logger(self).error(str(err))
59 if not valid:
60 return -1
4461 workspace = ws.name
4562 CONF.setLastWorkspace(workspace)
4663 CONF.saveConfig()
5572
5673 def postEvent(self, receiver, event):
5774 receiver.update(event)
75
76
77 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 from __future__ import absolute_import
67
78 from faraday.server.utils.logger import get_logger
89 from faraday.client.gui.customevents import CHANGEFROMINSTANCE
910
1011
11 class EventWatcher(object):
12 class EventWatcher:
1213 def __init__(self):
1314 self.logger = get_logger(self)
1415
1617 if event.type() == CHANGEFROMINSTANCE:
1718 get_logger(self).info(
1819 "[Update Received] " + event.change.getMessage())
20
21
22 # I'm Py3
00 #!/usr/bin/env python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
7 from __future__ import absolute_import
8
79 import threading
810 from faraday.client.gui.gui_app import FaradayUi
911 import faraday.client.gui.customevents as events
9496
9597 def sendCustomLog(self, log_obj):
9698 self._notifyWidgets(events.LogCustomEvent(log_obj))
99
100
101 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11
2 '''
2 """
33 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
44 Author: Ezequiel Tavella
55 See the file 'doc/LICENSE' for the license information
77 This script generate a CSV file with information about the cfdb database.
88 CSV Format:
99 cwe,name,description,resolution,exploitation,references
10 '''
10 """
11 from __future__ import absolute_import
12 from __future__ import print_function
1113
1214 from subprocess import call
1315 from os import walk
8789 def main():
8890
8991 #Get DB cfdb
90 print '[*]Execute git clone...'
92 print('[*]Execute git clone...')
9193 return_code = call(['git', 'clone', URL_PROYECT])
9294
9395 if return_code != 0 and return_code != 128:
94 print '[!]Error:\n Git return code: ' + str(return_code)
96 print('[!]Error:\n Git return code: ' + str(return_code))
9597
9698 file_csv = open('cfdb.csv','w')
9799
108110 )
109111
110112 #Get DB names...
111 print '[*]Looking for DBs...'
113 print('[*]Looking for DBs...')
112114 for (root, dirs, files) in walk(DB_PATH):
113115
114116 #Jump dirs without info
115117 if root.find('.git') < 0 and root.find('.gitignore') < 0:
116118 if root != './cfdb/':
117119
118 print '[*]Parsing folder: ' + root
120 print('[*]Parsing folder: ' + root)
119121 for file_db in files:
120122
121 print '[_]File: ' + root + '/' + file_db
123 print('[_]File: ' + root + '/' + file_db)
122124 with open(root + '/' + file_db, 'r') as file_md:
123125
124126 csv_content = parseFile(file_md)
134136
135137 writer.writerow(result)
136138
137 print '[*]Parse folder finished...\n'
139 print('[*]Parse folder finished...\n')
138140
139 print '[*]All Finished... OK'
141 print('[*]All Finished... OK')
140142
141143 file_csv.close()
142144
143145 if __name__ == '__main__':
144146 main()
147 # I'm Py3
0 #!/usr/bin/env python2.7
1 '''
0 #!/usr/bin/env python3
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
66 This script fixes invalid XMLs.
7 '''
7 """
8 from __future__ import absolute_import
89
910 import argparse
1011 from bs4 import BeautifulSoup
3334
3435 if __name__ == "__main__":
3536 main()
37 # I'm Py3
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
77
8 '''
8 """
99
1010 """
1111 Create a report using the libs.reports package.
460460
461461 app = theexploit()
462462 ret = standard_callback_commandline(app)
463 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11
2 '''
2 """
33 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
44 Author: Ezequiel Tavella
55
66 This script generate a CSV file with information about the vulndb database.
77 CSV Format:
88 cwe,name,desc_summary,description,resolution,exploitation,references
9 '''
9 """
10 from __future__ import absolute_import
11 from __future__ import print_function
12
1013 from subprocess import call
1114 from os import walk, path
1215 import json
7578 def main():
7679
7780 #Get DB of vulndb
78 print '[*]Execute git clone...'
81 print('[*]Execute git clone...')
7982 return_code = call(['git', 'clone', URL_PROYECT])
8083
8184 if return_code != 0 and return_code != 128:
82 print '[!]Error:\n Git return code: ' + str(return_code)
85 print('[!]Error:\n Git return code: ' + str(return_code))
8386
8487 #Get DB names...
85 print '[*]Looking for DBs...'
88 print('[*]Looking for DBs...')
8689
8790 with open('vulndb.csv', mode='w') as file_csv:
8891 file_csv.write(
114117
115118 for file_db in vulndb_files:
116119
117 print '[*]Parsing ' + file_db
120 print('[*]Parsing ' + file_db)
118121 with open(path.join(vulndb_path, file_db), 'r') as file_object:
119122 csv_content = JsonToCsv(file_object)
120123 description = get_data_from_file(csv_content.description, desc_files)
130133
131134 writer.writerow(result)
132135
133 print '[*]Parse finished...'
134
136 print('[*]Parse finished...')
135137
136138 def parse_filenames(files):
137139 # Parse filenames from description or fix folders
161163
162164 return data
163165
164
165166 if __name__ == '__main__':
166167 main()
168
169 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66
77
8 # I'm Py3
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
77
8 '''
8 """
9 from __future__ import absolute_import
910
1011 import os
1112
1213 # TODO: remove this in next versions
1314
14 class ViewsManager(object):
15
16 class ViewsManager:
1517 """docstring for ViewsWrapper"""
1618 def __init__(self):
1719 self.vw = ViewsListObject()
4143 self.addView(v, workspaceDB)
4244
4345
44 class ViewsListObject(object):
46 class ViewsListObject:
4547 """ Representation of the FS Views """
4648 def __init__(self):
4749 self.views_path = os.path.join(os.getcwd(), "views")
4850 self.designs_path = os.path.join(self.views_path, "reports", "_attachments", "views")
4951
5052 def _listPath(self, path):
51 flist = filter(lambda x: not x.startswith('.'), os.listdir(path))
52 return map(lambda x: os.path.join(path, x), flist)
53 flist = list(filter(lambda x: not x.startswith('.'), os.listdir(path)))
54 return [os.path.join(path, x) for x in flist]
5355
5456 def get_fs_designs(self):
5557 return self._listPath(self.designs_path)
5658
5759 def get_all_views(self):
5860 return self.get_fs_designs()
61
62
63 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
4 '''
4 """
55 import logging
6
76 from faraday.client.persistence.server.models import create_object, get_object, update_object, delete_object
87
98 # NOTE: This class is intended to be instantiated by the
1413 logger = logging.getLogger(__name__)
1514
1615
17 class MapperManager(object):
16 class MapperManager:
1817 def __init__(self):
1918 # create and store the datamappers
2019 self.workspace_name = None
3635
3736 def find(self, class_signature, obj_id):
3837 if self.workspace_name is None:
39 logger.warn('No workspace detected. please call createMappers first.')
38 logger.warning('No workspace detected. please call createMappers first.')
4039 return get_object(self.workspace_name, class_signature, obj_id)
4140
4241 def remove(self, obj_id, class_signature):
43 return delete_object(self.workspace_name, class_signature, obj_id)
42 return delete_object(self.workspace_name, class_signature, obj_id)
43
44 # I'm Py3
22 Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44 """
5
65 import json
76 import os
87 import re
2928
3029 def __init__(self, plugin_controller):
3130
32 Thread.__init__(self)
31 Thread.__init__(self, name="OnlinePluginsThread")
3332 self.setDaemon(True)
34 self._stop = False
33 self._must_stop = False
3534
3635 self.online_plugins = {
3736 "MetasploitOn": {
5554 logger.debug("Running online plugin...")
5655
5756 def stop(self):
58 self._stop = True
57 self._must_stop = True
5958
6059 def run(self):
6160
62 while not self._stop:
63
64 for name, config_dict in self.online_plugins.iteritems():
61 while not self._must_stop:
62
63 for name, config_dict in self.online_plugins.items():
6564 if name in self.plugins_settings:
6665 if self.plugins_settings[name]['settings']['Enable'] == "1":
6766
8786 def processReport(self, filename):
8887 """ Process one Report """
8988 logger.debug("Report file is %s" % filename)
90
91 parser = ReportParser(filename)
92
93 if parser.report_type is None:
94
95 logger.error(
96 'Plugin not found: automatic and manual try!')
97 return False
98
99 return self.sendReport(parser.report_type, filename)
89 report_analyzer = ReportAnalyzer(self.plugin_controller, filename)
90 plugin_id = report_analyzer.get_plugin_id()
91 if not plugin_id:
92 logger.error('Plugin not found: automatic and manual try!')
93 return None
94 return self.sendReport(plugin_id, filename)
10095
10196 def sendReport(self, plugin_id, filename):
10297 """Sends a report to the appropiate plugin specified by plugin_id"""
103
104 logger.info(
105 'The file is %s, %s' % (filename, plugin_id))
106
107 command_id = self.plugin_controller.processReport(
108 plugin_id, filename, ws_name=self.ws_name)
109
98 logger.info('The file is %s, %s', filename, plugin_id)
99 command_id = self.plugin_controller.processReport(plugin_id, filename, ws_name=self.ws_name)
110100 if not command_id:
111
112 logger.error(
113 "Faraday doesn't have a plugin for this tool... Processing: ABORT")
114 return False
115
101 logger.error("Faraday doesn't have a plugin for this tool... Processing: ABORT")
102 return None
116103 return command_id
117104
118105
119106 class ReportManager(Thread):
120107
121108 def __init__(self, timer, ws_name, plugin_controller, polling=True):
122
123109 Thread.__init__(self)
124110 self.setDaemon(True)
125
126111 self.polling = polling
127112 self.ws_name = ws_name
128113 self.timer = timer
129 self._stop = False
130
114 self._must_stop = False
131115 self._report_path = os.path.join(CONF.getReportPath(), ws_name)
132116 self._report_ppath = os.path.join(self._report_path, "process")
133117 self._report_upath = os.path.join(self._report_path, "unprocessed")
134
135118 self.processor = ReportProcessor(plugin_controller, ws_name)
136119 self.online_plugins = OnlinePlugins(plugin_controller)
137
138
139120 if not os.path.exists(self._report_path):
140121 os.mkdir(self._report_path)
141
142122 if not os.path.exists(self._report_ppath):
143123 os.mkdir(self._report_ppath)
144
145124 if not os.path.exists(self._report_upath):
146125 os.mkdir(self._report_upath)
147126
148127 def run(self):
149
150128 self.online_plugins.start()
151129 tmp_timer = .0
152
153 while not self._stop:
154
130 while not self._must_stop:
155131 time.sleep(.1)
156132 tmp_timer += .1
157
158133 if tmp_timer >= self.timer:
159
160134 try:
161135 self.syncReports()
162136 if not self.polling:
163137 break
164
165138 except Exception:
166
167 logger.error(
168 "An exception was captured while saving reports\n%s"
169 % traceback.format_exc())
170
139 logger.error("An exception was captured while saving reports\n%s", traceback.format_exc())
171140 finally:
172141 tmp_timer = 0
173142
174143 def stop(self):
175 self._stop = True
144 self._must_stop = True
176145 self.online_plugins.stop()
177146
178147 def syncReports(self):
181150 We first make sure that all shared reports were added to the repo
182151 """
183152 for root, dirs, files in os.walk(self._report_path, False):
184
185153 # skip processed and unprocessed directories
186154 if root == self._report_path:
187155 for name in files:
188
189156 filename = os.path.join(root, name)
190157 name = os.path.basename(filename)
191
192158 # If plugin not is detected... move to unprocessed
193159 # PluginCommiter will rename the file to processed or unprocessed
194160 # when the plugin finishes
195161 if self.processor.processReport(filename) is False:
196
197 logger.info(
198 'Plugin not detected. Moving {0} to unprocessed'.format(filename))
199
200 os.rename(
201 filename,
202 os.path.join(self._report_upath, name))
162 logger.info('Plugin not detected. Moving {0} to unprocessed'.format(filename))
163 os.rename(filename, os.path.join(self._report_upath, name))
203164 else:
204
205 logger.info(
206 'Detected valid report {0}'.format(filename))
207
208 os.rename(
209 filename,
210 os.path.join(self._report_ppath, name))
165 logger.info("Detected valid report {%s}", filename)
166 os.rename(filename, os.path.join(self._report_ppath, name))
211167
212168 def sendReportToPluginById(self, plugin_id, filename):
213169 """Sends a report to be processed by the specified plugin_id"""
214170 self.processor.sendReport(plugin_id, filename)
215171
216172
217 class ReportParser(object):
218 """
219 Class that handles reports files.
220 :param filepath: report file.
221 :class:`.LoadReport`
222 """
223
224 def __init__(self, report_path):
225 self.report_type = None
226 root_tag, output = self.getRootTag(report_path)
227
228 if root_tag:
229 self.report_type = self.rType(root_tag, output)
230
231 if self.report_type is None:
232
233 logger.debug(
234 'Automatical detection FAILED... Trying manual...')
235
236 self.report_type = self.getUserPluginName(report_path)
237
238 def getUserPluginName(self, pathFile):
239
240 if pathFile == None:
173 class ReportAnalyzer:
174
175 def __init__(self, plugin_controller, report_path):
176 self.plugin_controller = plugin_controller
177 self.report_path = report_path
178
179 def get_plugin_id(self):
180 if not os.path.isfile(self.report_path):
181 logger.error("Report [%s] don't exists", self.report_path)
241182 return None
242
243 rname = pathFile[pathFile.rfind('/') + 1:]
244 ext = rname.rfind('.')
245 if ext < 0:
246 ext = len(rname) + 1
247 rname = rname[0:ext]
248 faraday_index = rname.rfind('_faraday_')
249 if faraday_index > -1:
250 plugin = rname[faraday_index + 9:]
251 return plugin
252
253 return None
254
255 def open_file(self, file_path):
256 """
257 This method uses file signatures to recognize file types
258
259 :param file_path: report file.
260
261 If you need add support to a new report type
262 add the file signature here
263 and add the code in self.getRootTag() for get the root tag.
264 """
265 f = result = None
266
267 signatures = {
268 "\x50\x4B": "zip",
269 "\x3C\x3F\x78\x6D\x6C": "xml",
270 "# Lynis Re": "dat",
271 }
272
183 else:
184 file_name = os.path.basename(self.report_path)
185 plugin_id = self._get_plugin_by_name(file_name)
186 if not plugin_id: # Was unable to detect plugin from report file name
187 logger.debug("Plugin by name not found")
188 plugin_id = self._get_plugin_by_file_type(self.report_path)
189 if not plugin_id:
190 logger.debug("Plugin by file not found")
191 return plugin_id
192
193 def _get_plugin_by_file_type(self, report_path):
194 plugin_id = None
195 file_name = os.path.basename(self.report_path)
196 file_name_base, file_extension = os.path.splitext(file_name)
197 file_extension = file_extension.lower()
198 main_tag = None
199 logger.debug("Analyze report File")
200 # Try to parse as xml
273201 try:
274
275 if file_path == None:
276 return None, None
277
278 f = open(file_path, 'rb')
279 file_signature = f.read(10)
280
281 for key in signatures:
282 if file_signature.find(key) == 0:
283
284 result = signatures[key]
202 report_file = open(report_path)
203 except Exception as e:
204 logger.error("Error reading report content [%s]", e)
205 else:
206 try:
207 for event, elem in ET.iterparse(report_file, ('start',)):
208 main_tag = elem.tag
285209 break
286
287 if not result:
288 # try json loads to detect a json file.
289 try:
290 f.seek(0)
291 json.loads(f.read())
292 result = 'json'
293 except ValueError:
294 pass
295
296 except IOError as err:
297 self.report_type = None
298 logger.error(
299 "Error while opening file.\n%s. %s" % (err, file_path))
300
301 logger.debug("Report type detected: %s" % result)
302 f.seek(0)
303 return f, result
304
305 def getRootTag(self, file_path):
306
307 report_type = result = f = None
308
309 f, report_type = self.open_file(file_path)
310 if file_path.endswith('fpr'):
311 report_type = 'fpr'
312
313 # Check error in open_file()
314 if f is None and report_type is None:
315 self.report_type = None
316 return None, None
317
318 # Find root tag based in report_type
319 if report_type == "zip":
320 result = "maltego"
321 elif report_type == "dat":
322 result = 'lynis'
323 elif report_type == 'json':
324 # this will work since recon-ng is the first plugin to use json.
325 # we need to add json detection here!
326 result = 'reconng'
327 elif report_type == 'fpr':
328 result = 'fortify'
210 logger.debug("Found XML content on file: %s - Main tag: %s", report_path, main_tag)
211 except Exception as e:
212 logger.info("Non XML content [%s] - %s", report_path, e)
213 finally:
214 report_file.close()
215 for _plugin_id, _plugin in self.plugin_controller.getAvailablePlugins().items():
216 if _plugin.report_belongs_to(main_tag=main_tag, report_path=report_path, extension=file_extension):
217 plugin_id = _plugin_id
218 break
219 return plugin_id
220
221 def _get_plugin_by_name(self, file_name_base):
222 plugin_id = None
223 plugin_name_regex = r".*_faraday_(?P<plugin_name>.+)\..*$"
224 match = re.match(plugin_name_regex, file_name_base)
225 if match:
226 plugin_id = match.groupdict()['plugin_name'].lower()
227 logger.debug("Plugin name match: %s", plugin_id)
228 if plugin_id in self.plugin_controller.getAvailablePlugins():
229 return plugin_id
230 else:
231 logger.info("Invalid plugin from file name: %s", plugin_id)
232 return None
329233 else:
330
331 try:
332 for event, elem in ET.iterparse(f, ('start', )):
333 result = elem.tag
334 break
335
336 except SyntaxError as err:
337 self.report_type = None
338 logger.error("Not an xml file.\n %s" % (err))
339
340 f.seek(0)
341 output = f.read()
342 if f:
343 f.close()
344
345 return result, output
346
347 def rType(self, tag, output):
348 """ Compares report root tag with known root tags """
349 if tag == "nmaprun":
350 return "Nmap"
351 elif tag == "w3af-run":
352 return "W3af"
353 elif tag == "NessusClientData_v2":
354 return "Nessus"
355 elif tag == "report":
356
357 if re.search(
358 "https://raw.githubusercontent.com/Arachni/arachni/", output) is not None:
359 return "Arachni"
360
361 elif re.search("OpenVAS", output) is not None or re.search('<omp><version>', output) is not None:
362 return "Openvas"
363
364 else:
365 return "Zap"
366
367 elif tag == "xml-report":
368 if re.search("Appscan", output) is not None:
369 return "Appscan"
370 elif tag == "niktoscan":
371 return "Nikto"
372 elif tag == "MetasploitV4":
373 return "Metasploit"
374 elif tag == "MetasploitV5":
375 return "Metasploit"
376 elif tag == "issues":
377 return "Burp"
378 elif tag == "OWASPZAPReport":
379 return "Zap"
380 elif tag == "ScanGroup":
381 return "Acunetix"
382 elif tag == "session":
383 return "X1"
384 elif tag == "landscapePolicy":
385 return "X1"
386 elif tag == "entities":
387 return "Core Impact"
388 elif tag == "NexposeReport":
389 return "NexposeFull"
390 elif tag in ("ASSET_DATA_REPORT", "SCAN"):
391 return "Qualysguard"
392 elif tag == "scanJob":
393 return "Retina"
394 elif tag == "netsparker":
395 return "Netsparker"
396 elif tag == "netsparker-cloud":
397 return "NetsparkerCloud"
398 elif tag == "maltego":
399 return "Maltego"
400 elif tag == "lynis":
401 return "Lynis"
402 elif tag == "reconng":
403 return "Reconng"
404 elif tag == "fortify":
405 return "Fortify"
406 elif tag == "document":
407 if re.search("SSLyzeVersion", output) is not None:
408 return "Sslyze"
409 else:
410 return None
234 logger.debug("Could not extract plugin_id from filename: %s", file_name_base)
235 return plugin_id
236
237 # I'm Py3
0 # -*- coding: utf-8 -*-
1
2 '''
0 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
64
7 '''
5 """
86 import re
97 import time
108
2220 pass
2321
2422
25 class WorkspaceManager(object):
23 class WorkspaceManager:
2624 """
2725 This class is in charge of creating, deleting and opening workspaces
2826 """
6260 WorkspaceException if something went wrong along the way.
6361 """
6462 if name not in get_workspaces_names():
65 raise WorkspaceException(
66 "Workspace %s wasn't found" % name)
63 raise WorkspaceException("Workspace %s wasn't found" % name)
6764
6865 try:
6966 workspace = get_workspace(name)
110107 return True
111108 else:
112109 return False
110
111
112 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66
77 class Modelactions:
88 ADDHOST = 2000
7575
7676 @staticmethod
7777 def getDescription(action):
78 return modelactions.__descriptions.get(action, "")
78 return modelactions.__descriptions.get(action, "")# I'm Py3
00 #!/usr/bin/env python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
7 from __future__ import absolute_import
78
89 import os
910 import shutil
523524
524525 def getActiveWorkspace():
525526 return __workspace_manager.getActiveWorkspace()
527
528
529 # I'm Py3
00 #!/usr/bin/env python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
7 from __future__ import absolute_import
8 from __future__ import print_function
9
710 import os
811 import sys
912 import signal
4144 logger = logging.getLogger(__name__)
4245
4346
44 class MainApplication(object):
47 class MainApplication:
4548
4649 def __init__(self, args):
4750 self._original_excepthook = sys.excepthook
181184 def ctrlC(self, signal, frame):
182185 logger.info("Exiting...")
183186 self.app.quit()
187
188
189 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 from __future__ import absolute_import
7
68 import logging
79
810 from faraday.client.managers.reports_managers import ReportProcessor
2729
2830 rp = ReportProcessor(self.plugin_controller)
2931 rp.processReport(args.filename)
32
33
34 # I'm Py3
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
77
8 '''
8 """
9 from __future__ import absolute_import
10
911 import socket
1012 import subprocess
1113 import getpass
3234 if ip:
3335 if not ip.startswith('127'):
3436 return ip
35 if _platform == "linux" or _platform == "linux2": # linux
37 if _platform in ["linux", "linux2"]: # linux
3638 ip = subprocess.check_output(["ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d'/'"], shell=True)
3739 elif _platform == "darwin": # MAC OS X
3840 ip = subprocess.check_output(["ifconfig | grep 'inet ' | grep -Fv 127.0.0.1 | awk '{print $2}' "], shell=True)
4749 return getpass.getuser()
4850
4951
50 class CommandRunInformation(object):
52 class CommandRunInformation:
5153 """Command Run information object containing:
5254 command, parameters, time, workspace, etc."""
5355 class_signature = "CommandRunInformation"
8486 for k, v in dictt.items():
8587 setattr(self, k, v)
8688 return self
89
90
91 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 from __future__ import absolute_import
7
68 import sys
79 import traceback
810 import threading
911 import logging
1012 try:
1113 import xmlrpclib
12 import SimpleXMLRPCServer
14 from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
1315 except ImportError:
1416 from xmlrpc import client as xmlrpclib
15 from xmlrpc.server import SimpleXMLRPCServer
17 from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
1618
1719 try:
18 import faraday.client.model.api as api
19 except AttributeError:
20 from faraday.client.model import api
21 except AttributeError as e:
2022 import api
2123
2224 from faraday.config.configuration import getInstanceConfiguration
2729 # -------------------------------------------------------------------------------
2830 # TODO: refactor this class to make it generic so this can be used also for plugins
2931 # then create a subclass and inherit the generic factory
30 class ModelObjectFactory(object):
32 class ModelObjectFactory:
3133 """
3234 Factory to creat any ModelObject type
3335 """
4042
4143 def listModelObjectClasses(self):
4244 """returns a list of registered classes"""
43 return self._registered_objects.values()
45 return list(self._registered_objects.values())
4446
4547 def getModelObjectClass(self, name):
4648 """get the class for a particular object typename"""
4850
4951 def listModelObjectTypes(self):
5052 """returns an array with object typenames the factory is able to create"""
51 names = self._registered_objects.keys()
53 names = list(self._registered_objects.keys())
5254 names.sort()
5355 return names
5456
118120
119121 # -------------------------------------------------------------------------------
120122
121 class CustomXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
123 class CustomXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
122124
123125 def __init__(self, *args, **kwargs):
124 SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.__init__(self, *args, **kwargs)
126 SimpleXMLRPCRequestHandler.__init__(self, *args, **kwargs)
125127
126128 def handle(self):
127129 try:
130132 api.devlog("[XMLRPCHandler] - client_address = %s" % str(self.client_address))
131133 api.devlog("[XMLRPCHandler] - server = %s" % str(self.server))
132134 api.devlog("-" * 60)
133 SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.handle(self)
135 SimpleXMLRPCRequestHandler.handle(self)
134136 except Exception:
135137 api.devlog("[XMLRPCHandler] - An error ocurred while handling a request\n%s" % traceback.format_exc())
136138
202204 # http://epydoc.sourceforge.net/stdlib/BaseHTTPServer.BaseHTTPRequestHandler-class.html#address_string
203205 #
204206
205 class XMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, threading.Thread):
207 class XMLRPCServer(SimpleXMLRPCServer, threading.Thread):
206208 """
207209 Stoppable XMLRPC Server with custom dispatch to send over complete traceback
208210 in case of exception.
209211 """
210212 def __init__(self, *args, **kwargs):
211213 threading.Thread.__init__(self)
212 SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self,
214 SimpleXMLRPCServer.__init__(self,
213215 requestHandler=CustomXMLRPCRequestHandler,
214216 allow_none=True, *args, **kwargs)
215 self._stop = False
217 self._must_stop = False
216218 # set timeout for handle_request. If we don't the server will hang
217219 self.timeout = 2
218220
219221 def run(self):
220222 self.serve_forever()
221223 api.devlog("serve_forever ended")
222 return
223224
224225 # overloaded method to be able to stop server
225226 def serve_forever(self):
226 while not self._stop:
227 while not self._must_stop:
227228 self.handle_request()
228229 api.devlog("server forever stopped by flag")
229230
230231 def stop_server(self):
231232 api.devlog("server stopping...")
232 self._stop = True
233 self._must_stop = True
233234
234235 # The default dispatcher does not send across the whole stack trace.
235236 # Only type and value are passed back. The client has no way of knowing
330331
331332 return response
332333
333 class XMLRPCKeywordProxy(object):
334 class XMLRPCKeywordProxy:
334335 """
335336 custom XMLRPC Server Proxy capable of receiving keyword arguments
336337 when calling remote methods
344345 def _call(*args, **kwargs):
345346 return call_proxy(args, kwargs)
346347 return _call
348
349
350 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66
77
88 class Conflict():
3939 self.first_object.updateAttributes(**kwargs)
4040 self.first_object.updateResolved(self)
4141 return True
42 # I'm Py3
00 #!/usr/bin/python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
7 from __future__ import absolute_import
78
89
910 class ModelObjectContainer(dict):
2223 def __getattr__( self, name):
2324 return getattr(self.container, name)
2425
25 def itervalues(self):
26 return self.container.itervalues()
27
2826 def values(self):
29 return self.container.values()
27 return list(self.container.values())
3028
3129 def keys(self):
32 return self.container.keys()
30 return list(self.container.keys())
3331
3432 def __str__(self):
3533 return str(self.container)
6361 ModelObjectContainer.__setitem__(self, k, v)
6462
6563
64
65
66 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 from __future__ import absolute_import
7
68 import time
79 import logging
810 import traceback
911 import faraday.client.model.common # this is to make sure the factory is created
1012 from multiprocessing import Lock
11 from Queue import Empty
13 from queue import Empty
1214 from threading import Thread
1315
1416 from faraday.config.configuration import getInstanceConfiguration
3032 class ModelController(Thread):
3133
3234 def __init__(self, mappers_manager, pending_actions):
33 Thread.__init__(self)
35 #Thread.__init__(self)
36 super().__init__(name="ModelControllerThread")
3437
3538 self.mappers_manager = mappers_manager
3639
3740 # set as daemon
3841 # self.setDaemon(True)
39
40 # flag to stop daemon thread
41 self._stop = False
42 # sets the flag to stop the thread when it has finished processing
43 self._must_stop = False
44
4245 # locks needed to make model thread-safe
4346 self._hosts_lock = Lock()
4447
170173 """
171174 Sets the flag to stop daemon
172175 """
173 self._stop = True
176 self._must_stop = True
174177
175178 def _dispatchActionWithLock(self, action_callback, *args):
176179 res = False
256259 This will make host addition and removal "thread-safe" and will
257260 avoid locking components that need to interact with the model
258261 """
259
260 while not self._stop or self.processing:
262 while not self._must_stop or self.processing:
261263 # check if thread must finish
262264 # no plugin should be active to stop the controller
263 if self._stop and self.active_plugins_count == 0:
265 if self._must_stop and self.active_plugins_count == 0:
264266 break
265267 # first we check if there is a sync api request
266268 # or if the model is being saved/sync'ed
548550 "Couldn't get vulnerabilities count: assuming it is zero.")
549551 count = 0
550552 return count
553
554
555 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66
77
8 class ModelObjectDiff(object):
8 class ModelObjectDiff:
99 def __init__(self, objLeft, objRight):
1010 try:
1111 if not getattr(objLeft, 'class_signature') == getattr(objRight, 'class_signature'):
5555 # return only_in_obj1, only_in_obj2
5656
5757
58 class MergeStrategy(object):
58 class MergeStrategy:
5959 @staticmethod
6060 def solve(old, new):
6161 raise NotImplementedError("This is an abstract class")
7373 return old
7474
7575
76 class MergeSolver(object):
76 class MergeSolver:
7777 def __init__(self, strategy):
7878 if strategy == "new":
7979 self.strategy = MergeKeepNew
8484
8585 def solve(self, old, new):
8686 return self.strategy.solve(old, new)
87 # I'm Py3
00 #!/usr/bin/env python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
7 from __future__ import absolute_import
78
89 import faraday.client.model.common
910 from faraday.client.gui.notifier import NotificationCenter
462463 if obj is not None:
463464 notification_center.editHostFromChanges(obj)
464465 return True
465 return False
466 return False
467
468 # I'm Py3
00 #!/usr/bin/env python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
7 from __future__ import absolute_import
8
79 import logging
810 from faraday.client.gui.customevents import (ShowPopupCustomEvent,
911 ShowDialogCustomEvent)
2527 return Notifier()
2628
2729
28 class Notifier(object):
30 class Notifier:
2931 """
3032 This class is used to show information to the user using dialog boxes or
3133 little pop ups (like tooltips).
4850
4951 def showPopup(self, text, level="Information"):
5052 self._postCustomEvent(text, level, ShowPopupCustomEvent)
53
54
55 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 from __future__ import absolute_import
7
68 import datetime
79 import hashlib
810
911
10 class Session(object):
12 class Session:
1113 """
1214 It will handle a Faraday session, that contains:
1315 - current user logged in
2830
2931 def get_token():
3032 return self.__token
33
34
35 # I'm Py3
00 #!/usr/bin/env python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
77
8 class Branch(object):
8 class Branch:
99 pass
1010
11 class BranchItem(object):
11 class BranchItem:
1212 pass
1313
1414
15 # I'm Py3
00 #!/usr/bin/env python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
77
8 class View(object):
8 class View:
99 """A view for the data in a CouchDB"""
1010 def __init__(self):
1111 pass
1313
1414 class HostsServiceFrequencies(View):
1515 pass
16 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 """
77 Contains base classes used to represent the application model
88 and some other common objects and functions used in the model
99 """
1010
1111
12 class ModelObjectVisitor(object):
12 class ModelObjectVisitor:
1313 def visit(self, modelObjectInstance):
1414 raise NotImplemented('Abstract method')
1515
3131 parent = parent.getParent()
3232
3333 self.parents.append(parents)
34 # I'm Py3
00 #!/usr/bin/env python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
7 from __future__ import absolute_import
78
89 import time
910
1112 CONF = getInstanceConfiguration()
1213
1314
14 class Workspace(object):
15 class Workspace:
1516 """
1617 Handles a complete workspace (or project)
1718 It contains a reference to the model and the command execution
7475 return self.name == self._workspace_manager.getActiveWorkspace().name
7576
7677 def getHosts(self):
77 return self.hosts.values()
78 return list(self.hosts.values())
7879
7980 def setHosts(self, hosts):
8081 self.hosts = hosts
8182
83
84
85 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 __all__ = []
7 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66
7 # I'm Py3
0 #!/usr/bin/python2.7
0 #!/usr/bin/python3
11 # -*- coding: utf-8 -*-
2 '''
2 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66
7 '''
7 """
8 from __future__ import absolute_import
9 from __future__ import print_function
10 from past.builtins import basestring
11
812 import json
913 import logging
1014 import threading
11 try:
12 from Queue import Queue, Empty
13 except ImportError:
14 from queue import Queue, Empty
15 from queue import Queue, Empty
1516 import requests
1617 import websocket
1718
2122 logger = logging.getLogger(__name__)
2223
2324
24 class ChangesStream(object):
25 class ChangesStream:
2526
2627 def __enter__(self):
2728 return self
8687 # ws.run_forever will call on_message, on_error, on_close and on_open
8788 # see websocket client python docs on:
8889 # https://github.com/websocket-client/websocket-client
89 thread = threading.Thread(target=self.ws.run_forever, args=())
90 thread = threading.Thread(target=self.ws.run_forever, args=(),
91 name='WebsocketsChangesStream')
9092 thread.daemon = True
9193 thread.start()
9294
9597 super(WebsocketsChangesStream, self).stop()
9698
9799 def on_open(self):
98 from faraday.client.persistence.server.server import _create_server_api_url, _post
100 from faraday.client.persistence.server.server import _create_server_api_url, _post # pylint:disable=import-outside-toplevel
101
99102 response = _post(
100103 _create_server_api_url() +
101104 '/ws/{}/websocket_token/'.format(self.workspace_name),
111114 logger.debug('New message {0}'.format(message))
112115 self.changes_queue.put(message)
113116
114 def on_error(self, ws, error):
117 def on_error(ws, error):
118 pass
115119 print(error)
116120
117121 def on_close(self):
130134 try:
131135 data = json.loads(self.changes_queue.get_nowait())
132136 except Empty:
133 raise StopIteration
137 return
134138 yield data
135139
136140 def _get_object_type_and_name_from_change(self, change):
163167 for raw_line in self._response.iter_lines():
164168 line = self._sanitize(raw_line)
165169 if not line:
166 if self._stop: break
167 else: continue
170 if not self._stop:
171 continue
168172 change = self._parse_change(line)
169173 if not change:
170174 continue
178182 raise ChangesStreamStoppedAbruptly
179183 except Exception as e:
180184 self.stop()
185
186
187 # I'm Py3
127127 <div itemprop="articleBody">
128128
129129 <h1>Source code for persistence.server.server</h1><div class="highlight"><pre>
130 <span></span><span class="ch">#!/usr/bin/python2.7</span>
130 <span></span><span class="ch">#!/usr/bin/python3</span>
131131 <span class="c1"># -*- coding: utf-8 -*-</span>
132132
133133 <span class="c1"># Faraday Penetration Test IDE</span>
127127 <div itemprop="articleBody">
128128
129129 <h1>Source code for server</h1><div class="highlight"><pre>
130 <span></span><span class="ch">#!/usr/bin/python2.7</span>
130 <span></span><span class="ch">#!/usr/bin/python3</span>
131131 <span class="c1"># -*- coding: utf-8 -*-</span>
132132 <span class="sd">&#39;&#39;&#39;</span>
133133 <span class="sd">Faraday Penetration Test IDE</span>
1616 # add these directories to sys.path here. If the directory is relative to the
1717 # documentation root, use os.path.abspath to make it absolute, like shown here.
1818 #
19 from __future__ import absolute_import
20 from __future__ import print_function
1921 import os
2022 import sys
21 print os.path.abspath('..')
23
24 print(os.path.abspath('..'))
2225 sys.path.insert(0, os.path.abspath('..'))
2326 sys.path.insert(0, os.path.abspath('../../..'))
2427
348351 # If true, do not generate a @detailmenu in the "Top" node's menu.
349352 #
350353 # texinfo_no_detailmenu = False
354 # I'm Py3
0 #!/usr/bin/python2.7
0 #!/usr/bin/python3
11 # -*- coding: utf-8 -*-
2 '''
2 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66
7 '''
7 """
8 from __future__ import absolute_import
9
810 import logging
911 from time import time
1012 import traceback
3739
3840 def _conf():
3941 if FARADAY_UP:
40 from faraday.config.configuration import getInstanceConfiguration
42 from faraday.config.configuration import getInstanceConfiguration # pylint:disable=import-outside-toplevel
4143 return getInstanceConfiguration()
4244 else:
4345 raise CantAccessConfigurationWithoutTheClient
673675
674676 def get_workspaces_names():
675677 """Return a list with all the workspace names available."""
676 active_workspaces = filter(lambda ws: ws['active'], server.get_workspaces_names())
677 return map(lambda ws: ws['name'], active_workspaces)
678 active_workspaces = list(filter(lambda ws: ws['active'], server.get_workspaces_names()))
679 return [ws['name'] for ws in active_workspaces]
678680
679681
680682 def server_info():
692694 # I think there are several # discrepancies between the models here,
693695 # those on the server and the parameters the apis specify,
694696 # and this leads to potential dissaster. Remember params?
695 class ModelBase(object):
697 class ModelBase:
696698 """A model for all the Faraday Objects.
697699 There should be a one to one correspondance with the jsons the faraday
698700 server gives through apis and the classes inheriting from this one.
942944 self.ports = [service['ports']]
943945 else:
944946 # plugin creates a list of strings with the ports
945 self.ports = map(int, service['ports'])
947 self.ports = list(map(int, service['ports']))
946948 self.version = service['version']
947949 self.status = service['status']
948950 self.vuln_amount = int(service.get('vulns', 0))
10641066 return True
10651067
10661068 if key == "status":
1067 if prop1 == "closed" or prop1 == "re-opened":
1069 if prop1 in ["closed", "re-opened"]:
10681070 return "re-opened"
10691071 if prop1 == "risk-accepted":
10701072 return 'risk-accepted'
13151317 return self._resolve_response(prop1, prop2)
13161318
13171319 if key == "status":
1318 if prop1 == "closed" or prop1 == "re-opened":
1320 if prop1 in ["closed", "re-opened"]:
13191321 return "re-opened"
13201322 if prop1 == "risk-accepted":
13211323 return 'risk-accepted'
14731475 return self.end_date
14741476
14751477
1476 class MetadataUpdateActions(object):
1478 class MetadataUpdateActions:
14771479 """Constants for the actions made on the update"""
14781480 UNDEFINED = -1
14791481 CREATE = 0
14801482 UPDATE = 1
14811483
14821484
1483 class Metadata(object):
1485 class Metadata:
14841486 """To save information about the modification of ModelObjects.
14851487 All members declared public as this is only a wrapper"""
14861488
15231525 if controller_funcallnames:
15241526 return "ModelControler." + " ModelControler.".join(controller_funcallnames)
15251527 return "No model controller call"
1528
1529
1530 # I'm Py3
0 #!/usr/bin/python2.7
0 #!/usr/bin/python3
11 # -*- coding: utf-8 -*-
22
33 # Faraday Penetration Test IDE
2121 be used with care, specially regarding the ID of objects, which must
2222 be always unique.
2323 """
24 from __future__ import absolute_import
25
2426 import urllib
2527
2628 import os
7476
7577
7678 def _conf():
77 from faraday.config.configuration import getInstanceConfiguration
79 from faraday.config.configuration import getInstanceConfiguration # pylint:disable=import-outside-toplevel
7880 CONF = getInstanceConfiguration()
7981
8082 # If you are running this libs outside of Faraday, cookies are not setted.
9799 server_url = _conf().getAPIUrl()
98100 else:
99101 server_url = SERVER_URL
100
101102 return server_url.rstrip('/')
102103
103104
195196 Return the response from the server.
196197 """
197198 answer = None
198 #logger.debug('Sending request to api endpoint {0}'.format(server_url))
199199 try:
200200 answer = server_io_function(server_url, **payload)
201201 if answer.status_code == 409:
588588 A dictionary with the object's information.
589589 """
590590 get_url = _create_couch_get_url(workspace_name, object_id)
591
591592 response = _unsafe_io_with_server(requests.get, [200], get_url,
592593 params={'revs': 'true', 'open_revs': 'all'})
593594 try:
15811582 return False
15821583 except requests.adapters.ReadTimeout:
15831584 return False
1585
1586
1587 # I'm Py3
0 #!/usr/bin/python2.7
0 #!/usr/bin/python3
11 # -*- coding: utf-8 -*-
2 '''
2 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66
7 '''
7 """
88 class ServerRequestException(Exception):
99 def __init__(self):
1010 pass
8686 def __str__(self):
8787 return ("You're tring to access to the Faraday Configuration without "
8888 "having the client up. This is not possible at the moment.")
89 # I'm Py3
0 #!/usr/bin/python2.7
0 #!/usr/bin/python3
11 # -*- coding: utf-8 -*-
2 '''
2 """
33 Faraday Penetration Test IDE
44 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
55 See the file 'doc/LICENSE' for the license information
66
7 '''
7 """
8 from __future__ import absolute_import
9
810 import re
911 import logging
1012 import socket
162164 'params': command.params,
163165 'import_source': command.import_source,
164166 }
167
168
169 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
77
8 '''
8 """
9 from past.builtins import basestring
10 from builtins import range
11
912 import os
1013 import time
1114 import shlex
3437 class PluginCommiter(Thread):
3538
3639 def __init__(self, output_queue, output, pending_actions, plugin, command, mapper_manager, end_event=None):
37 super(PluginCommiter, self).__init__()
40 super(PluginCommiter, self).__init__(name="PluginCommiterThread")
3841 self.output_queue = output_queue
3942 self.pending_actions = pending_actions
4043 self.stop = False
5154 self.stop = True
5255
5356 def commit(self):
54 logger.debug('Plugin end. Commiting to faraday server.')
57 logger.info('Plugin end. Commiting to faraday server.')
5558 self.pending_actions.put(
5659 (Modelactions.PLUGINEND, self.plugin.id, self.command.getID()))
5760 self.command.duration = time.time() - self.command.itime
6467 try:
6568 self.output_queue.join()
6669 self.commit()
67 if '\0' not in self.output and os.path.isfile(self.output):
70 if b'\0' not in self.output and os.path.isfile(self.output):
6871 # sometimes output is a filepath
6972 name = os.path.basename(self.output)
7073 os.rename(self.output,
7174 os.path.join(self._report_ppath, name))
7275 except Exception as ex:
7376 logger.exception(ex)
74 logger.info('Something failed, moving file to unprocessed')
75 os.rename(self.output,
76 os.path.join(self._report_upath, name))
77 logger.warning('Something failed, moving file to unprocessed')
78 os.rename(self.output, os.path.join(self._report_upath, name))
7779
7880
7981
8284 TODO: Doc string.
8385 """
8486 def __init__(self, id, plugin_manager, mapper_manager, pending_actions, end_event=None):
85 super(PluginController, self).__init__()
87 super(PluginController, self).__init__(name="PluginControllerThread")
8688 self.plugin_manager = plugin_manager
8789 self._plugins = plugin_manager.getPlugins()
8890 self.id = id
108110 block list defined by the user. Returns False if the modified
109111 command is ok, True if otherwise.
110112 """
111 block_chars = set(["|", "$", "#"])
113 block_chars = {"|", "$", "#"}
112114
113115 if original_command == modified_command:
114116 return False
121123
122124 block_flag = False
123125 orig_args_len = len(orig_cmd_args)
124 for index in xrange(0, len(mod_cmd_args)):
126 for index in range(0, len(mod_cmd_args)):
125127 if (index < orig_args_len and
126128 orig_cmd_args[index] == mod_cmd_args[index]):
127129 continue
134136 return block_flag
135137
136138 def _get_plugins_by_input(self, cmd, plugin_set):
137 for plugin in plugin_set.itervalues():
139 for plugin in plugin_set.values():
140 if isinstance(cmd, bytes):
141 cmd = cmd.decode()
138142 if plugin.canParseCommandString(cmd):
139143 return plugin
140144 return None
164168 """
165169 output_queue = JoinableQueue()
166170 plugin.set_actions_queue(self.pending_actions)
167
168 self.plugin_process = PluginProcess(
169 plugin, output_queue, isReport)
170
171 logger.debug(
172 "Created plugin_process (%d) for plugin instance (%d)" %
173 (id(self.plugin_process), id(plugin)))
174
171 self.plugin_process = PluginProcess(plugin, output_queue, isReport)
172 logger.info("Created plugin_process (%d) for plugin instance (%d)", id(self.plugin_process), id(plugin))
175173 self.pending_actions.put((Modelactions.PLUGINSTART, plugin.id, command.getID()))
176174 output_queue.put((output, command.getID()))
177175 plugin_commiter = PluginCommiter(
192190 decodes and performs the action given
193191 It works kind of a dispatcher
194192 """
195 logger.debug(
196 "_processAction - %s - parameters = %s" %
197 (action, str(parameters)))
193 logger.debug("_processAction - %s - parameters = %s", action, parameters)
198194 self._actionDispatcher[action](*parameters)
199195
200196 def _setupActionDispatcher(self):
259255 return None, None
260256
261257 def onCommandFinished(self, pid, exit_code, term_output):
262 if pid not in self._active_plugins.keys():
258 if pid not in list(self._active_plugins.keys()):
263259 return False
264260 if exit_code != 0:
265261 del self._active_plugins[pid]
274270 del self._active_plugins[pid]
275271 return True
276272
277 def processReport(self, plugin, filepath, ws_name=None):
278
273 def processReport(self, plugin_id, filepath, ws_name=None):
274 if plugin_id not in self._plugins:
275 logger.warning("Unknown Plugin ID: %s", plugin_id)
276 return False
279277 if not ws_name:
280278 ws_name = faraday.client.model.api.getActiveWorkspace().name
281279
283281 **{'workspace': ws_name,
284282 'itime': time.time(),
285283 'import_source': 'report',
286 'command': plugin,
284 'command': plugin_id,
287285 'params': filepath,
288286 })
289287
291289 command_id = self._mapper_manager.save(cmd_info)
292290 cmd_info.setID(command_id)
293291
294 if plugin in self._plugins:
295 logger.info('Processing report with plugin {0}'.format(plugin))
296 self._plugins[plugin].workspace = ws_name
292 if plugin_id in self._plugins:
293 logger.info('Processing report with plugin {0}'.format(plugin_id))
294 self._plugins[plugin_id].workspace = ws_name
297295 with open(filepath, 'rb') as output:
298 self.processOutput(self._plugins[plugin], output.read(), cmd_info, True)
296 self.processOutput(self._plugins[plugin_id], output.read(), cmd_info, True)
299297 return command_id
300298
301299 # Plugin to process this report not found, update duration of plugin process
305303
306304 def clearActivePlugins(self):
307305 self._active_plugins = {}
306
307
308 # I'm Py3
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
77
8 '''
8 """
9 from __future__ import absolute_import
910
1011 from faraday.client.plugins.plugin import PluginBase as PluginBaseExt
1112
1213 # This class was moved to plugins.plugin so we need a way to
1314 # support plugins that are still inheriting from core
1415 PluginBase = PluginBaseExt
16
17
18 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 from __future__ import absolute_import
7
68 import imp
79 import os
810 import sys
1719
1820
1921 def get_available_plugins():
20 from faraday import client
22 from faraday import client # pylint:disable=import-outside-toplevel
2123 client_base_path = os.path.dirname(os.path.abspath(client.__file__))
2224
2325 scan_path = os.path.join(client_base_path, "bin")
8789 url=CONF.getServerURI(),
8890 workspace=workspace_name
8991 )
92
93
94 # I'm Py3
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
77
8 '''
8 """
9 from __future__ import absolute_import
910
10 import imp
1111 import os
1212 import re
1313 import sys
1414 import traceback
1515 import logging
16
17 from importlib.machinery import SourceFileLoader
1618
1719 from faraday.config.configuration import getInstanceConfiguration
1820
2022
2123 logger = logging.getLogger(__name__)
2224
23 class PluginManager(object):
25 class PluginManager:
2426
2527 def __init__(self, plugin_repo_path, pending_actions=None):
2628 self._controllers = {}
2729 self._plugin_modules = {}
30 self._plugin_instances = {}
2831 self._loadPlugins(plugin_repo_path)
2932 self._plugin_settings = {}
3033 self.pending_actions = pending_actions
3942 self._plugin_settings = _plugin_settings
4043
4144 activep = self._instancePlugins()
42 for plugin_id, plugin in activep.iteritems():
45 for plugin_id, plugin in activep.items():
4346 if plugin_id in _plugin_settings:
4447 plugin.updateSettings(_plugin_settings[plugin_id]["settings"])
4548 self._plugin_settings[plugin_id] = {
5154 }
5255
5356 dplugins = []
54 for k, v in self._plugin_settings.iteritems():
57 for k, v in self._plugin_settings.items():
5558 if k not in activep:
5659 dplugins.append(k)
5760
6871 self._plugin_settings = settings
6972 CONF.setPluginSettings(settings)
7073 CONF.saveConfig()
71 for plugin_id, params in settings.iteritems():
74 for plugin_id, params in settings.items():
7275 new_settings = params["settings"]
73 for c_id, c_instance in self._controllers.iteritems():
76 for c_id, c_instance in self._controllers.items():
7477 c_instance.updatePluginSettings(plugin_id, new_settings)
7578
7679 def _instancePlugins(self):
77 plugins = {}
78 for module in self._plugin_modules.values():
79 new_plugin = module.createPlugin()
80 new_plugin.set_actions_queue(self.pending_actions)
81 self._verifyPlugin(new_plugin)
82 plugins[new_plugin.id] = new_plugin
83 return plugins
80 if not self._plugin_instances:
81 for module in self._plugin_modules.values():
82 new_plugin = module.createPlugin()
83 new_plugin.set_actions_queue(self.pending_actions)
84 self._verifyPlugin(new_plugin)
85 if new_plugin.id.lower() not in self._plugin_instances:
86 self._plugin_instances[new_plugin.id.lower()] = new_plugin
87 else:
88 logger.warning("Duplicated Plugin ID (%s)", new_plugin.id.lower())
89 return self._plugin_instances
8490
8591 def _loadPlugins(self, plugin_repo_path):
8692 """
9197 os.stat(plugin_repo_path)
9298 except OSError:
9399 pass
94
95100 sys.path.append(plugin_repo_path)
96
97101 dir_name_regexp = re.compile(r"^[\d\w\-\_]+$")
98102 if not os.path.exists(plugin_repo_path):
99103 logger.error('Plugins path could not be opened, no pluging will be available!')
100104 return
101105 for name in os.listdir(plugin_repo_path):
102 if dir_name_regexp.match(name):
106 if dir_name_regexp.match(name) and name != "__pycache__":
103107 try:
104108 module_path = os.path.join(plugin_repo_path, name)
105109 sys.path.append(module_path)
106110 module_filename = os.path.join(module_path, "plugin.py")
107 if not os.path.exists(module_filename):
108 module_filename = os.path.join(module_path,
109 "plugin.pyc")
110
111111 file_ext = os.path.splitext(module_filename)[1]
112112 if file_ext.lower() == '.py':
113 self._plugin_modules[name] = imp.load_source(name,
114 module_filename)
115
116 elif file_ext.lower() == '.pyc':
117 self._plugin_modules[name] = imp.load_compiled(name,
118 module_filename)
113 loader = SourceFileLoader(name, module_filename)
114 self._plugin_modules[name] = loader.load_module()
119115 logger.debug('Loading plugin {0}'.format(name))
120116 except Exception as e:
121 msg = "An error ocurred while loading plugin %s.\n%s" % (
122 module_filename, traceback.format_exc())
123 logger.debug(msg)
124 logger.warn(e)
117 logger.debug("An error ocurred while loading plugin %s.\n%s", module_filename, traceback.format_exc())
118 logger.warning(e)
125119
126120 def getPlugins(self):
127121 plugins = self._instancePlugins()
141135 assert(new_plugin.name is not None)
142136 assert(new_plugin.framework_version is not None)
143137 except (AssertionError, KeyError):
144
145138 return False
146139 return True
140
141
142 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9
10
5 """
116 import os
127 import re
138 import time
2116 import faraday.client.model.common
2217 from faraday import __license_version__ as license_version
2318 from faraday.client.model.common import factory
24 from faraday.client.persistence.server.models import get_host , update_host
19 from faraday.client.persistence.server.models import get_host, update_host
2520 from faraday.client.persistence.server.models import (
2621 Host,
2722 Service,
3833 logger = logging.getLogger(__name__)
3934
4035
41 class PluginBase(object):
36 class PluginBase:
4237 # TODO: Add class generic identifier
4338 class_signature = "PluginBase"
4439
4540 def __init__(self):
46
4741 self.data_path = CONF.getDataPath()
4842 self.persistence_path = CONF.getPersistencePath()
4943 self.workspace = CONF.getLastWorkspace()
6256 self._new_elems = []
6357 self._settings = {}
6458 self.command_id = None
59 self.logger = logger.getChild(self.__class__.__name__)
60
61 def report_belongs_to(self, **kwargs):
62 return False
6563
6664 def has_custom_output(self):
6765 return bool(self._output_file_path)
8381 self.command_id = command_id
8482
8583 def getSettings(self):
86 for param, (param_type, value) in self._settings.iteritems():
84 for param, (param_type, value) in self._settings.items():
8785 yield param, value
8886
8987 def get_ws(self):
9795 self._settings[param] = param_type, value
9896
9997 def updateSettings(self, new_settings):
100 for name, value in new_settings.iteritems():
98 for name, value in new_settings.items():
10199 if name in self._settings:
102100 setting_type, curr_value = self._settings[name]
103101 self._settings[name] = setting_type, setting_type(value)
117115 """
118116 words = current_input.split(" ")
119117 cword = words[len(words) - 1]
120
121118 options = {}
122 for k, v in self._completition.iteritems():
119 for k, v in self._completition.items():
123120 if re.search(str("^" + cword), k, flags=re.IGNORECASE):
124121 options[k] = v
125
126122 return options
127123
128124 def processOutput(self, term_output):
164160 The caller of this function has to build the action in the right
165161 way since no checks are preformed over args
166162 """
167
168163 if self.command_id:
169164 args = args + (self.command_id, )
170165 else:
171 logger.warn('Warning command id not set for action {0}'.format(args))
166 logger.warning('Warning command id not set for action {%s}', args)
172167 logger.debug('AddPendingAction %s', args)
173168 self._pending_actions.put(args)
174169
175170 def createAndAddHost(self, name, os="unknown", hostnames=None, mac=None):
176
177171 host_obj = factory.createModelObject(
178172 Host.class_signature,
179173 name,
182176 workspace_name=self.workspace,
183177 hostnames=hostnames,
184178 mac=mac)
185
186179 host_obj._metadata.creatoserverr = self.id
187180 self.__addPendingAction(Modelactions.ADDHOST, host_obj)
188181 return host_obj.getID()
223216 current_version=VERSION,
224217 details="Interface object removed. Use host or service instead. Service will be attached to Host!")
225218 def createAndAddServiceToInterface(self, host_id, interface_id, name,
226 protocol="tcp?", ports=[],
219 protocol="tcp?", ports=None,
227220 status="open", version="unknown",
228221 description=""):
222 if not ports:
223 ports = []
229224 if status not in ("open", "closed", "filtered"):
230225 self.log(
231226 'Unknown service status %s. Using "open" instead' % status,
245240 return serv_obj.getID()
246241
247242 def createAndAddServiceToHost(self, host_id, name,
248 protocol="tcp?", ports=[],
243 protocol="tcp?", ports=None,
249244 status="open", version="unknown",
250245 description=""):
246 if not ports:
247 ports = []
251248 if status not in ("open", "closed", "filtered"):
252249 self.log(
253250 'Unknown service status %s. Using "open" instead' % status,
266263 self.__addPendingAction(Modelactions.ADDSERVICEHOST, serv_obj)
267264 return serv_obj.getID()
268265
269 def createAndAddVulnToHost(self, host_id, name, desc="", ref=[],
266 def createAndAddVulnToHost(self, host_id, name, desc="", ref=None,
270267 severity="", resolution="", data="", external_id=None):
271
268 if not ref:
269 ref = []
272270 vuln_obj = faraday.client.model.common.factory.createModelObject(
273271 Vuln.class_signature,
274272 name, data=data, desc=desc, refs=ref, severity=severity,
284282 current_version=VERSION,
285283 details="Interface object removed. Use host or service instead. Vuln will be added to Host")
286284 def createAndAddVulnToInterface(self, host_id, interface_id, name,
287 desc="", ref=[], severity="",
285 desc="", ref=None, severity="",
288286 resolution="", data=""):
289
287 if not ref:
288 ref = []
290289 vuln_obj = faraday.client.model.common.factory.createModelObject(
291290 Vuln.class_signature,
292291 name, data=data, desc=desc, refs=ref, severity=severity,
299298 return vuln_obj.getID()
300299
301300 def createAndAddVulnToService(self, host_id, service_id, name, desc="",
302 ref=[], severity="", resolution="", data="", external_id=None):
303
301 ref=None, severity="", resolution="", data="", external_id=None):
302 if not ref:
303 ref = []
304304 vuln_obj = faraday.client.model.common.factory.createModelObject(
305305 Vuln.class_signature,
306306 name, data=data, desc=desc, refs=ref, severity=severity,
314314 return vuln_obj.getID()
315315
316316 def createAndAddVulnWebToService(self, host_id, service_id, name, desc="",
317 ref=[], severity="", resolution="",
317 ref=None, severity="", resolution="",
318318 website="", path="", request="",
319319 response="", method="", pname="",
320320 params="", query="", category="", data="", external_id=None):
321 if not ref:
322 ref = []
321323 vulnweb_obj = faraday.client.model.common.factory.createModelObject(
322324 VulnWeb.class_signature,
323325 name, data=data, desc=desc, refs=ref, severity=severity,
365367
366368 class PluginTerminalOutput(PluginBase):
367369 def __init__(self):
368 super(PluginTerminalOutput, self).__init__()
370 super().__init__()
369371
370372 def processOutput(self, term_output):
371 self.parseOutputString(term_output)
373 try:
374 self.parseOutputString(term_output)
375 except Exception as e:
376 self.logger.exception(e)
372377
373378
374379 class PluginCustomOutput(PluginBase):
375380 def __init__(self):
376 super(PluginCustomOutput, self).__init__()
381 super().__init__()
377382
378383 def processOutput(self, term_output):
379384 # we discard the term_output since it's not necessary
381386 self.processReport(self._output_file_path)
382387
383388
389 class PluginByExtension(PluginBase):
390 def __init__(self):
391 super().__init__()
392 self.extension = []
393
394 def report_belongs_to(self, extension="", **kwargs):
395 match = False
396 if type(self.extension) == str:
397 match = (self.extension == extension)
398 elif type(self.extension) == list:
399 match = (extension in self.extension)
400 self.logger.debug("Extension Match: [%s =/in %s] -> %s", extension, self.extension, match)
401 return match
402
403
404 class PluginXMLFormat(PluginByExtension):
405
406 def __init__(self):
407 super().__init__()
408 self.identifier_tag = []
409 self.extension = ".xml"
410
411 def report_belongs_to(self, main_tag="", **kwargs):
412 match = False
413 if super().report_belongs_to(**kwargs):
414 if type(self.identifier_tag) == str:
415 match = (main_tag == self.identifier_tag)
416 elif type(self.identifier_tag) == list:
417 match = (main_tag in self.identifier_tag)
418 self.logger.debug("Tag Match: [%s =/in %s] -> %s", main_tag, self.identifier_tag, match)
419 return match
420
421
422 class PluginJsonFormat(PluginByExtension):
423
424 def __init__(self):
425 super().__init__()
426 self.json_keys = set()
427 self.extension = ".json"
428
429 def report_belongs_to(self, **kwargs):
430 match = False
431 if super().report_belongs_to(**kwargs):
432 pass
433 return match
434
435
384436 class PluginProcess(Thread):
385437 def __init__(self, plugin_instance, output_queue, isReport=False):
386438 """
390442 :param output_queue: queue with raw ouput of that the plugin needs.
391443 :param isReport: output data was read from file.
392444 """
393 super(PluginProcess, self).__init__()
445 super(PluginProcess, self).__init__(name="PluginProcessThread")
394446 self.output_queue = output_queue
395447 self.plugin = plugin_instance
396448 self.isReport = isReport
397449 self.setDaemon(True)
398 self.stop = False
450 self._must_stop = False
399451
400452 def run(self):
401453 proc_name = self.name
402454 faraday.client.model.api.devlog("-" * 40)
403 faraday.client.model.api.devlog("proc_name = %s" % proc_name)
404 faraday.client.model.api.devlog("Starting run method on PluginProcess")
405 faraday.client.model.api.devlog('parent process: %s' % os.getppid())
406 faraday.client.model.api.devlog('process id: %s' % os.getpid())
455 faraday.client.model.api.devlog(f"proc_name = {proc_name}")
456 faraday.client.model.api.devlog(f"Starting run method on PluginProcess")
457 faraday.client.model.api.devlog(f"parent process: {os.getppid()}")
458 faraday.client.model.api.devlog(f"process id: {os.getpid()}")
407459 faraday.client.model.api.devlog("-" * 40)
408460 done = False
409 while not done and not self.stop:
461 while not done and not self._must_stop:
410462 output, command_id = self.output_queue.get()
411463 self.plugin.setCommandID(command_id)
412464 if output is not None:
413 faraday.client.model.api.devlog('%s: %s' % (proc_name, "New Output"))
465 faraday.client.model.api.devlog(f"{proc_name}: New Output")
414466 try:
467 if isinstance(output, bytes):
468 output = output.decode()
415469 self.plugin.processOutput(output)
416470 except Exception as ex:
417471 faraday.client.model.api.devlog("Plugin raised an exception:")
418472 faraday.client.model.api.devlog(traceback.format_exc())
419473 else:
420474 done = True
421 faraday.client.model.api.devlog('%s: Exiting' % proc_name)
422
475 faraday.client.model.api.devlog(f"{proc_name}: Exiting")
423476 self.output_queue.task_done()
424477 time.sleep(0.1)
425478
426 return
427
428479 def stop(self):
429 self.stop = True
480 self._must_stop = True
481
482 # I'm Py3
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
77
8 '''
8 """
9 from __future__ import absolute_import
910
10 import urlparse
11 try:
12 from urlparse import urlsplit
13 except ImportError:
14 from urllib.parse import urlsplit
1115
1216 def get_vulnweb_url_fields(url):
1317 """Given a URL, return kwargs to pass to createAndAddVulnWebToService."""
14 parse = urlparse.urlsplit(url)
18 parse = urlsplit(url)
1519 return {
1620 "website": "{}://{}".format(parse.scheme, parse.netloc),
1721 "path": parse.path,
1822 "query": parse.query
1923 }
24
25
26 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 from __future__ import absolute_import
67
78 import os
9 import logging
810
911 from faraday.client.start_client import FARADAY_CLIENT_BASE
1012
13 SERVICE_MAPPER = None
14
15 logger = logging.getLogger(__name__)
16
17
1118 def filter_services():
12 filename = os.path.join(FARADAY_CLIENT_BASE, 'plugins/port_mapper.txt')
13 with open(filename, "r") as fp:
14 mapper = fp.read()
15 filtering = mapper.split('\n')
16 services = []
19 global SERVICE_MAPPER
20 if not SERVICE_MAPPER:
21 logger.debug("Load service mappers")
22 filename = os.path.join(FARADAY_CLIENT_BASE, 'plugins/port_mapper.txt')
23 with open(filename, encoding='utf-8') as fp:
24 SERVICE_MAPPER = list(map(lambda x: x.strip().split('\t'), list(filter(len, fp.readlines()))))
25 return SERVICE_MAPPER
1726
18 for item in filtering:
19 tup = ()
20 filt = filter(len,item.split('\t'))
21 tup = (filt[0],filt[1])
22 services.append(tup)
23
24 return services
2527
2628 def get_all_protocols():
2729 protocols = [
8385
8486 for item in protocols:
8587 yield item
88
89
90 # I'm Py3
659659 1935/tcp Macromedia Flash Communications Server MX, the precursor to Adobe Flash Media Server before Macromedia's acquisition by Adobe on December 3, 2005
660660 1970/udp Netop Remote Control
661661 1970/tcp Netop Remote Control
662 1972/udp InterSystems Caché
663 1972/tcp InterSystems Caché
662 1972/udp InterSystems Cache
663 1972/tcp InterSystems Cache
664664 1984/udp Big Brother
665665 1984/tcp Big Brother
666666 1985/udp Cisco Hot Standby Router Protocol (HSRP)
711711 2404/tcp IEC 60870-5-104, used to send electric power telecontrol messages between two systems via directly connected data circuits
712712 2427/udp Media Gateway Control Protocol (MGCP) media gateway
713713 2427/tcp Media Gateway Control Protocol (MGCP) media gateway
714 2447/udp ovwdb—OpenView Network Node Manager (NNM) daemon
715 2447/tcp ovwdb—OpenView Network Node Manager (NNM) daemon
714 2447/udp ovwdb OpenView Network Node Manager (NNM) daemon
715 2447/tcp ovwdb OpenView Network Node Manager (NNM) daemon
716716 2483/udp Oracle database listening for insecure client connections to the listener, replaces port 1521
717717 2483/tcp Oracle database listening for insecure client connections to the listener, replaces port 1521
718718 2484/udp Oracle database listening for SSL client connections to the listener
753753 3050/tcp gds-db (Interbase/Firebird databases)
754754 3052/udp APC PowerChute Network
755755 3052/tcp APC PowerChute Network
756 3074/udp Xbox LIVE and Games for Windows – Live
757 3074/tcp Xbox LIVE and Games for Windows – Live
756 3074/udp Xbox LIVE and Games for Windows Live
757 3074/tcp Xbox LIVE and Games for Windows Live
758758 3225/udp Fibre Channel over IP (FCIP)
759759 3225/tcp Fibre Channel over IP (FCIP)
760760 3233/udp WhiskerControl research control protocol
11391139 11112/tcp ACR/NEMA Digital Imaging and Communications in Medicine (DICOM)
11401140 11371/udp OpenPGP HTTP key server
11411141 11371/tcp OpenPGP HTTP key server
1142 13720/udp Symantec NetBackup—bprd (formerly VERITAS)
1143 13720/tcp Symantec NetBackup—bprd (formerly VERITAS)
1144 13721/udp Symantec NetBackup—bpdbm (formerly VERITAS)
1145 13721/tcp Symantec NetBackup—bpdbm (formerly VERITAS)
1146 13724/udp Symantec Network Utility—vnetd (formerly VERITAS)
1147 13724/tcp Symantec Network Utility—vnetd (formerly VERITAS)
1148 13782/udp Symantec NetBackup—bpcd (formerly VERITAS)
1149 13782/tcp Symantec NetBackup—bpcd (formerly VERITAS)
1142 13720/udp Symantec NetBackup-bprd (formerly VERITAS)
1143 13720/tcp Symantec NetBackup-bprd (formerly VERITAS)
1144 13721/udp Symantec NetBackup-bpdbm (formerly VERITAS)
1145 13721/tcp Symantec NetBackup-bpdbm (formerly VERITAS)
1146 13724/udp Symantec Network Utility-vnetd (formerly VERITAS)
1147 13724/tcp Symantec Network Utility-vnetd (formerly VERITAS)
1148 13782/udp Symantec NetBackup-bpcd (formerly VERITAS)
1149 13782/tcp Symantec NetBackup-bpcd (formerly VERITAS)
11501150 13783/udp Symantec VOPIED protocol (formerly VERITAS)
11511151 13783/tcp Symantec VOPIED protocol (formerly VERITAS)
1152 13785/udp Symantec NetBackup Database—nbdb (formerly VERITAS)
1153 13785/tcp Symantec NetBackup Database—nbdb (formerly VERITAS)
1152 13785/udp Symantec NetBackup Database-nbdb (formerly VERITAS)
1153 13785/tcp Symantec NetBackup Database-nbdb (formerly VERITAS)
11541154 13786/udp Symantec nomdb (formerly VERITAS)
11551155 13786/tcp Symantec nomdb (formerly VERITAS)
11561156 15345/udp XPilot Contact
11871187 27009/tcp FlexNet Publisher's License server (from the range of default ports)
11881188 33434/udp traceroute
11891189 33434/tcp traceroute
1190 40000/udp SafetyNET p – a real-time Industrial Ethernet protocol
1191 40000/tcp SafetyNET p – a real-time Industrial Ethernet protocol
1190 40000/udp SafetyNET p a real-time Industrial Ethernet protocol
1191 40000/tcp SafetyNET p a real-time Industrial Ethernet protocol
11921192 44818/udp EtherNet/IP explicit messaging
11931193 44818/tcp EtherNet/IP explicit messaging
11941194 47808/udp BACnet Building Automation and Control Networks (4780810 = BAC016)
11951195 47808/tcp BACnet Building Automation and Control Networks (4780810 = BAC016)
11961196 49151/udp Reserved
1197 49151/tcp Reserved
1197 49151/tcp Reserved
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
10 from faraday.client.plugins import core
11 from faraday.client.model import api
12 from urlparse import urlsplit
5 """
6 from urllib.parse import urlsplit
137 import socket
14 import sys
158 import re
169 import os
1710
2316 import xml.etree.ElementTree as ET
2417 ETREE_VERSION = ET.VERSION
2518
19 from faraday.client.plugins.plugin import PluginXMLFormat
20 from faraday.client.model import api
21
2622 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
2723
2824 current_path = os.path.abspath(os.getcwd())
3632 __status__ = "Development"
3733
3834
39 class AcunetixXmlParser(object):
35 class AcunetixXmlParser:
4036 """
4137 The objective of this class is to parse an xml file generated by
4238 the acunetix tool.
5046 """
5147
5248 def __init__(self, xml_output):
53
5449 tree = self.parse_xml(xml_output)
55
5650 if tree:
57 self.sites = [data for data in self.get_items(tree)]
51 self.sites = list(self.get_items(tree))
5852 else:
5953 self.sites = []
6054
6963 """
7064 try:
7165 tree = ET.fromstring(xml_output)
72 except SyntaxError, err:
73 print "SyntaxError: %s. %s" % (err, xml_output)
66 except SyntaxError as err:
67 print("SyntaxError: %s. %s", err, xml_output)
7468 return None
7569
7670 return tree
119113 return None
120114
121115
122 class Site(object):
116 class Site:
123117
124118 def __init__(self, item_node):
125119 self.node = item_node
172166 return url_data
173167
174168
175 class Item(object):
169 class Item:
176170 """
177171 An abstract representation of a Item
178172
182176
183177 def __init__(self, item_node):
184178 self.node = item_node
185
186179 self.name = self.get_text_from_subnode('Name')
187180 self.severity = self.get_text_from_subnode('Severity')
188181 self.request = self.get_text_from_subnode('TechnicalDetails/Request')
226219 return None
227220
228221
229 class AcunetixPlugin(core.PluginBase):
222 class AcunetixPlugin(PluginXMLFormat):
230223 """
231224 Example plugin to parse acunetix output.
232225 """
233226
234227 def __init__(self):
235 core.PluginBase.__init__(self)
228 super().__init__()
229 self.identifier_tag = "ScanGroup"
236230 self.id = "Acunetix"
237231 self.name = "Acunetix XML Output Plugin"
238232 self.plugin_version = "0.0.1"
241235 self.options = None
242236 self._current_output = None
243237 self.target = None
244 self._command_regex = re.compile(
245 r'^(acunetix|sudo acunetix|\.\/acunetix).*?')
238 self._command_regex = re.compile(r'^(acunetix|sudo acunetix|\.\/acunetix).*?')
246239
247240 global current_path
248241 self._output_file_path = os.path.join(
261254 parser = AcunetixXmlParser(output)
262255
263256 for site in parser.sites:
264
265257 if site.ip is None:
266258 continue
267
268259 host = []
269260 if site.host != site.ip:
270261 host = [site.host]
271
272262 h_id = self.createAndAddHost(site.ip, site.os)
273263 i_id = self.createAndAddInterface(
274264 h_id,
275265 site.ip,
276266 ipv4_address=site.ip,
277267 hostname_resolution=host)
278
279268 s_id = self.createAndAddServiceToInterface(
280269 h_id,
281270 i_id,
284273 ports=[site.port],
285274 version=site.banner,
286275 status='open')
287
288 n_id = self.createAndAddNoteToService(h_id, s_id, "website", "")
289 self.createAndAddNoteToNote(h_id, s_id, n_id, site.host, "")
290
291276 for item in site.items:
292277 self.createAndAddVulnWebToService(
293278 h_id,
302287 request=item.request,
303288 response=item.response,
304289 ref=item.ref)
305
306290 del parser
307291
308292 def processCommandString(self, username, current_path, command_string):
314298
315299 def createPlugin():
316300 return AcunetixPlugin()
317
318 if __name__ == '__main__':
319 parser = AcunetixXmlParser(sys.argv[1])
320 for item in parser.items:
321 if item.status == 'up':
322 print item
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
9 from __future__ import with_statement
4 """
105 from faraday.client.plugins import core
116 import argparse
127 import shlex
2217 """ Example plugin to parse amap output."""
2318
2419 def __init__(self):
25 core.PluginBase.__init__(self)
20 super().__init__()
2621 self.id = "Amap"
2722 self.name = "Amap Output Plugin"
2823 self.plugin_version = "0.0.3"
129124
130125 def get_ip_6(self, host, port=0):
131126 alladdr = socket.getaddrinfo(host, port)
132 ip6 = filter(
127 ip6 = list(filter(
133128 lambda x: x[0] == socket.AF_INET6,
134 alladdr)
129 alladdr))
135130
136 return list(ip6)[0][4][0]
131 return ip6[0][4][0]
137132
138133 def getAddress(self, hostname):
139134 """
141136 """
142137 try:
143138 return socket.gethostbyname(hostname)
144 except socket.error, msg:
139 except socket.error as msg:
145140 return hostname
146141
147142 def processCommandString(self, username, current_path, command_string):
194189
195190 def createPlugin():
196191 return AmapPlugin()
192
193
194 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
8
9 import pprint
7 """
8
109 import socket
11 from faraday.client.plugins import core
10 from faraday.client.plugins.plugin import PluginXMLFormat
1211 from lxml import objectify
13 from urlparse import urlparse
12 try:
13 from urlparse import urlparse
14 except ImportError:
15 from urllib.parse import urlparse
1416
1517 __author__ = "Alejando Parodi, Ezequiel Tavella"
1618 __copyright__ = "Copyright (c) 2015, Infobyte LLC"
3133
3234 class AppscanParser():
3335
34 def __init__(self, output):
36 def __init__(self, output, logger):
3537 self.issue_list = []
36 self.obj_xml = objectify.fromstring(output)
38 self.logger = logger
39 self.obj_xml = objectify.fromstring(output.encode('utf-8'))
3740
3841 def parse_issues(self):
3942 issue_type = self.parse_issue_type()
4043 for issue in self.obj_xml["issue-group"]["item"]:
4144 issue_data = issue_type[issue['issue-type']['ref']]
4245 obj_issue = {}
43
4446 obj_issue["name"] = issue_data["name"]
4547 obj_issue['advisory'] = issue_data["advisory"]
46
47 if("cve" in issue_data):
48 if "cve" in issue_data:
4849 obj_issue['cve'] = issue_data["cve"].text
49
5050 obj_issue['url'] = self.get_url(issue['url']['ref'].text)
5151 obj_issue['cvss_score'] = issue["cvss-score"].text
5252 obj_issue['response'] = self.get_response(issue)
5454 obj_issue['method'] = self.get_method(issue['variant-group']['item']["test-http-traffic"].text)
5555 obj_issue['severity'] = issue['severity'].text
5656 obj_issue['issue-description'] = self.parse_advisory_group(issue_data['advisory'])
57
58 for recomendation in self.obj_xml["fix-recommendation-group"]["item"]:
57 for recommendation in self.obj_xml["fix-recommendation-group"]["item"]:
5958 full_data = ""
60 if(recomendation.attrib['id'] == issue_data["fix-recommendation"]):
61 for data in recomendation['general']['fixRecommendation']["text"]:
59 if recommendation.attrib['id'] == issue_data["fix-recommendation"]:
60 for data in recommendation['general']['fixRecommendation']["text"]:
6261 full_data += '' + data
6362 obj_issue["recomendation"] = full_data
64 if(hasattr(recomendation['general']['fixRecommendation'], 'link')):
65 obj_issue["ref_link"] = recomendation['general']['fixRecommendation']['link'].text
66
63 if hasattr(recommendation['general']['fixRecommendation'], 'link'):
64 obj_issue["ref_link"] = recommendation['general']['fixRecommendation']['link'].text
6765 self.issue_list.append(obj_issue)
6866 return self.issue_list
6967
7068 def parse_hosts(self):
7169 hosts_list = []
72
7370 for host in self.obj_xml['scan-configuration']['scanned-hosts']['item']:
7471 hosts_dict = {}
7572 hosts_dict['ip'] = socket.gethostbyname(host['host'].text)
7673 hosts_dict['hostname'] = host['host'].text
7774 hosts_dict['os'] = host['operating-system'].text
7875 hosts_dict['port'] = host['port'].text
79
8076 if host['port'].text == '443':
8177 hosts_dict['scheme'] = 'https'
8278 else:
8379 hosts_dict['scheme'] = 'http'
84
8580 hosts_list.append(hosts_dict)
86
8781 return hosts_list
8882
8983 def parse_issue_type(self):
9084 res = {}
91
9285 for issue_type in self.obj_xml["issue-type-group"]["item"]:
9386 res[issue_type.attrib['id']] = {
9487 'name': issue_type.name.text,
9588 'advisory': issue_type["advisory"]["ref"].text,
9689 'fix-recommendation': issue_type["fix-recommendation"]["ref"].text
97 }
98
90 }
9991 if "cve" in issue_type:
100 res[issue_type.attrib['id']] = {'cve': issue_type["cve"].text}
101
92 res[issue_type.attrib['id']] = {'cve': issue_type["cve"].text}
10293 return res
10394
10495 def parse_advisory_group(self, advisory):
105 '''
96 """
10697 Function that parse advisory-group in order to get the item's description
107 '''
98 """
10899 for item in self.obj_xml["advisory-group"]["item"]:
109100 if item.attrib['id'] == advisory:
110101 return item['advisory']['testTechnicalDescription']['text'].text
115106 return item['name'].text
116107
117108 def get_method(self, http_traffic):
118 methods_list = ['GET','POST','PUT','DELETE','CONNECT','PATCH', 'HEAD', 'OPTIONS']
119
109 methods_list = ['GET', 'POST', 'PUT', 'DELETE', 'CONNECT', 'PATCH', 'HEAD', 'OPTIONS']
120110 try:
121111 if http_traffic:
122112 for item in methods_list:
123113 if http_traffic.startswith(item):
124114 return item
125
126115 except TypeError:
127116 return None
128
129117 return None
130118
131119 def get_response(self, node):
146134 return scan_information
147135
148136
149 class AppscanPlugin(core.PluginBase):
137 class AppscanPlugin(PluginXMLFormat):
150138 """ Example plugin to parse Appscan XML report"""
151139
152140 def __init__(self):
153 core.PluginBase.__init__(self)
141 super().__init__()
142 self.identifier_tag = "xml-report"
154143 self.id = "Appscan"
155144 self.name = "Appscan XML Plugin"
156145 self.plugin_version = "0.0.1"
157146 self.options = None
158147
159148 def parseOutputString(self, output, debug=False):
160
161 parser = AppscanParser(output)
162 issues = parser.parse_issues()
163 scanned_hosts = parser.parse_hosts()
164 hosts_dict = {}
165
166 for host in scanned_hosts:
167 host_id = self.createAndAddHost(host['ip'], os=host['os'], hostnames=[host['hostname']])
168 service_id = self.createAndAddServiceToHost(
169 host_id,
170 host['scheme'],
171 ports=[host['port']],
172 protocol="tcp?HTTP")
173
174 hosts_dict['://'.join([host['scheme'], host['hostname']])] = {'host_id': host_id, 'service_id': service_id}
175
176 for issue in issues:
177 url_parsed = urlparse(str(issue['url']))
178 url_string = '://'.join([url_parsed.scheme, url_parsed.netloc])
179 for key in hosts_dict:
180 if url_string == key:
181 h_id = hosts_dict[key]['host_id']
182 s_id = hosts_dict[key]['service_id']
183 refs = []
184 if "ref_link" in issue:
185 refs.append("Fix link: " + issue["ref_link"])
186 if "cvss_score" in issue:
187 refs.append("CVSS Score: " + issue["cvss_score"])
188 if "cve" in issue:
189 refs.append("CVE: " + issue["cve"])
190 if "advisory" in issue:
191 refs.append("Advisory: " + issue["advisory"])
192
193 self.createAndAddVulnWebToService(
194 h_id,
195 s_id,
196 cleaner_unicode(issue["name"]),
197 desc=cleaner_unicode(issue["issue_description"]) if "issue_description" in issue else "",
198 ref=refs,
199 severity=issue["severity"],
200 resolution=cleaner_unicode(issue["recomendation"]),
201 website=url_parsed.netloc,
202 path=url_parsed.path,
203 request=cleaner_unicode(issue["request"]) if "request" in issue else "",
204 response=cleaner_unicode(issue["response"]) if issue["response"] else "",
205 method=issue["method"] if issue["method"] else "")
206
207 return
149 try:
150 parser = AppscanParser(output, self.logger)
151 issues = parser.parse_issues()
152 scanned_hosts = parser.parse_hosts()
153 hosts_dict = {}
154 for host in scanned_hosts:
155 host_id = self.createAndAddHost(host['ip'], os=host['os'], hostnames=[host['hostname']])
156 service_id = self.createAndAddServiceToHost(
157 host_id,
158 host['scheme'],
159 ports=[host['port']],
160 protocol="tcp?HTTP")
161 if host['port']:
162 key_url = f"{host['scheme']}://{host['hostname']}:{host['port']}"
163 else:
164 key_url = f"{host['scheme']}://{host['hostname']}"
165 hosts_dict[key_url] = {'host_id': host_id, 'service_id': service_id}
166 for issue in issues:
167 url_parsed = urlparse(str(issue['url']))
168 url_string = '://'.join([url_parsed.scheme, url_parsed.netloc])
169 for key in hosts_dict:
170 if url_string == key:
171 h_id = hosts_dict[key]['host_id']
172 s_id = hosts_dict[key]['service_id']
173 refs = []
174 if "ref_link" in issue:
175 refs.append(f"Fix link: {issue['ref_link']}" )
176 if "cvss_score" in issue:
177 refs.append(f"CVSS Score: {issue['cvss_score']}")
178 if "cve" in issue:
179 refs.append(f"CVE: {issue['cve']}")
180 if "advisory" in issue:
181 refs.append(f"Advisory: {issue['advisory']}")
182 self.createAndAddVulnWebToService(
183 h_id,
184 s_id,
185 cleaner_unicode(issue["name"]),
186 desc=cleaner_unicode(issue["issue_description"]) if "issue_description" in issue else "",
187 ref=refs,
188 severity=issue["severity"],
189 resolution=cleaner_unicode(issue["recomendation"]),
190 website=url_parsed.netloc,
191 path=url_parsed.path,
192 request=cleaner_unicode(issue["request"]) if "request" in issue else "",
193 response=cleaner_unicode(issue["response"]) if issue["response"] else "",
194 method=issue["method"] if issue["method"] else "")
195 except Exception as e:
196 self.logger.error("Parsing Output Error: %s", e)
208197
209198 def processCommandString(self, username, current_path, command_string):
210199 return
220209 parser.parseOutputString(report.read())
221210 for item in parser.items:
222211 if item.status == 'up':
223 print item
212 print(item)
213
214
215 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
8
9 from __future__ import with_statement
10 from faraday.client.plugins import core
7 """
8 from faraday.client.plugins.plugin import PluginXMLFormat
119 from faraday.client.model import api
1210 import socket
1311 import os
3028 class ArachniXmlParser():
3129
3230 def __init__(self, xml_output):
33
3431 self.tree = self.parse_xml(xml_output)
35
3632 if self.tree:
3733 self.issues = self.getIssues(self.tree)
3834 self.plugins = self.getPlugins(self.tree)
3935 self.system = self.getSystem(self.tree)
40
4136 else:
4237 self.system = None
4338 self.issues = None
4439 self.plugins = None
4540
4641 def parse_xml(self, xml_output):
47
4842 try:
4943 tree = ET.fromstring(xml_output)
50 except SyntaxError, err:
51 print 'SyntaxError In xml: %s. %s' % (err, xml_output)
44 except SyntaxError as err:
45 print('SyntaxError In xml: %s. %s' % (err, xml_output))
5246 return None
5347
5448 return tree
352346 return 'None'
353347
354348
355 class ArachniPlugin(core.PluginBase):
349 class ArachniPlugin(PluginXMLFormat):
356350
357351 # Plugin that parses Arachni's XML report files.
358352
359353 def __init__(self):
360
361 core.PluginBase.__init__(self)
354 super().__init__()
355 self.identifier_tag = "report"
362356 self.id = 'Arachni'
363357 self.name = 'Arachni XML Output Plugin'
364358 self.plugin_version = '1.0.1'
365359 self.version = '1.3.2'
366360 self.framework_version = '1.0.0'
367361 self.options = None
368
369 self._command_regex = re.compile(
370 r'^(arachni |\.\/arachni).*?')
371
362 self._command_regex = re.compile(r'^(arachni |\.\/arachni).*?')
372363 self.protocol = None
373364 self.hostname = None
374365 self.port = '80'
375
376366 self.address = None
367
368 def report_belongs_to(self, **kwargs):
369 if super().report_belongs_to(**kwargs):
370 report_path = kwargs.get("report_path", "")
371 with open(report_path) as f:
372 output = f.read()
373 return re.search("https://raw.githubusercontent.com/Arachni/arachni/", output) is not None
374 return False
377375
378376 def parseOutputString(self, output, debug=False):
379377 """
385383
386384 # Check xml parsed ok...
387385 if not parser.system:
388 print 'Error in xml report... Exiting...'
386 print('Error in xml report... Exiting...')
389387 return
390388
391389 self.hostname = self.getHostname(parser.system.url)
411409 version='',
412410 description='')
413411
414 # Scan Note.
415 noteScan_id = self.createAndAddNoteToService(
416 host_id,
417 service_id,
418 'Scan Information',
419 parser.system.note)
420
421 # Plugins Notes
422 note_id = self.createAndAddNoteToService(
423 host_id,
424 service_id,
425 'Plugins arachni',
426 'Plugins used by arachni and results of this.')
427
428 if parser.plugins.waf != 'None':
429
430 note2_id = self.createAndAddNoteToNote(
431 host_id,
432 service_id,
433 note_id,
434 'Waf Plugin',
435 parser.plugins.waf)
436
437 if parser.plugins.healthmap != 'None':
438
439 note3_id = self.createAndAddNoteToNote(
440 host_id,
441 service_id,
442 note_id,
443 'Healthmap Plugin',
444 parser.plugins.healthmap)
445412
446413 # Create issues.
447414 for issue in parser.issues:
540507 # Returns remote IP address from hostname.
541508 try:
542509 return socket.gethostbyname(hostname)
543 except socket.error, msg:
510 except socket.error as msg:
544511 return self.hostname
545512
546513
547514 def createPlugin():
548515 return ArachniPlugin()
516
517
518 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
4 """
95 from faraday.client.plugins import core
106 from faraday.client.model import api
117 import re
2723 """
2824
2925 def __init__(self):
30 core.PluginBase.__init__(self)
26 super().__init__()
3127 self.id = "arp-scan"
3228 self.name = "arp-scan network scanner"
3329 self.plugin_version = "0.0.2"
6157 host = vals[0]
6258 h_id = self.createAndAddHost(host)
6359 i_id = self.createAndAddInterface(h_id, host, ipv4_address=host, mac=vals[1])
64 n_id = self.createAndAddNoteToHost(h_id, "NIC VENDOR:", vals[2])
6560
6661 return True
6762
7166
7267 def createPlugin():
7368 return CmdArpScanPlugin()
69 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
9 from __future__ import with_statement
4 """
105 from faraday.client.plugins import core
116 from faraday.client.model import api
127 import re
13 import os
148 import sys
15 import urllib2
9 from urllib.request import urlopen
1610 import json
1711
1812 __author__ = "Francisco Amato"
3125 """
3226
3327 def __init__(self):
34 core.PluginBase.__init__(self)
28 super().__init__()
3529 self.id = "Beef"
3630 self.name = "BeEF Online Service Plugin"
3731 self.plugin_version = "0.0.1"
5650 output being sent is valid.
5751 """
5852 try:
59 f = urllib2.urlopen(self.getSetting(
53 f = urlopen(self.getSetting(
6054 "Host") + "/api/hooks?token=" + self.getSetting("Authkey"))
6155 data = json.loads(f.read())
6256 except:
111105 def createPlugin():
112106 return BeefPlugin()
113107
114 if __name__ == '__main__':
115 parser = BeefXmlParser(sys.argv[1])
116 for item in parser.items:
117 if item.status == 'up':
118 print item
108 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
4 '''
4 """
5 # I'm Py3
0 #!/usr/bin/python2.7
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
4 """
95 import re
106 import socket
11 from urlparse import urlparse
7 from urllib.parse import urlparse
128
139 from faraday.client.plugins import core
1410
2117 class brutexss (core.PluginBase):
2218
2319 def __init__(self):
24 core.PluginBase.__init__(self)
20 super().__init__()
2521 self.id = "brutexss"
2622 self.name = "brutexss"
2723 self.plugin_version = "0.0.2"
3228
3329 def parseOutputString(self, output, debug=False):
3430 lineas = output.split("\n")
35 parametro=[]
31 parametro = []
3632 found_vuln = False
3733 for linea in lineas:
38 if (linea.find("is available! Good!")>0):
34 if linea.find("is available! Good!") > 0:
3935 print(linea)
4036 url = re.findall('(?:[-\w.]|(?:%[\da-fA-F]{2}))+', linea)[0]
4137 port = 80
4440 netloc_splitted = urlparse(url).netloc.split(':')
4541 if len(netloc_splitted) > 1:
4642 port = netloc_splitted[1]
47 if ((linea.find("Vulnerable")>0) and (linea.find("No")<0)):
43 if linea.find("Vulnerable") > 0 and "No" not in linea:
4844 vuln_list = re.findall("\w+", linea)
49 if vuln_list[2]=="Vulnerable":
45 if vuln_list[2] == "Vulnerable":
5046 parametro.append(vuln_list[1])
5147 found_vuln=len(parametro) > 0
5248 host_id = self.createAndAddHost(url)
5349 address=socket.gethostbyname(url)
5450 interface_id = self.createAndAddInterface(host_id,address,ipv4_address=address,hostname_resolution=[url])
55 service_id = self.createAndAddServiceToInterface(host_id,interface_id,self.protocol,'tcp',ports=[port],status='Open',version="",description="")
51 service_id = self.createAndAddServiceToInterface(host_id, interface_id, self.protocol, 'tcp',
52 ports=[port], status='Open', version="", description="")
5653 if found_vuln:
57 self.createAndAddVulnWebToService(host_id,service_id,name="xss",desc="XSS",ref='',severity='med',website=url,path='',method='',pname='',params=''.join(parametro),request='',response='')
54 self.createAndAddVulnWebToService(host_id,service_id, name="xss", desc="XSS", ref='', severity='med',
55 website=url, path='', method='', pname='', params=''.join(parametro),
56 request='', response='')
5857
5958 def processCommandString(self, username, current_path, command_string):
6059 return None
6261
6362 def createPlugin():
6463 return brutexss()
64
65
66 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
77
8 '''
9 from __future__ import with_statement
8 """
109 import re
1110 import os
1211 import sys
12 import base64
1313 from bs4 import BeautifulSoup, Comment
14 from faraday.client.plugins import core
15 from urlparse import urlsplit
14 from faraday.client.plugins.plugin import PluginXMLFormat
15 try:
16 from urlparse import urlsplit
17 except ImportError:
18 from urllib.parse import urlsplit
1619 import distutils.util #pylint: disable=import-error
1720
1821
3841 __status__ = "Development"
3942
4043
41 class BurpXmlParser(object):
44 class BurpXmlParser:
4245 """
4346 The objective of this class is to parse an xml file generated by the burp tool.
4447
5760
5861 tree = self.parse_xml(xml_output)
5962 if tree:
60 self.items = [data for data in self.get_items(tree)]
63 self.items = list(self.get_items(tree))
6164 else:
6265 self.items = []
6366
7275 """
7376 try:
7477 tree = ET.fromstring(xml_output)
75 except SyntaxError, err:
76 print "SyntaxError: %s. %s" % (err, xml_output)
78 except SyntaxError as err:
79 print("SyntaxError: %s. %s" % (err, xml_output))
7780 return None
7881
7982 return tree
122125 return None
123126
124127
125 class Item(object):
128 class Item:
126129 """
127130 An abstract representation of a Item
128131 @param item_node A item_node taken from an burp xml tree
191194 return ""
192195 encoded = distutils.util.strtobool(subnode.get('base64', 'false'))
193196 if encoded:
194 res = subnode.text.decode('base64', 'strict')
197 res = base64.b64decode(subnode.text).decode('utf-8', errors="backslashreplace")
195198 else:
196199 res = subnode.text
197200 return "".join([ch for ch in res if ord(ch) <= 128])
209212 return None
210213
211214
212 class BurpPlugin(core.PluginBase):
215 class BurpPlugin(PluginXMLFormat):
213216 """
214217 Example plugin to parse burp output.
215218 """
216219
217220 def __init__(self):
218
219 core.PluginBase.__init__(self)
221 super().__init__()
222 self.identifier_tag = "issues"
220223 self.id = "Burp"
221224 self.name = "Burp XML Output Plugin"
222225 self.plugin_version = "0.0.2"
252255 "tcp",
253256 ports=[str(item.port)],
254257 status="open")
255
256 n_id = self.createAndAddNoteToService(
257 h_id,
258 s_id,
259 "website",
260 "")
261
262 n2_id = self.createAndAddNoteToNote(
263 h_id,
264 s_id,
265 n_id,
266 item.host,
267 "")
268258
269259 desc = "Detail\n" + item.detail
270260 if item.background:
328318 parser = BurpXmlParser(sys.argv[1])
329319 for item in parser.items:
330320 if item.status == 'up':
331 print item
321 print(item)
322
323
324 # I'm Py3
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44 """
5 # I'm Py3
0 # -*- coding: utf-8 -*-
10 """
21 Updated by Mike Zhong, 25 Oct 2017.
32
2625 """
2726
2827 def __init__(self):
29 core.PluginBase.__init__(self)
28 super().__init__()
3029 self.id = u"dig"
3130 self.name = u"DiG"
3231 self.plugin_version = u"0.0.1"
157156
158157 except Exception as ex:
159158 print("some part of the dig plug-in caused an error! Please check repo/dig/plugin.py")
160 logger.error("Error from dig plugin: %s" % (ex))
161159 return False
162160
163161
166164
167165 def createPlugin():
168166 return DigPlugin()
167
168
169 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
4 '''
4 """
5 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
4 """
95 from faraday.client.plugins import core
106 import re
117 import socket
2218 class dirbPlugin(core.PluginBase):
2319
2420 def __init__(self):
25 core.PluginBase.__init__(self)
21 super().__init__()
2622 self.id = "dirb"
2723 self.name = "Dirb"
2824 self.plugin_version = "0.0.1"
121117
122118 def createPlugin():
123119 return dirbPlugin()
120
121
122 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
4 """
95 import re
106 import os
117 import json
139 import socket
1410 import argparse
1511 import tempfile
16 import urlparse
12 import urllib.parse as urlparse
13
14
1715 from faraday.client.plugins.plugin import PluginTerminalOutput
1816 from faraday.client.plugins.plugin_utils import get_vulnweb_url_fields
1917
5957
6058 class DirsearchPlugin(PluginTerminalOutput):
6159 def __init__(self):
62 super(DirsearchPlugin, self).__init__()
60 super().__init__()
6361 self.id = "dirsearch"
6462 self.name = "dirsearch"
6563 self.plugin_version = "0.0.1"
176174
177175 def createPlugin():
178176 return DirsearchPlugin()
177
178
179 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
5 """
106 from faraday.client.plugins import core
117 from faraday.client.model import api
128 import re
139 import os
14 import sys
1510
1611 try:
1712 import xml.etree.cElementTree as ET
3530 __status__ = "Development"
3631
3732
38 class DnsenumXmlParser(object):
33 class DnsenumXmlParser:
3934 """
4035 The objective of this class is to parse an xml file generated by the dnsenum tool.
4136
5045 tree = self.parse_xml(xml_output)
5146
5247 if tree:
53 self.items = [data for data in self.get_items(tree)]
48 self.items = list(self.get_items(tree))
5449 else:
5550 self.items = []
5651
6560 """
6661 try:
6762 tree = ET.fromstring(xml_output)
68 except SyntaxError, err:
69 print "SyntaxError: %s. %s" % (err, xml_output)
63 except SyntaxError as err:
64 print("SyntaxError: %s. %s" % (err, xml_output))
7065 return None
7166
7267 return tree
115110 return None
116111
117112
118 class Item(object):
113 class Item:
119114 """
120115 An abstract representation of a Item
121116
157152 """
158153
159154 def __init__(self):
160
161 core.PluginBase.__init__(self)
155 super().__init__()
162156 self.id = "Dnsenum"
163157 self.name = "Dnsenum XML Output Plugin"
164158 self.plugin_version = "0.0.1"
222216 def createPlugin():
223217 return DnsenumPlugin()
224218
225 if __name__ == '__main__':
226 parser = DnsenumXmlParser(sys.argv[1])
227 for item in parser.items:
228 if item.status == 'up':
229 print item
219 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
0 """from __future__ import print_function
21
3 '''
42 Faraday Penetration Test IDE
53 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
64 See the file 'doc/LICENSE' for the license information
7 '''
8
9 from __future__ import with_statement
5 """
106 from faraday.client.plugins import core
117 import re
128 import os
139 import sys
1410 import random
11 from collections import defaultdict
1512
1613 current_path = os.path.abspath(os.getcwd())
1714
2522 __status__ = "Development"
2623
2724
28 class DnsmapParser(object):
25 class DnsmapParser:
2926 """
3027 The objective of this class is to parse an xml file generated by the
3128 dnsmap tool.
3936 """
4037
4138 def __init__(self, output):
42 self.items = []
39 self.items = defaultdict(list)
4340 if "\n\n" in output:
4441 self.parse_txt(output)
4542 else:
6057 self.add_host_info_to_items(ip, hostname)
6158
6259 def parse_csv(self, output):
63 hosts = filter(None, output.splitlines())
60 hosts = list(filter(None, output.splitlines()))
6461
6562 for host in hosts:
6663 host_data = host.split(",", 1)
8279 if not splitted[i]:
8380 hosts_list.append(aux_list)
8481 aux_list = []
85 continue
8682 else:
8783 aux_list.append(splitted[i])
88
8984 return hosts_list
9085
9186 def clean_ip(self, item):
9388 return ip[1].strip()
9489
9590 def add_host_info_to_items(self, ip_address, hostname):
96 data = {}
97 exists = False
98 for item in self.items:
99 if ip_address in item['ip']:
100 item['hosts'].append(hostname)
101 exists = True
102
103 if not exists:
104 data['ip'] = ip_address
105 data['hosts'] = [hostname]
106 self.items.append(data)
91 self.items[ip_address].append(hostname)
10792
10893
10994 class DnsmapPlugin(core.PluginBase):
11095 """Example plugin to parse dnsmap output."""
11196
11297 def __init__(self):
113
114 core.PluginBase.__init__(self)
98 super().__init__()
11599 self.id = "Dnsmap"
116100 self.name = "Dnsmap Output Plugin"
117101 self.plugin_version = "0.3"
119103 self.options = None
120104 self._current_output = None
121105 self.current_path = None
122 self._command_regex = re.compile(
123 r'^(sudo dnsmap|dnsmap|\.\/dnsmap).*?')
124
106 self._command_regex = re.compile(r'^(sudo dnsmap|dnsmap|\.\/dnsmap).*?')
125107 self.xml_arg_re = re.compile(r"^.*(-r\s*[^\s]+).*$")
126108
127109 global current_path
147129 from the xml where it expects it to be present.
148130 """
149131 parser = DnsmapParser(output)
150 for item in parser.items:
151 h_id = self.createAndAddHost(
152 item['ip'],
153 hostnames=item['hosts'])
154
132 for ip_address, hostnames in parser.items.items():
133 h_id = self.createAndAddHost(ip_address, hostnames=hostnames)
155134 return True
156135
157136 def processCommandString(self, username, current_path, command_string):
172151 def createPlugin():
173152 return DnsmapPlugin()
174153
175 if __name__ == '__main__':
176 parser = DnsmapParser(sys.argv[1])
177 for item in parser.items:
178 if item.status == 'up':
179 print item
154 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
22
3 '''
3 """
44 Faraday Penetration Test IDE
55 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 See the file 'doc/LICENSE' for the license information
7 '''
8
9 from __future__ import with_statement
7 """
108 from faraday.client.plugins import core
119 from faraday.client.model import api
1210 import re
3533 __status__ = "Development"
3634
3735
38 class DnsreconXmlParser(object):
36 class DnsreconXmlParser:
3937 """
4038 The objective of this class is to parse an xml file generated by the dnsrecon tool.
4139
5149 tree = self.parse_xml(xml_output)
5250
5351 if tree:
54 self.hosts = [host for host in self.get_hosts(tree)]
52 self.hosts = list(self.get_hosts(tree))
5553 else:
5654 self.hosts = []
5755
6664 """
6765 try:
6866 tree = ET.fromstring(xml_output)
69 except SyntaxError, err:
70 print "SyntaxError: %s. %s" % (err, xml_output)
67 except SyntaxError as err:
68 print("SyntaxError: %s. %s" % (err, xml_output))
7169 return None
7270
7371 return tree
114112 return None
115113
116114
117 class Item(object):
115 class Item:
118116 """
119117 An abstract representation of a Item
120118
138136 self.name = self.do_clean(self.node.get('name'))
139137 self.exchange = self.do_clean(self.node.get('exchange'))
140138
141 print "GENERATION:" + self.type, self.address, self.zonetransfer
139 print("GENERATION:" + self.type, self.address, self.zonetransfer)
142140
143141 def do_clean(self, value):
144142 myreturn = ''
165163 """
166164
167165 def __init__(self):
168 core.PluginBase.__init__(self)
166 super().__init__()
169167 self.id = "Dnsrecon"
170168 self.name = "Dnsrecon XML Output Plugin"
171169 self.plugin_version = "0.0.2"
183181
184182 def validHosts(self, hosts):
185183 valid_records = ["NS", "CNAME", "A", "MX", "info"]
186 hosts = filter(lambda h: h.type in valid_records, hosts)
184 hosts = list(filter(lambda h: h.type in valid_records, hosts))
187185 return hosts
188186
189187 def parseOutputString(self, output, debug=False):
199197
200198 for host in self.validHosts(parser.hosts):
201199
202 print host.type, host.name, host.zonetransfer
200 print(host.type, host.name, host.zonetransfer)
203201 hostname = host.target
204202 if host.type == "MX":
205203 hostname = host.exchange
272270 def createPlugin():
273271 return DnsreconPlugin()
274272
275 if __name__ == '__main__':
276 parser = DnsreconXmlParser(sys.argv[1])
277 for item in parser.items:
278 if item.status == 'up':
279 print item
273 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 '''
0 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
64
7 '''
8 from __future__ import with_statement
5 """
6
97 from faraday.client.plugins import core
108 import re
119 import os
12 import sys
1310 import socket
1411
1512 current_path = os.path.abspath(os.getcwd())
2421 __status__ = "Development"
2522
2623
27 class DnswalkParser(object):
24 class DnswalkParser:
2825 """
2926 The objective of this class is to parse an xml file generated
3027 by the dnswalk tool.
7976 """
8077
8178 def __init__(self):
82 core.PluginBase.__init__(self)
79 super().__init__()
8380 self.id = "Dnswalk"
8481 self.name = "Dnswalk XML Output Plugin"
8582 self.plugin_version = "0.0.1"
148145 def createPlugin():
149146 return DnswalkPlugin()
150147
151 if __name__ == '__main__':
152 parser = DnswalkParser(sys.argv[1])
153 for item in parser.items:
154 if item.status == 'up':
155 print item
148
149 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 '''
0 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
64
7 '''
8 from __future__ import with_statement
5 """
96 from faraday.client.plugins import core
107 import socket
118 import re
2724 valid_records = ["NS", "CNAME", "A"]
2825
2926
30 class FierceParser(object):
27 class FierceParser:
3128 """
3229 The objective of this class is to parse an shell output generated by
3330 the fierce tool.
5148 if regex is not None:
5249 self.target = regex.group(1)
5350 mstr = re.sub("\t", "", regex.group(2))
54 self.dns = filter(None, mstr.splitlines())
51 self.dns = list(filter(None, mstr.splitlines()))
5552
5653 regex = re.search(
5754 "Now performing [\d]+ test\(s\)...\n([^$]+)\nSubnets found ",
6966 self.isZoneVuln = False
7067 output = output.replace('\\$', '')
7168 regex = re.search(
72 "Whoah, it worked - misconfigured DNS server found:([^$]+)\There isn't much point continuing, you have everything.", output)
69 "Whoah, it worked - misconfigured DNS server found:([^$]+)\nThere isn't much point continuing, you have everything.", output)
7370
7471 if regex is not None:
7572 self.isZoneVuln = True
104101 """
105102
106103 def __init__(self):
107 core.PluginBase.__init__(self)
104 super().__init__()
108105 self.id = "Fierce"
109106 self.name = "Fierce Output Plugin"
110107 self.plugin_version = "0.0.1"
207204 def createPlugin():
208205 return FiercePlugin()
209206
210 if __name__ == '__main__':
211 parser = FierceParser(sys.argv[1])
212 for item in parser.items:
213 if item.status == 'up':
214 print item
207
208 # I'm Py3
00 import base64
11 import io
22 import re
3 from HTMLParser import HTMLParser
3 from html.parser import HTMLParser
44 from zipfile import ZipFile
55
66 import html2text
148148 self.vulns[vulnID]['severity'] = self.calculate_severity(vuln)
149149
150150 # placeholder for storing hosts ids when created in main plugin method
151 if path not in self.hosts.keys():
151 if path not in self.hosts:
152152 self.hosts[path] = None
153153
154154 if vuln.ClassInfo.ClassID not in self.vuln_classes:
300300 if group.get('name') == "Accuracy":
301301 accuracy = group
302302
303 likelihood = (accuracy * vuln.InstanceInfo.Confidence * probability) / 25
303 likelihood = (accuracy * vuln.InstanceInfo.Confidence * probability) / 25.0
304304
305305 if impact and probability:
306306
414414 print(fp.vulns[vulnID]['replacements'])
415415 print("{}{}{}".format("="*50, vulnID, "="*50))
416416 print(fp.format_description(vulnID))
417 print("{}|{}|{}").format(vulnID, fp.vulns[vulnID].get('name'), fp.vulns[vulnID].get('severity'))
417 print("{}|{}|{}".format(vulnID, fp.vulns[vulnID].get('name'), fp.vulns[vulnID].get('severity')))
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
00 #!/usr/bin/python
1 '''
1 """
22 Copyright (C) 2016 xtr4nge [_AT_] gmail.com
33
44 This program is free software: you can redistribute it and/or modify
1313
1414 You should have received a copy of the GNU General Public License
1515 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 '''
16 """
17 from __future__ import absolute_import
18 from __future__ import print_function
1719
1820 import sys, getopt
1921 import json
142144 print(json.dumps(output))
143145 else:
144146 print(json.dumps("No clients connected"))
147
148
149 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
5 """
96 from faraday.client.plugins import core
107 import re
118 import os, json
2623 """
2724
2825 def __init__(self):
29 core.PluginBase.__init__(self)
26 super().__init__()
3027 self.id = "fruitywifi"
3128 self.name = "FruityWiFi"
3229 self.plugin_version = "0.0.1"
9592 macaddress = item[1]
9693 hostname = item[2]
9794 vuln_name = "FruityWiFi"
98 severity = severity
99
95
10096 desc = "Client ip: " + ip_address + \
10197 " has been connected to FruityWiFi\n"
10298 desc += "More information:"
141137
142138 def createPlugin():
143139 return FruityWiFiPlugin()
140
141
142 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
5 """
96 from faraday.client.plugins import core
107 from faraday.client.model import api
118 import re
3128 """
3229
3330 def __init__(self):
34 core.PluginBase.__init__(self)
31 super().__init__()
3532 self.id = "ftp"
3633 self.name = "Ftp"
3734 self.plugin_version = "0.0.1"
6360 ip_address = self.resolve(hostname)
6461 self._version = banner.groups(0) if banner else ""
6562 if debug:
66 print ip_address
63 print(ip_address)
6764
6865 h_id = self.createAndAddHost(ip_address)
6966
9693 if re.search("[\d]+", count_args[c - 1]):
9794 self._port = count_args[c - 1]
9895
99 return None
100
10196
10297 def createPlugin():
10398 return CmdFtpPlugin()
99
100
101 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
9 from __future__ import with_statement
4 """
105 from faraday.client.plugins import core
116 import socket
12 import sys
137 import re
148 import os
159
2519 __status__ = "Development"
2620
2721
28 class GoohostParser(object):
22 class GoohostParser:
2923 """
3024 The objective of this class is to parse an xml file generated by the goohost tool.
3125
3933 def __init__(self, output, goohost_scantype):
4034
4135 self.items = []
42 lines = filter(None, output.split('\n'))
36 lines = list(filter(None, output.split('\n')))
4337 for line in lines:
4438 if goohost_scantype == 'ip':
4539 data = line.split()
7973 """
8074
8175 def __init__(self):
82 core.PluginBase.__init__(self)
76 super().__init__()
8377 self.id = "Goohost"
8478 self.name = "Goohost XML Output Plugin"
8579 self.plugin_version = "0.0.1"
172166 def createPlugin():
173167 return GoohostPlugin()
174168
175 if __name__ == '__main__':
176 with open('/home/javier/Plugins/goohost/report-10071-google.com.txt','r') as report:
177 output = report.read()
178 parser = GoohostPlugin()
179 parser.parseOutputString(output)
180 for item in parser.items:
181 if item.status == 'up':
182 print item
169 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/python2.7
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
4 """
95 from faraday.client.plugins import core
106 import re
117
1814 class hping3 (core.PluginBase):
1915
2016 def __init__(self):
21
22 core.PluginBase.__init__(self)
17 super().__init__()
2318 self.id = "Hping3"
2419 self.name = "hping3"
2520 self.plugin_version = "0.0.1"
7974
8075 def createPlugin():
8176 return hping3()
77
78
79 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
9 from __future__ import with_statement
4 """
105 from faraday.client.plugins import core
116 from faraday.client.model import api
127 import re
2621 __status__ = "Development"
2722
2823
29 class HydraParser(object):
24 class HydraParser:
3025 """
3126 The objective of this class is to parse an xml file generated by the hydra tool.
3227
6055 """
6156
6257 def __init__(self):
63 core.PluginBase.__init__(self)
58 super().__init__()
6459 self.id = "Hydra"
6560 self.name = "Hydra XML Output Plugin"
6661 self.plugin_version = "0.0.1"
9893 service = item['plugin']
9994 port = item['port']
10095
101 if hosts.has_key(item['ip']) == False:
96 if item['ip'] not in hosts == False:
10297 hosts[item['ip']] = []
10398
10499 hosts[item['ip']].append([item['login'], item['password']])
105100
106 for k, v in hosts.iteritems():
101 for k, v in hosts.items():
107102
108103 h_id = self.createAndAddHost(k)
109104
175170 def createPlugin():
176171 return HydraPlugin()
177172
178 if __name__ == '__main__':
179 parser = HydraParser(sys.argv[1])
180 for item in parser.items:
181 if item.status == 'up':
182 print item
173
174 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
5 """
106 import re
117 import os
128 import sys
13 import logging
14
15 from faraday.client.plugins import core
16
17 logger = logging.getLogger(__name__)
9
10 from faraday.client.plugins.plugin import PluginXMLFormat
11
1812
1913 try:
2014 import xml.etree.cElementTree as ET
3832 __status__ = "Development"
3933
4034
41 class ImpactXmlParser(object):
35 class ImpactXmlParser:
4236 """
4337 The objective of this class is to parse an xml file generated by the impact tool.
4438
5246 def __init__(self, xml_output):
5347 tree = self.parse_xml(xml_output)
5448 if tree:
55 self.items = [data for data in self.get_items(tree)]
49 self.items = list(self.get_items(tree))
5650 else:
5751 self.items = []
5852
6862 try:
6963 tree = ET.fromstring(xml_output)
7064 except SyntaxError as err:
71 logger.error("SyntaxError: %s. %s" % (err, xml_output))
65 #logger.error("SyntaxError: %s. %s" % (err, xml_output))
7266 return None
7367
7468 return tree
8175 yield Item(node, tree)
8276
8377
84 class Item(object):
78 class Item:
8579 """
8680 An abstract representation of a Item
8781
219213 return None
220214
221215
222 class ImpactPlugin(core.PluginBase):
216 class ImpactPlugin(PluginXMLFormat):
223217 """
224218 Example plugin to parse impact output.
225219 """
226220
227221 def __init__(self):
228 core.PluginBase.__init__(self)
229 self.id = "Core Impact"
222 super().__init__()
223 self.identifier_tag = "entities"
224 self.id = "CoreImpact"
230225 self.name = "Core Impact XML Output Plugin"
231226 self.plugin_version = "0.0.2"
232227 self.version = "Core Impact 2013R1/2017R2"
320315 return ImpactPlugin()
321316
322317
323 if __name__ == '__main__':
324 parser = ImpactXmlParser(sys.argv[1])
325 for item in parser.items:
326 if item.status == 'up':
327 print(item)
318 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
4 '''
4 """
5 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
4 """
95 import csv
10 import StringIO
6 from io import StringIO
117 from faraday.client.plugins import core
128
9
1310 def calculate_severity(number):
14
1511 if number is None:
1612 return "info"
13 number = float(number)
14 # Based in CVSS V2
15 if 0 <= number < 4.0:
16 return "low"
17 elif 4.0 <= number < 7.0:
18 return "med"
19 elif 7.0 <= number <= 10:
20 return "high"
1721
18 number = float(number)
19
20 # Based in CVSS V2
21 if number >= 0 and number <= 3.9:
22 return "low"
23 elif number >= 4.0 and number <= 6.9:
24 return "med"
25 elif number >= 7.0 and number <= 10:
26 return "high"
2722
2823 class Ip360Parser:
2924
3025 def __init__(self, csv_content):
31 self.csv_content = StringIO.StringIO(csv_content.decode('ascii', 'ignore'))
26 self.csv_content = StringIO(csv_content.decode('ascii', 'ignore'))
3227 self.csv_reader = csv.DictReader(self.csv_content, delimiter=',', quotechar='"')
3328
3429 def parse(self):
7368 """
7469
7570 def __init__(self):
76 core.PluginBase.__init__(self)
71 super().__init__()
7772 self.id = "Ip360"
7873 self.name = "Ip360 CSV Output Plugin"
7974 self.plugin_version = "0.0.1"
118113 ref=vulnerability.get("ref"))
119114
120115 def createPlugin():
121 return Ip360Plugin()
116 return Ip360Plugin()
117
118 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
5 """
106 from faraday.client.plugins import core
117 from faraday.client.model import api
12 import re
138 import os
14 import pprint
15 import sys
169 from lxml import etree
1710
1811 try:
3427 __email__ = "[email protected]"
3528 __status__ = "Development"
3629
37 '''
30 """
3831 This plugin has been designed to be used with python-unittest2/paramiko script to perform security compliancy verification. It enables to have displayed both security scans results (nmap,
3932 nessus, ..) and security verification compliancy (CIS-CAT, compagny's product security requirement) by Faraday-IPE
4033
5649 </testsuite>
5750
5851
59 '''
52 """
6053
61 class JunitXmlParser(object):
54
55 class JunitXmlParser:
6256 """
6357 The objective of this class is to parse an xml file generated by the junit.
6458
6963
7064 tree = self.parse_xml(xml_output)
7165 if tree:
72 self.items = [data for data in self.get_items(tree)]
66 self.items = list(self.get_items(tree))
7367 else:
7468 self.items = []
7569
8276 try:
8377 # return ET.fromstring(xml_output)
8478 tree = etree.fromstring(xml_output)
85 except SyntaxError, err:
86 print "SyntaxError: %s. %s" % (err, xml_output)
79 except SyntaxError as err:
80 print("SyntaxError: %s. %s" % (err, xml_output))
8781 return None
8882 return tree
8983
9690 yield Testsuite(node)
9791
9892
99 class Testsuite(object):
93 class Testsuite:
10094
10195 def __init__(self, testsuite_node):
10296 self.node = testsuite_node
10599 self.name = self.parent.get('name')
106100 self.host = self.parent.get('host')
107101 if self.host is None:
108 print 'host element is missing'
102 print('host element is missing')
109103 self.host = ''
110104
111105 self.message = self.get_text_from_subnode('message')
121115 return sub_node
122116
123117 return None
124
118
119
125120 class JunitPlugin(core.PluginBase):
126121 """
127122 Example plugin to parse junit output.
128123 """
129124
130125 def __init__(self):
131 core.PluginBase.__init__(self)
126 super().__init__()
132127 self.id = "Junit"
133128 self.name = "Junit XML Output Plugin"
134129 self.plugin_version = "0.0.1"
150145 def createPlugin():
151146 return JunitPlugin()
152147
153 if __name__ == '__main__':
154 parser = JunitXmlParser(sys.argv[1])
155 for item in parser.items:
156 if item.status == 'up':
157 print item
148
149
150 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
9 from __future__ import with_statement
4 """
105 from faraday.client.plugins import core
116 from faraday.client.model import api
127 import re
138 import os
14 import sys
159
1610 current_path = os.path.abspath(os.getcwd())
1711
2317 __email__ = "[email protected]"
2418 __status__ = "Development"
2519
26 class ListurlsParser(object):
20
21 class ListurlsParser:
2722 """
2823 The objective of this class is to parse an xml file generated by the listurls tool.
2924
4641
4742 for line in lists:
4843 if i > 8:
49 print line
44 print(line)
5045 item = {'link': line}
5146 self.items.append(item)
5247 i = i + 1
5853 """
5954
6055 def __init__(self):
61 core.PluginBase.__init__(self)
56 super().__init__()
6257 self.id = "Listurls"
6358 self.name = "Listurls XML Output Plugin"
6459 self.plugin_version = "0.0.1"
10095 self.port = 443
10196 if host.group(11) is not None:
10297 self.port = host.group(11)
103 return None
10498
10599 def setHost(self):
106100 pass
109103 def createPlugin():
110104 return ListurlsPlugin()
111105
112 if __name__ == '__main__':
113 parser = ListurlsParser(sys.argv[1])
114 for item in parser.items:
115 if item.status == 'up':
116 print item
106
107 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
9 from __future__ import with_statement
4 """
105 import re
116 import os
12 import random
13 import socket
147 from collections import defaultdict
158
16 from faraday.client.plugins import core
9 from faraday.client.plugins.plugin import PluginByExtension
1710 from faraday.client.plugins.plugins_utils import filter_services, get_all_protocols
1811
1912
120113 # self.aux_items will be an auxiliar list. We will use it...
121114 # ...for poping the url and the protocol so that the last element...
122115 # ... of the list, will be the name of the service
123 self.aux_items = filter(None, items_service)
116 self.aux_items = list(filter(None, items_service))
124117 elements_ip_port, count = self.get_ip_and_port(self.aux_items, remove_from_list=True)
125118 protocol = self.get_protocol()
126119 name = self.aux_items[0]
239232 return(warns)
240233
241234
242 class LynisPlugin(core.PluginBase):
235 class LynisPlugin(PluginByExtension):
243236 """ Simple example plugin to parse lynis' lynis-report.dat file."""
244237
245238 def __init__(self):
246 core.PluginBase.__init__(self)
239 super().__init__()
240 self.extension = [".dat", ".log"]
247241 self.id = "Lynis"
248242 self.name = "Lynis DAT Output Plugin"
249243 self.plugin_version = "0.4"
255249 self._hosts = []
256250
257251 global current_path
252
253 def report_belongs_to(self, **kwargs):
254 if super().report_belongs_to(**kwargs):
255 report_path = kwargs.get("report_path", "")
256 with open(report_path) as f:
257 output = f.read()
258 return output.startswith("# Lynis Report")
259 return False
258260
259261 def parseOutputString(self, output, debug=False):
260262 datpath = self.getDatPath(output)
306308 ports=[service_data['port']])
307309
308310 def create_vulns_with_kernel(self, host_id, kernel_versions):
309 for kernel, version in kernel_versions.iteritems():
311 for kernel, version in kernel_versions.items():
310312 self.createAndAddVulnToHost(
311313 host_id=host_id,
312314 name=kernel,
350352
351353 def createPlugin():
352354 return LynisPlugin()
355
356
357 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
30 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74 """
8
9 from __future__ import with_statement
10 from faraday.client.plugins import core
5 from faraday.client.plugins.plugin import PluginXMLFormat
116
127 import zipfile
13 import sys
148 import re
159 import os
1610
4236 xml = ET.parse(file.open('Graphs/Graph1.graphml'))
4337
4438 except:
45 print "Bad report format"
39 print("Bad report format")
4640 return None
4741
4842 file.close()
303297 return self.list_hosts
304298
305299
306 class MaltegoPlugin(core.PluginBase):
300 class MaltegoPlugin(PluginXMLFormat):
307301
308302 def __init__(self):
309
310 core.PluginBase.__init__(self)
303 super().__init__()
304 self.identifier_tag = "maltego"
311305 self.id = "Maltego"
312306 self.name = "Maltego MTGX Output Plugin"
313307 self.plugin_version = "1.0.1"
445439
446440 def createPlugin():
447441 return MaltegoPlugin()
442
443
444 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
4 '''
5
4 """
65 from faraday.client.plugins.repo.nmap.plugin import NmapPlugin
76 import os
87 import re
5655
5756
5857 def createPlugin():
59 return CmdMasscanPlugin()
58 return CmdMasscanPlugin()
59
60 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
9 from __future__ import with_statement
4 """
105 from faraday.client.plugins import core
116 from faraday.client.model import api
127 import re
2722 __status__ = "Development"
2823
2924
30 class MedusaParser(object):
25 class MedusaParser:
3126 """
3227 The objective of this class is to parse an xml file generated by the medusa tool.
3328
4944 "ACCOUNT FOUND: \[([^$]+)\] Host: ([^$]+) User: ([^$]+) Password: ([^$]+) \[SUCCESS\]",
5045 l)
5146
52 print "REG" + str(reg)
47 print("REG" + str(reg))
5348
5449 if reg:
5550
5954 'user': reg.group(3),
6055 'pass': reg.group(4)}
6156
62 print "ITEM" + str(item)
57 print("ITEM" + str(item))
6358 item['ip'] = self.getAddress(item['host'])
6459 item['port'] = self.srv[item['service']]
65 print "ITEM" + str(item)
60 print("ITEM" + str(item))
6661 self.items.append(item)
6762
6863 def getAddress(self, hostname):
7166 """
7267 try:
7368 return socket.gethostbyname(hostname)
74 except socket.error, msg:
69 except socket.error as msg:
7570 return hostname
7671
7772
8176 """
8277
8378 def __init__(self):
84 core.PluginBase.__init__(self)
79 super().__init__()
8580 self.id = "Medusa"
8681 self.name = "Medusa Output Plugin"
8782 self.plugin_version = "0.0.1"
187182 def createPlugin():
188183 return MedusaPlugin()
189184
190 if __name__ == '__main__':
191 parser = MedusaParser(sys.argv[1])
192 for item in parser.items:
193 if item.status == 'up':
194 print item
185
186 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
5 """
106 from faraday.client.plugins import core
117 from faraday.client.model import api
128 import re
139 import os
1410 import socket
15 import pprint
1611 import sys
1712
1813
2823 __status__ = "Development"
2924
3025
31 class MetagoofilParser(object):
26 class MetagoofilParser:
3227 """
3328 The objective of this class is to parse an xml file generated by the metagoofil tool.
3429
7570 """
7671
7772 def __init__(self):
78 core.PluginBase.__init__(self)
73 super().__init__()
7974 self.id = "Metagoofil"
8075 self.name = "Metagoofil XML Output Plugin"
8176 self.plugin_version = "0.0.1"
122117 def createPlugin():
123118 return MetagoofilPlugin()
124119
125 if __name__ == '__main__':
126 parser = MetagoofilParser(sys.argv[1])
127 for item in parser.items:
128 if item.status == 'up':
129 print item
120
121 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
9 from __future__ import with_statement
10 from faraday.client.plugins import core
4 """
5 from faraday.client.plugins.plugin import PluginXMLFormat
116 from faraday.client.model import api
127 import re
138 import os
3530 __status__ = "Development"
3631
3732
38 class MetasploitXmlParser(object):
33 class MetasploitXmlParser:
3934 """
4035 The objective of this class is to parse an xml file generated by the metasploit tool.
4136
4843
4944 def __init__(self, xml_output):
5045 tree = self.parse_xml(xml_output)
51
5246 if tree:
5347 servicesByWebsite = {}
5448 for site in tree.findall('web_sites/web_site'):
55
56 servicesByWebsite[site.find('id').text] = site.find(
57 'service-id').text
58
49 servicesByWebsite[site.find('id').text] = site.find('service-id').text
5950 webVulnsByService = {}
60 for v in [data for data in self.get_vulns(tree, servicesByWebsite)]:
51 for v in self.get_vulns(tree, servicesByWebsite):
6152 if v.service_id not in webVulnsByService:
6253 webVulnsByService[v.service_id] = []
6354 webVulnsByService[v.service_id].append(v)
6455
65 self.hosts = [data for data in self.get_items(
56 self.hosts = list(self.get_items(
6657 tree,
67 webVulnsByService)]
58 webVulnsByService))
6859 else:
6960 self.hosts = []
7061
7970 """
8071 try:
8172 tree = ET.fromstring(xml_output)
82 except SyntaxError, err:
83 print "SyntaxError: %s. %s" % (err, xml_output)
73 except SyntaxError as err:
74 print("SyntaxError: %s. %s" % (err, xml_output))
8475 return None
8576
8677 return tree
136127 return None
137128
138129
139 class Host(object):
130 class Host:
140131
141132 def __init__(self, item_node, webVulnsByService):
142133 self.node = item_node
166157 self.vulnsByService[service['id']] = []
167158 self.notesByService[service['id']] = []
168159 if service['id'] in webVulnsByService:
169 self.vulnsByService[service['id']
170 ] += webVulnsByService[service['id']]
160 self.vulnsByService[service['id']] += webVulnsByService[service['id']]
171161
172162 for v in self.node.findall('vulns/vuln'):
173163 vuln = HostVuln(v)
206196 return None
207197
208198
209 class WebVuln(object):
199 class WebVuln:
210200
211201 def __init__(self, item_node, services):
212202 self.node = item_node
241231 return ""
242232
243233
244 class HostNote(object):
234 class HostNote:
245235 """
246236 An abstract representation of a HostNote
247237
271261 return ""
272262
273263
274 class HostCred(object):
264 class HostCred:
275265 """
276266 An abstract representation of a HostNote
277267
301291 return ""
302292
303293
304 class HostVuln(object):
294 class HostVuln:
305295 """
306296 An abstract representation of a HostVuln
307297
333323 return ""
334324
335325
336 class MetasploitPlugin(core.PluginBase):
326 class MetasploitPlugin(PluginXMLFormat):
337327 """
338328 Example plugin to parse metasploit output.
339329 """
340330
341331 def __init__(self):
342 core.PluginBase.__init__(self)
332 super().__init__()
333 self.identifier_tag = ["MetasploitV4", "MetasploitV5"]
343334 self.id = "Metasploit"
344335 self.name = "Metasploit XML Output Plugin"
345336 self.plugin_version = "0.0.1"
348339 self.options = None
349340 self._current_output = None
350341 self.target = None
351 self._command_regex = re.compile(
352 r'^(metasploit|sudo metasploit|\.\/metasploit).*?')
342 self._command_regex = re.compile(r'^(metasploit|sudo metasploit|\.\/metasploit).*?')
353343
354344 global current_path
355 self._output_file_path = os.path.join(self.data_path,
356 "metasploit_output-%s.xml" % self._rid)
345 self._output_file_path = os.path.join(self.data_path, "metasploit_output-%s.xml" % self._rid)
357346
358347 def parseOutputString(self, output, debug=False):
359348 """
400389 self.createAndAddVulnToService(h_id, s_id, "Weak Credentials", "[metasploit found the following credentials]\nuser:%s\npass:%s" % (
401390 c.user, c.passwd), severity="high")
402391
403 n_id = None
404392 for v in item.vulnsByService[s['id']]:
405393 if v.isWeb:
406 if n_id == None:
407 n_id = self.createAndAddNoteToService(
408 h_id, s_id, "website", "")
409 n2_id = self.createAndAddNoteToNote(
410 h_id, s_id, n_id, v.host, "")
411394 v_id = self.createAndAddVulnWebToService(h_id, s_id, v.name, v.desc,
412395 severity=v.risk, website=v.host,
413396 path=v.path, request=v.request, method=v.method,
435418 def createPlugin():
436419 return MetasploitPlugin()
437420
438 if __name__ == '__main__':
439 parser = MetasploitXmlParser(sys.argv[1])
440 for item in parser.items:
441 if item.status == 'up':
442 print item
421
422 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
5 """
106 from faraday.client.plugins import core
117 from faraday.client.model import api
128 import re
3834 """
3935
4036 def __init__(self):
41
42 core.PluginBase.__init__(self)
37 super().__init__()
4338 self.id = "MetasploitOn"
4439 self.name = "Metasploit Online Service Plugin"
4540 self.plugin_version = "0.0.3"
8479
8580 cur = conn.cursor()
8681 except Exception as e:
87 print "[Faraday - MetasplotiOn] Error Connecting to the database"
88 print "[Faraday - MetasplotiOn]Check your metasploit postgresql credentials and server IP/Port"
89 print e
82 print("[Faraday - MetasplotiOn] Error Connecting to the database")
83 print("[Faraday - MetasplotiOn]Check your metasploit postgresql credentials and server IP/Port")
84 print(e)
9085 return
9186
9287 cur = self._doSql(
9388 cur,
9489 "select * from hosts inner join workspaces ON (hosts.workspace_id=workspaces.id) where workspaces.name like '" + self.getSetting("Workspace") + "';")
9590 if cur is None:
96 print "Error getting database data\n"
91 print("Error getting database data\n")
9792 return
9893
9994 self.path = self.data_path + "/" + api.getActiveWorkspace().name + \
311306 "select * from web_sites where service_id=" + str(s[0]) + self._mwhere)
312307
313308 for w in cur.fetchall():
314
315309 self._checkDate(str(w[3]))
316
317 n_id = self.createAndAddNoteToService(
318 h_id,
319 s_id,
320 "website",
321 "")
322
323 n2_id = self.createAndAddNoteToNote(
324 h_id,
325 s_id,
326 n_id,
327 str(w[4]),
328 "")
329310
330311 cur.close()
331312 conn.close()
334315 try:
335316 api.devlog("SQL:" + sql)
336317 db.execute(sql)
337 except Exception, e:
318 except Exception as e:
338319 print ("Error SQL[" + e.pgcode + "] - " + e.pgerror)
339320 return None
340321
397378 def createPlugin():
398379 return MetasploitOnPlugin()
399380
400 if __name__ == '__main__':
401 parser = MetasploitOnXmlParser(sys.argv[1])
402 for item in parser.items:
403 if item.status == 'up':
404 print item
381 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
5 """
96 from faraday.client.plugins import core
107 from faraday.client.model import api
118 import re
4542
4643 try:
4744 return ET.fromstring(xmlOutput)
48 except SyntaxError, err:
49 print "SyntaxError: %s" % err
45 except SyntaxError as err:
46 print("SyntaxError: %s" % err)
5047 return None
5148
5249 def getHostsDiffs(self, tree):
113110 """
114111
115112 def __init__(self):
116 core.PluginBase.__init__(self)
113 super().__init__()
117114 self.id = "Ndiff"
118115 self.name = "ndiff"
119116 self.plugin_version = "0.0.1"
169166
170167 def createPlugin():
171168 return CmdNdiffPlugin()
169
170
171 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
1919 # Place, Suite 330, Boston, MA 02111-1307 USA
2020 #
2121 # 2011-03-12: 0.1.1: Initial version.
22 from __future__ import absolute_import
23
2224 import sys
2325 import re
2426 import xml.etree.ElementTree as ET
2527 from datetime import datetime
26 from StringIO import StringIO
28 from io import StringIO, BytesIO
2729
2830
2931 # List all nodes in a ReportItem object that can have multiple values
7476 ]
7577
7678
77 class Report(object):
79 class Report:
7880
7981 def __init__(self):
8082 self.name = None
8789 # Parse XML file
8890 #getLogger(self).debug("Parsing report start")
8991 if from_string:
90 xml_file = StringIO(xml_file)
92 try:
93 xml_file = BytesIO(xml_file)
94 except Exception as e1:
95 try:
96 xml_file = StringIO(xml_file)
97 except Exception as e2:
98 raise Exception(str(e1) + "\n" + str(e2))
9199
92100 # Iterate through each host scanned and create objects for each
93101 for event, elem in ET.iterparse(xml_file):
96104 # Grab the report name from the Report element
97105 if event == 'end' and elem.tag == 'Report':
98106 self.name = elem.attrib.get('name')
99 continue
100107
101108 # Only process ReportHost elements
102109 elif event == 'end' and elem.tag != 'ReportHost':
127134 return t
128135
129136
130 class ReportHost(object):
137 class ReportHost:
131138
132139 def __init__(self, xml_report_host):
133140 self.name = None
196203 # Check to see if named fields were given
197204 if res.groupdict():
198205 # Store each named field as an attribute
199 for k, v in res.groupdict().iteritems():
206 for k, v in res.groupdict().items():
200207 setattr(self, k, v)
201208
202209 # No named fields, just grab whatever matched
277284 return self.name
278285
279286
280 class ReportItem(object):
287 class ReportItem:
281288
282289 def __init__(self, xml_report_item):
283290 # Make sure object is well formed
320327 return getattr(self, attr)
321328 except AttributeError:
322329 return None
330 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
10 import logging
11 from faraday.client.plugins import core
12 from faraday.client.model import api
5 """
6
7 from faraday.client.plugins.plugin import PluginXMLFormat
138 import re
149 import os
1510 import socket
16 import pprint
17 import sys
18 import dotnessus_v2
19
20 logger = logging.getLogger(__name__)
11
12 import faraday.client.plugins.repo.nessus.dotnessus_v2 as dotnessus_v2
13
14
2115
2216 current_path = os.path.abspath(os.getcwd())
2317
3125 __status__ = "Development"
3226
3327
34 class NessusParser(object):
28 class NessusParser:
3529 """
3630 The objective of this class is to parse an xml file generated by the nessus tool.
3731
5751 i = i + 1
5852
5953
60 class NessusPlugin(core.PluginBase):
54 class NessusPlugin(PluginXMLFormat):
6155 """
6256 Example plugin to parse nessus output.
6357 """
6458
6559 def __init__(self):
66 core.PluginBase.__init__(self)
60 super().__init__()
61 self.extension = ".nessus"
62 self.identifier_tag = "NessusClientData_v2"
6763 self.id = "Nessus"
6864 self.name = "Nessus XML Output Plugin"
6965 self.plugin_version = "0.0.1"
8076 self.fail = None
8177
8278 global current_path
83 self.output_path = os.path.join(self.data_path,
84 "nessus_output-%s.txt" % self._rid)
79 self.output_path = os.path.join(self.data_path, "nessus_output-%s.txt" % self._rid)
8580
8681 def canParseCommandString(self, current_input):
8782 if self._command_regex.match(current_input.strip()):
10196 try:
10297 p.parse(output, from_string=True)
10398 except Exception as e:
104 logger.error("Exception - %s", e)
99 self.logger.error("Exception - %s", e)
105100
106101 for t in p.targets:
107102 mac = ""
137132 external_id = v.get('plugin_id')
138133
139134 desc = ""
140 desc += v.get('description').encode("ascii",
141 errors="backslashreplace") if v.get('description') else ""
135 desc += v.get('description') if v.get('description') else ""
142136 resolution = ""
143 resolution = v.get('solution').encode(
144 "ascii", errors="backslashreplace") if v.get('solution') else ""
145
146 data = "\nOutput: " + v.get('plugin_output').encode(
147 "ascii", errors="backslashreplace") if v.get('plugin_output') else ""
137 resolution = v.get('solution') if v.get('solution') else ""
138
139 data = "\nOutput: " + v.get('plugin_output') if v.get('plugin_output') else ""
148140
149141 ref = []
150142 if v.get('cve'):
151143 cves = v.get('cve')
152144 for cve in cves:
153 logger.debug('Appending %s', cve.encode("utf-8"))
145 #logger.debug('Appending %s', cve.encode("utf-8"))
154146 ref.append(cve.encode("utf-8").strip())
155147 if v.get('bid'):
156148 bids = v.get('bid')
157149 for bid in bids:
158 logger.debug('Appending %s', bid.encode("utf-8"))
150 #logger.debug('Appending %s', bid.encode("utf-8"))
159151 ref.append("BID-%s" % bid.encode("utf-8").strip() )
160152 if v.get('cvss_base_score'):
161153 ref.append("CVSS: " + ", ".join(v.get('cvss_base_score')))
173165 str(v.get('port'))],
174166 status="open")
175167
176 web = True if re.search(
177 r'^(www|http)', v.get('svc_name')) else False
178 if srv.has_key(v.get('svc_name')) == False:
168 web = re.search(r'^(www|http)', v.get('svc_name'))
169 if v.get('svc_name') in srv:
179170 srv[v.get('svc_name')] = 1
180 if web:
181 n_id = self.createAndAddNoteToService(
182 h_id, s_id, "website", "")
183 n2_id = self.createAndAddNoteToNote(
184 h_id, s_id, n_id, host, "")
185171
186172 if web:
187173 v_id = self.createAndAddVulnWebToService(h_id, s_id, v.get('plugin_name'),
215201 def createPlugin():
216202 return NessusPlugin()
217203
218 if __name__ == '__main__':
219 parser = NessusPlugin()
220 with open('/home/dnadares/report-collection/latest_reports/DNS_publicos_hlnn77.nessus', 'r') as report:
221 parser.parseOutputString(report.read())
222 for item in parser.items:
223 if item.status == 'up':
224 print item
204 # I'm Py3
1919 #
2020 # 2010-08-12: 0.1.0: Initial version.
2121 # 2011-03-12: 0.2.1: Added a bunch of methods and robustified everything.
22 from __future__ import absolute_import
23 from __future__ import print_function
24
2225 import sys
23 import urllib2
24 from urlparse import urljoin
25 from urllib import quote
26 try:
27 from urllib2 import ProxyHandler, build_opener, urlopen, install_opener
28 from urlparse import urljoin
29 from urllib import quote
30 except ImportError:
31 from urllib.request import ProxyHandler, build_opener, urlopen, install_opener
32 from urllib.parse import urljoin, quote
33
2634 import xml.etree.ElementTree as ET
2735 import re
2836 import datetime
4351 ]
4452
4553
46 class NessusServer(object):
54 class NessusServer:
4755
4856 def __init__(self, server, port, username, password, verbose=False):
4957 self.server = server
5563 self.launched_scans = {}
5664
5765 # Force urllib2 to not use a proxy
58 hand = urllib2.ProxyHandler({})
59 opener = urllib2.build_opener(hand)
60 urllib2.install_opener(opener)
66 hand = ProxyHandler({})
67 opener = build_opener(hand)
68 install_opener(opener)
6169
6270 self.login()
6371
7987 data = make_args(login=self.username, password=quote(self.password))
8088 resp = self._call('login', data)
8189 if self.verbose:
82 print resp
90 print(resp)
8391
8492 # Parse token
8593 seq, status, parsed = parse_reply(resp, ['token'])
132140 else:
133141 data = make_args(token=self.token, report=uuid)
134142 url = urljoin(self.base_url, 'file/report/download/?%s' % data)
135 req = urllib2.urlopen(url)
143 req = urlopen(url)
136144 resp = req.read()
137145 if not check_auth(resp):
138 print >> sys.stderr, "Unauthorized"
146 print("Unauthorized", file=sys.stderr)
139147 return None
140148 return resp
141149
146154 name), policy_id=policy_id, target=arg_targets)
147155 resp = self._call('/scan/new', data)
148156 if self.verbose:
149 print resp
157 print(resp)
150158
151159 # Get parsed data
152160 keys = ['uuid', 'owner', 'start_time', 'scan_name']
172180 def get_policy_id(self, policy_name):
173181 """Attempts to grab the policy ID for a name"""
174182 pols = self.list_policies()
175 for k, v in pols.iteritems():
183 for k, v in pols.items():
176184 if v.get('policyName').lower() == policy_name:
177185 return k
178186
271279 def _call(self, func_url, args):
272280 url = urljoin(self.base_url, func_url)
273281 if self.verbose:
274 print "URL: '%s'" % url
275 print "POST: '%s'" % args
276 req = urllib2.urlopen(url, args)
282 print("URL: '%s'" % url)
283 print("POST: '%s'" % args)
284 req = urlopen(url, args)
277285 resp = req.read()
278286 if not check_auth(resp):
279 print >> sys.stderr, "200 Unauthorized"
287 print("200 Unauthorized", file=sys.stderr)
280288 return resp
281289 return resp
282290
571579 # computer.
572580 memset = ctypes.CDLL("libc.so.6").memset
573581
574 print "Clearing 0x%08x size %i bytes" % (location, size)
582 print("Clearing 0x%08x size %i bytes" % (location, size))
575583
576584 memset(location, 0, size)
585
586
587 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
5 """
96 from faraday.client.plugins import core
107 from faraday.client.model import api
118 import re
2926 """
3027
3128 def __init__(self):
32 core.PluginBase.__init__(self)
29 super().__init__()
3330 self.id = "netcat"
3431 self.name = "Netcat"
3532 self.plugin_version = "0.0.1"
108105 We take a split & filter approach to matching our regexps to the
109106 command output
110107 """
111 mapped_list = map(lambda s: re.search(regexp, s),
112 re.split(r'(\r|\n)', output))
113 filtered_list = filter(lambda s: s is not None, mapped_list)
108 mapped_list = list(map(lambda s: re.search(regexp, s), re.split(r'(\r|\n)', output)))
109 filtered_list = list(filter(lambda s: s is not None, mapped_list))
114110
115111 if len(filtered_list) > 0:
116112 return filtered_list[0]
123119 'traditional' version. The verbose output differs between them, so we
124120 will try to cover both cases.
125121 """
126 print output
122 print(output)
127123 nc_bsd_rx = re.compile(
128124 r'^Connection\s+to\s+(?P<host>\S+)\s+(?P<port>\d+)\s+port\s+\[(?P<protocol>tcp|udp)/(?P<service>[^\]]+)\]\s+succeeded.*')
129125 nc_sys_rx = re.compile(
154150
155151 def createPlugin():
156152 return CmdNetcatPlugin()
153
154
155 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
4 '''
4 """
5 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
4 """
95 from faraday.client.plugins import core
106 import pprint
117 import re
2117 class NetdiscoverPlugin(core.PluginBase):
2218
2319 def __init__(self):
24 core.PluginBase.__init__(self)
20 super().__init__()
2521 self.id = "Netdiscover"
2622 self.name = "netdiscover"
2723 self.plugin_version = "0.0.1"
5046
5147 def createPlugin():
5248 return NetdiscoverPlugin()
49
50
51 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
10 from faraday.client.plugins import core
5 """
6 from faraday.client.plugins.plugin import PluginXMLFormat
117 from faraday.client.model import api
128 import re
139 import os
3834 __status__ = "Development"
3935
4036
41 class NetsparkerXmlParser(object):
37 class NetsparkerXmlParser:
4238 """
4339 The objective of this class is to parse an xml file generated by the netsparker tool.
4440
5450
5551 tree = self.parse_xml(xml_output)
5652 if tree:
57 self.items = [data for data in self.get_items(tree)]
53 self.items = list(self.get_items(tree))
5854 else:
5955 self.items = []
6056
6965 """
7066 try:
7167 tree = ET.fromstring(xml_output)
72 except SyntaxError, err:
68 except SyntaxError as err:
7369 self.devlog("SyntaxError: %s. %s" % (err, xml_output))
7470 return None
7571
8379 yield Item(node)
8480
8581
86 class Item(object):
82 class Item:
8783 """
8884 An abstract representation of a Item
8985
9692 return "high"
9793 return severity
9894
99 def __init__(self, item_node):
95 def __init__(self, item_node, encoding="ascii"):
10096 self.node = item_node
10197 self.url = self.get_text_from_subnode("url")
10298
124120 self.request = self.get_text_from_subnode("rawrequest")
125121 self.response = self.get_text_from_subnode("rawresponse")
126122 if self.response:
127 self.response = self.response.encode("ascii",errors="backslashreplace")
123 self.response = self.response.encode(encoding,errors="backslashreplace").decode(encoding)
128124 if self.request:
129 self.request = self.request.encode("ascii",errors="backslashreplace")
125 self.request = self.request.encode(encoding,errors="backslashreplace").decode(encoding)
130126 if self.reference:
131 self.reference = self.reference.encode("ascii",errors="backslashreplace")
127 self.reference = self.reference.encode(encoding,errors="backslashreplace").decode(encoding)
132128
133129
134130 self.kvulns = []
189185 return None
190186
191187
192 class NetsparkerPlugin(core.PluginBase):
188 class NetsparkerPlugin(PluginXMLFormat):
193189 """
194190 Example plugin to parse netsparker output.
195191 """
196192
197193 def __init__(self):
198 core.PluginBase.__init__(self)
194 super().__init__()
195 self.identifier_tag = "netsparker"
199196 self.id = "Netsparker"
200197 self.name = "Netsparker XML Output Plugin"
201198 self.plugin_version = "0.0.1"
207204 r'^(sudo netsparker|\.\/netsparker).*?')
208205
209206 global current_path
210 self._output_file_path = os.path.join(self.data_path,
211 "netsparker_output-%s.xml" % self._rid)
207 self._output_file_path = os.path.join(self.data_path, "netsparker_output-%s.xml" % self._rid)
212208
213209 def resolve(self, host):
214210 try:
242238 def processCommandString(self, username, current_path, command_string):
243239 return None
244240
241
245242 def createPlugin():
246243 return NetsparkerPlugin()
247244
248 if __name__ == '__main__':
249 parser = NetsparkerXmlParser(sys.argv[1])
250 for item in parser.items:
251 if item.status == 'up':
252 print item
245
246
247 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
10 from faraday.client.plugins import core
5 """
6 from faraday.client.plugins.plugin import PluginXMLFormat
117 from faraday.client.model import api
128 import re
139 import os
1410 import sys
1511 import socket
16 import urllib
1712
1813 try:
1914 import xml.etree.cElementTree as ET
5752 return urls
5853
5954
60 class NetsparkerCloudXmlParser(object):
55 class NetsparkerCloudXmlParser:
6156 """
6257 The objective of this class is to parse an xml file generated by the netsparkercloud tool.
6358
7368
7469 tree = self.parse_xml(xml_output)
7570 if tree:
76 self.items = [data for data in self.get_items(tree)]
71 self.items = list(self.get_items(tree))
7772 else:
7873 self.items = []
7974
8883 """
8984 try:
9085 tree = ET.fromstring(xml_output)
91 except SyntaxError, err:
86 except SyntaxError as err:
9287 self.devlog("SyntaxError: %s. %s" % (err, xml_output))
9388 return None
9489
10297 yield Item(node)
10398
10499
105 class Item(object):
100 class Item:
106101 """
107102 An abstract representation of a Item
108103
215210 return ""
216211
217212
218 class NetsparkerCloudPlugin(core.PluginBase):
213 class NetsparkerCloudPlugin(PluginXMLFormat):
219214 """
220215 Example plugin to parse netsparkercloud output.
221216 """
222217
223218 def __init__(self):
224 core.PluginBase.__init__(self)
219 super().__init__()
220 self.identifier_tag = "netsparker-cloud"
225221 self.id = "NetsparkerCloud"
226222 self.name = "NetsparkerCloud XML Output Plugin"
227223 self.plugin_version = "0.0.1"
259255 ports=[str(i.port)],
260256 status="open")
261257
262 n_id = self.createAndAddNoteToService(
263 h_id, s_id, "website", "")
264 n2_id = self.createAndAddNoteToNote(
265 h_id, s_id, n_id, i.hostname, "")
266258 first = False
267259
268260 v_id = self.createAndAddVulnWebToService(h_id, s_id, i.name, ref=i.ref, website=i.hostname,
281273 def createPlugin():
282274 return NetsparkerCloudPlugin()
283275
284 if __name__ == '__main__':
285 parser = NetsparkerCloudXmlParser(sys.argv[1])
286 for item in parser.items:
287 if item.status == 'up':
288 print item
276
277 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
10 from faraday.client.plugins import core
5 """
6 from faraday.client.plugins.plugin import PluginXMLFormat
117 from faraday.client.model import api
128 import re
139 import os
14 import pprint
1510 import sys
1611
1712 try:
3732 __status__ = "Development"
3833
3934
40 class NexposeFullXmlParser(object):
35 class NexposeFullXmlParser:
4136 """
4237 The objective of this class is to parse Nexpose's XML 2.0 Report.
4338
6863 """
6964 try:
7065 tree = ET.fromstring(xml_output)
71 except SyntaxError, err:
72 print "SyntaxError: %s. %s" % (err, xml_output)
66 except SyntaxError as err:
67 print("SyntaxError: %s. %s" % (err, xml_output))
7368 return None
7469
7570 return tree
7671
77 def parse_html_type(self, node):
72 def parse_html_type(self, node: ET.Element) -> str:
7873 """
7974 Parse XML element of type HtmlType
8075
8176 @return ret A string containing the parsed element
8277 """
8378 ret = ""
84 tag = node.tag.lower()
79 tag: str = node.tag.lower()
8580 if tag == 'containerblockelement':
8681 if len(list(node)) > 0:
8782 for child in list(node):
8883 ret += self.parse_html_type(child)
8984 else:
90 ret += node.text.encode(
91 "ascii", errors="backslashreplace").strip() if node.text else ""
85 ret += node.text.strip() if node.text else ""
9286 if tag == 'listitem':
9387 if len(list(node)) > 0:
9488 for child in list(node):
9589 ret += self.parse_html_type(child)
9690 else:
97 ret = node.text.encode(
98 "ascii", errors="backslashreplace").strip() if node.text else ""
91 ret = node.text.strip() if node.text else ""
9992 if tag == 'orderedlist':
10093 i = 1
10194 for item in list(node):
10699 for child in list(node):
107100 ret += self.parse_html_type(child)
108101 else:
109 ret += node.text.encode("ascii",
110 errors="backslashreplace") if node.text else ""
102 ret += node.text.strip() if node.text else ""
111103 if tag == 'unorderedlist':
112104 for item in list(node):
113105 ret += "\t" + "* " + self.parse_html_type(item) + "\n"
114106 if tag == 'urllink':
115107 if node.get('text'):
116 ret += node.text.encode("ascii",
117 errors="backslashreplace").strip() + " "
108 ret += node.text.strip() + " "
118109 last = ""
119110 for attr in node.attrib:
120111 if node.get(attr) and node.get(attr) != node.get(last):
121 ret += node.get(attr).encode("ascii",
122 errors="backslashreplace") + " "
112 ret += node.get(attr) + " "
123113 last = attr
124114
125115 return ret
182172 "ascii", errors="backslashreplace").strip()
183173 link = exploit.get('link').encode(
184174 "ascii", errors="backslashreplace").strip()
185 vuln['refs'].append(title + ' ' + link)
175 vuln['refs'].append(title + b' ' + link)
186176 if item.tag == 'references':
187177 for ref in list(item):
188178 if ref.text:
253243 return hosts
254244
255245
256 class NexposeFullPlugin(core.PluginBase):
246 class NexposeFullPlugin(PluginXMLFormat):
257247 """
258248 Example plugin to parse nexpose output.
259249 """
260250
261251 def __init__(self):
262 core.PluginBase.__init__(self)
252 super().__init__()
253 self.identifier_tag = "NexposeReport"
263254 self.id = "NexposeFull"
264255 self.name = "Nexpose XML 2.0 Report Plugin"
265256 self.plugin_version = "0.0.1"
344335 def createPlugin():
345336 return NexposeFullPlugin()
346337
347 if __name__ == '__main__':
348 if len(sys.argv) == 2:
349 xml_file = sys.argv[1]
350 if os.path.isfile(xml_file):
351 with open(xml_file) as f:
352 parser = NexposeFullXmlParser(f.read())
353 for item in parser.items:
354 print "* {0} ({1}) - Vulns: {2}".format(item['name'], item['os'], len(item['vulns']))
355 for vuln in item['vulns']:
356 print "- {0} (Severity: {1})".format(vuln['name'], vuln['severity'])
357 else:
358 print "File (%s) not found" % xml_file
359 else:
360 print "Usage: {0} XML_FILE".format(sys.argv[0])
338
339 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 '''
0 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
6 '''
7
8 from __future__ import with_statement
9 from faraday.client.plugins import core, plugin_utils
4 """
5 from faraday.client.plugins import plugin_utils
6 from faraday.client.plugins.plugin import PluginXMLFormat
107 import re
118 import os
129 import sys
1310 import random
14 import HTMLParser
11 from html.parser import HTMLParser
1512
1613 try:
1714 import xml.etree.cElementTree as ET
3532 __status__ = "Development"
3633
3734
38 class NiktoXmlParser(object):
35 class NiktoXmlParser:
3936 """
4037 The objective of this class is to parse an xml file generated by the nikto tool.
4138
5148 tree = self.parse_xml(xml_output)
5249
5350 if tree:
54 self.hosts = [host for host in self.get_hosts(tree)]
51 self.hosts = list(self.get_hosts(tree))
5552 else:
5653 self.hosts = []
5754
6663 """
6764 try:
6865 tree = ET.fromstring(xml_output)
69 except SyntaxError, err:
70 print "SyntaxError: %s. %s" % (err, xml_output)
66 except SyntaxError as err:
67 print("SyntaxError: %s. %s" % (err, xml_output))
7168 return None
7269
7370 return tree
119116 return None
120117
121118
122 class Item(object):
119 class Item:
123120 """
124121 An abstract representation of a Item
125122
210207 self.ipv4_address, self.mac_address, self.os, ports)
211208
212209
213 class Host(object):
210 class Host:
214211 """
215212 An abstract representation of a Host
216213
227224 self.starttime = self.node.get('starttime')
228225 self.sitename = self.node.get('sitename')
229226 self.siteip = self.node.get('hostheader')
230 self.items = [item for item in self.get_items()]
227 self.items = list(self.get_items())
231228
232229 def get_items(self):
233230 """
247244 self.ipv4_address, self.mac_address, self.os, ports)
248245
249246
250 class NiktoPlugin(core.PluginBase):
247 class NiktoPlugin(PluginXMLFormat):
251248 """
252249 Example plugin to parse nikto output.
253250 """
254251
255252 def __init__(self):
256
257 core.PluginBase.__init__(self)
253 super().__init__()
254 self.identifier_tag = "niktoscan"
258255 self.id = "Nikto"
259256 self.name = "Nikto XML Output Plugin"
260257 self.plugin_version = "0.0.2"
383380 def createPlugin():
384381 return NiktoPlugin()
385382
386 if __name__ == '__main__':
387 parser = NiktoXmlParser(sys.argv[1])
388 for item in parser.items:
389 if item.status == 'up':
390 print item
383
384 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
10 from faraday.client.plugins import core
5 """
6
7 from faraday.client.plugins.plugin import PluginXMLFormat
118 import re
129 import os
1310 import sys
1411 import random
15 import logging
16
17 logger = logging.getLogger(__name__)
12
1813
1914 try:
2015 import xml.etree.cElementTree as ET
2924 current_path = os.path.abspath(os.getcwd())
3025
3126
32 class NmapXmlParser(object):
27 class NmapXmlParser:
3328 """
3429 The objective of this class is to parse an xml file generated by
3530 the nmap tool.
4641 tree = self.parse_xml(xml_output)
4742
4843 if tree:
49 self.hosts = [host for host in self.get_hosts(tree)]
44 self.hosts = list(self.get_hosts(tree))
5045 else:
5146 self.hosts = []
5247
6257
6358 try:
6459 return ET.fromstring(xml_output)
65 except SyntaxError, err:
66 logger.error("SyntaxError: %s." % (err))
60 except SyntaxError as err:
61 #logger.error("SyntaxError: %s." % (err))
6762 return None
6863
6964 def get_hosts(self, tree):
111106 return None
112107
113108
114 class Host(object):
109 class Host:
115110 """
116111 An abstract representation of a Host
117112
136131 self.ipv4_address = self.get_ipv4_address()
137132 self.ipv6_address = self.get_ipv6_address()
138133 self.mac_address = self.get_mac_address()
139 self.os_guesses = [os_guess for os_guess in self.get_os_guesses()]
134 self.os_guesses = list(self.get_os_guesses())
140135 self.os = self.top_os_guess()
141 self.ports = [port for port in self.get_ports()]
142 self.vulns = [vuln for vuln in self.get_scripts()]
136 self.ports = list(self.get_ports())
137 self.vulns = list(self.get_scripts())
143138 if self.os != 'unknown':
144139 for p in self.ports:
145140 if p.service is not None:
291286 self.os, ports)
292287
293288
294 class Port(object):
289 class Port:
295290 """
296291 An abstract representation of a Port.
297292
305300 self.number = self.node.get("portid")
306301 self.state, self.reason, self.reason_ttl = self.get_state()
307302 self.service = self.get_service()
308 self.vulns = [vuln for vuln in self.get_scripts()]
303 self.vulns = list(self.get_scripts())
309304
310305 def get_attrib_from_subnode(self, subnode_xpath_expr, attrib_name):
311306 """
354349 return "%s, %s, Service: %s" % (self.number, self.state, self.service)
355350
356351
357 class Script(object):
352 class Script:
358353 """
359354 An abstract representation of a Script.
360355
384379 self.response = ""
385380 for k in script_node.findall("elem"):
386381 self.response += "\n" + str(k.get('key')) + ": " + str(k.text)
387 self.web = True if re.search("(http-|https-)", self.name) else False
382 self.web = re.search("(http-|https-)", self.name)
388383
389384 def __str__(self):
390385 return "%s, %s, %s" % (self.name, self.product, self.version)
391386
392387
393 class Service(object):
388 class Service:
394389 """
395390 An abstract representation of a Service.
396391
420415 return "%s, %s, %s" % (self.name, self.product, self.version)
421416
422417
423 class NmapPlugin(core.PluginBase):
418 class NmapPlugin(PluginXMLFormat):
424419 """
425420 Example plugin to parse nmap output.
426421 """
427422
428423 def __init__(self):
429 core.PluginBase.__init__(self)
424 super().__init__()
425 self.identifier_tag = "nmaprun"
430426 self.id = "Nmap"
431427 self.name = "Nmap XML Output Plugin"
432428 self.plugin_version = "0.0.3"
512508 version=srvversion,
513509 description=srvname)
514510
515 note = True
516511 for v in port.vulns:
517512 severity = 0
518513 desc = v.desc
525520 if re.search(r"Couldn't", desc):
526521 severity = "unclassified"
527522 if v.web:
528 if note:
529 n_id = self.createAndAddNoteToService(
530 h_id,
531 s_id,
532 "website",
533 "")
534
535 n2_id = self.createAndAddNoteToNote(
536 h_id,
537 s_id,
538 n_id,
539 minterfase,
540 "")
541
542 note = False
543523 v_id = self.createAndAddVulnWebToService(
544524 h_id,
545525 s_id,
594574 def createPlugin():
595575 return NmapPlugin()
596576
597 if __name__ == '__main__':
598 parser = NmapXmlParser(sys.argv[1])
599 for host in parser.hosts:
600 if host.status == 'up':
601 print host
577
578 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
5 """
106 import re
117 import os
12 import sys
138 from collections import defaultdict
14 import logging
15
16 logger = logging.getLogger(__name__)
179
1810 try:
1911 import xml.etree.cElementTree as ET
2315 import xml.etree.ElementTree as ET
2416 ETREE_VERSION = ET.VERSION
2517
26 from faraday.client.plugins import core
27 from faraday.client.start_client import FARADAY_BASE
18 from faraday.client.plugins.plugin import PluginXMLFormat
2819 from faraday.client.plugins.plugins_utils import filter_services
2920
3021 ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")]
4132 __status__ = "Development"
4233
4334
44 class OpenvasXmlParser(object):
35 class OpenvasXmlParser:
4536 """
4637 The objective of this class is to parse an xml file generated by the openvas tool.
4738
5243 @param openvas_xml_filepath A proper xml generated by openvas
5344 """
5445
55 def __init__(self, xml_output):
46 def __init__(self, xml_output, logger):
5647 self.target = None
5748 self.port = "80"
5849 self.host = None
50 self.logger = logger
5951 tree = self.parse_xml(xml_output)
6052 if tree:
6153 self.hosts = self.get_hosts(tree)
62 self.items = [data for data in self.get_items(tree, self.hosts)]
54 self.items = list(self.get_items(tree, self.hosts))
6355 else:
6456 self.items = []
6557
7466 """
7567 try:
7668 tree = ET.fromstring(xml_output)
77 except SyntaxError, err:
78 logger.error("SyntaxError: %s. %s", err, xml_output)
69 except SyntaxError as err:
70 self.logger.error("SyntaxError: %s. %s", err, xml_output)
7971 return None
80
8172 return tree
8273
8374 def get_items(self, tree, hosts):
8576 @return items A list of Host instances
8677 """
8778 try:
88 report = tree.findall('report')[0]
89 results = report.findall('results')[0]
90 for node in results.findall('result'):
91 yield Item(node, hosts)
92
93 except Exception:
94 result = tree.findall('result')
95 for node in result:
96 yield Item(node, hosts)
79 report = tree.find('report')
80 results = report.findall('results')
81 if results:
82 nodes = report.findall('results')[0]
83 else:
84 nodes = tree.findall('result')
85 for node in nodes:
86 try:
87 yield Item(node, hosts)
88 except Exception as e:
89 self.logger.error("Error generating Item from %s [%s]", node.attrib, e)
90 except Exception as e:
91 self.logger.error("Tag not found: %s", e)
9792
9893 def get_hosts(self, tree):
9994 # Hosts are located in: /report/report/host
10196 hosts = tree.findall('report/host')
10297 hosts_dict = {}
10398 for host in hosts:
99 ip = self.do_clean(host.find('ip').text)
104100 details = self.get_data_from_detail(host.findall('detail'))
105 hosts_dict[host.find('ip').text] = details
106
101 hosts_dict[ip] = details
107102 return hosts_dict
108103
109104 def get_data_from_detail(self, details):
111106 details_data = defaultdict(list)
112107 hostnames = []
113108 for item in details:
114 name = item.find('name').text
115 value = item.find('value').text
116 if 'EXIT' not in name:
117 if name == 'hostname':
118 hostnames.append(value)
119 else:
120 value = self.do_clean(value)
121 details_data[name].append(value)
122
109 name = self.do_clean(item.find('name').text)
110 value = self.do_clean(item.find('value').text)
111 if "EXIT" in name:
112 continue
113 if name == 'hostname':
114 hostnames.append(value)
115 else:
116 details_data[name].append(value)
123117 data['details'] = details_data
124118 data['hostnames'] = hostnames
125
126119 return data
127120
128121 def do_clean(self, value):
129122 myreturn = ""
130123 if value is not None:
131124 myreturn = re.sub("\s+", " ", value)
132
133125 return myreturn.strip()
134126
135127
168160 return None
169161
170162
171 class Item(object):
163 class Item:
172164 """
173165 An abstract representation of a Item
174166 @param item_node A item_node taken from an openvas xml tree
178170 self.node = item_node
179171 self.host = self.get_text_from_subnode('host')
180172 self.subnet = self.get_text_from_subnode('subnet')
181
182173 if self.subnet is '':
183174 self.subnet = self.host
184
185175 self.port = "None"
186176 self.severity = self.severity_mapper()
187177 self.service = "Unknown"
202192 info = port.split("/")
203193 self.protocol = info[1]
204194 self.service = info[0] # this value is general
205
206195 self.nvt = self.node.findall('nvt')[0]
207196 self.node = self.nvt
208197 self.id = self.node.get('oid')
209198 self.name = self.get_text_from_subnode('name')
210 self.cve = self.get_text_from_subnode(
211 'cve') if self.get_text_from_subnode('cve') != "NOCVE" else ""
212 self.bid = self.get_text_from_subnode(
213 'bid') if self.get_text_from_subnode('bid') != "NOBID" else ""
214 self.xref = self.get_text_from_subnode(
215 'xref') if self.get_text_from_subnode('xref') != "NOXREF" else ""
216
199 self.cve = self.get_text_from_subnode('cve') if self.get_text_from_subnode('cve') != "NOCVE" else ""
200 self.bid = self.get_text_from_subnode('bid') if self.get_text_from_subnode('bid') != "NOBID" else ""
201 self.xref = self.get_text_from_subnode('xref') if self.get_text_from_subnode('xref') != "NOXREF" else ""
217202 self.description = ''
218203 self.resolution = ''
219204 self.cvss_vector = ''
224209 self.resolution = tags_data['solution']
225210 self.cvss_vector = tags_data['cvss_base_vector']
226211
212
227213 def get_text_from_subnode(self, subnode_xpath_expr):
228214 """
229215 Finds a subnode in the host node and the retrieves a value from it.
233219 sub_node = self.node.find(subnode_xpath_expr)
234220 if sub_node is not None and sub_node.text is not None:
235221 return sub_node.text.strip()
236
237222 return ''
238223
239224 def severity_mapper(self):
248233 # value: list with the values associated with the name
249234 for name, value in details_from_host.items():
250235 service_detail = self.get_service_from_details(name, value, port)
251
252236 if service_detail:
253237 return service_detail
254
255238 # if the service is not in details_from_host, we will search it in
256239 # the file port_mapper.txt
257 srv = filter_services()
258 for service in srv:
240 services_mapper = filter_services()
241 for service in services_mapper:
259242 if service[0] == port:
260243 return service[1]
261244
299282 auxiliar_port = port.split('/')[0]
300283 if aux_value == auxiliar_port:
301284 res = name
302
303285 return res
304286
305287 def get_data_from_tags(self, tags_text):
326308 return data
327309
328310
329 class OpenvasPlugin(core.PluginBase):
311 class OpenvasPlugin(PluginXMLFormat):
330312 """
331313 Example plugin to parse openvas output.
332314 """
333315
334316 def __init__(self):
335 core.PluginBase.__init__(self)
317 super().__init__()
318 self.identifier_tag = "report"
336319 self.id = "Openvas"
337320 self.name = "Openvas XML Output Plugin"
338321 self.plugin_version = "0.3"
345328 r'^(openvas|sudo openvas|\.\/openvas).*?')
346329
347330 global current_path
348 self._output_file_path = os.path.join(self.data_path,
349 "openvas_output-%s.xml" % self._rid)
331 self._output_file_path = os.path.join(self.data_path, "openvas_output-%s.xml" % self._rid)
332
333 def report_belongs_to(self, **kwargs):
334 if super().report_belongs_to(**kwargs):
335 report_path = kwargs.get("report_path", "")
336 with open(report_path) as f:
337 output = f.read()
338 return re.search("OpenVAS", output) is not None or re.search('<omp>', output) is not None
339 return False
350340
351341 def parseOutputString(self, output, debug=False):
352342 """
356346 NOTE: if 'debug' is true then it is being run from a test case and the
357347 output being sent is valid.
358348 """
359
360 parser = OpenvasXmlParser(output)
361
349 parser = OpenvasXmlParser(output, self.logger)
362350 web = False
363351 ids = {}
364352 # The following threats values will not be taken as vulns
365353 self.ignored_severities = ['Log', 'Debug']
366
367354 for ip, values in parser.hosts.items():
368355 # values contains: ip details and ip hostnames
369356 h_id = self.createAndAddHost(
400387 if item.severity not in self.ignored_severities:
401388 v_id = self.createAndAddVulnToHost(
402389 h_id,
403 item.name.encode("utf-8"),
404 desc=item.description.encode("utf-8"),
405 severity=item.severity.encode("utf-8"),
406 resolution=item.resolution.encode("utf-8"),
390 item.name,
391 desc=item.description,
392 severity=item.severity,
393 resolution=item.resolution,
407394 ref=ref,
408395 external_id=item.id)
409396 else:
410397 if item.service:
411 web = True if re.search(
398 web = re.search(
412399 r'^(www|http)',
413 item.service) else False
400 item.service)
414401 else:
415 web = True if item.port in ('80', '443', '8080') else False
402 web = item.port in ('80', '443', '8080')
416403
417404 if item.subnet + "_" + item.port in ids:
418405 s_id = ids[item.subnet + "_" + item.port]
429416 v_id = self.createAndAddVulnWebToService(
430417 h_id,
431418 s_id,
432 item.name.encode("utf-8"),
433 desc=item.description.encode("utf-8"),
419 item.name,
420 desc=item.description,
434421 website=item.host,
435 severity=item.severity.encode("utf-8"),
422 severity=item.severity,
436423 ref=ref,
437 resolution=item.resolution.encode("utf-8"),
424 resolution=item.resolution,
438425 external_id=item.id)
439426 elif item.severity not in self.ignored_severities:
440427 self.createAndAddVulnToService(
441428 h_id,
442429 s_id,
443 item.name.encode("utf-8"),
444 desc=item.description.encode("utf-8"),
445 severity=item.severity.encode("utf-8"),
430 item.name,
431 desc=item.description,
432 severity=item.severity,
446433 ref=ref,
447 resolution=item.resolution.encode("utf-8"),
448 external_id=item.id )
449
434 resolution=item.resolution,
435 external_id=item.id)
450436 del parser
451437
452438 def _isIPV4(self, ip):
465451 def createPlugin():
466452 return OpenvasPlugin()
467453
468 if __name__ == '__main__':
469 parser = OpenvasPlugin()
470 with open("pathtoxmlfile.xml","r") as report:
471 parser.parseOutputString(report.read())
472 #for item in parser.items:
473 #if item.status == 'up':
474 #print item
454
455
456 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 '''
0 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
64
7 '''
5 """
86
97 # Author: @EzequielTBH
8 from builtins import str
109
1110 from faraday.client.plugins import core
1211 import json
2221 class pasteAnalyzerPlugin(core.PluginBase):
2322
2423 def __init__(self):
25 core.PluginBase.__init__(self)
24 super().__init__()
2625 self.id = "pasteAnalyzer"
2726 self.name = "pasteAnalyzer JSON Output Plugin"
2827 self.plugin_version = "1.0.0"
7776 for element in data:
7877
7978 # Is Category
80 if type(element) == str or type(element) == unicode:
79 if type(element) == str: #TODO bte arrray decode
8180 description += element + ": "
8281
8382 # Is a list with results!
109108
110109 def createPlugin():
111110 return pasteAnalyzerPlugin()
111
112
113 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 # -*- coding: utf-8 -*-
10 """
21 Faraday Penetration Test IDE
32 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
43 See the file 'doc/LICENSE' for the license information
54 """
6
75 import re
86 import socket
97 from os import path
108 from faraday.client.plugins import core
11 from urlparse import urlparse
9 from urllib.parse import urlparse
1210
1311 __author__ = "Andres Tarantini"
1412 __copyright__ = "Copyright (c) 2015 Andres Tarantini"
2624 """
2725
2826 def __init__(self):
29 core.PluginBase.__init__(self)
27 super().__init__()
3028 self.id = "peepingtom"
3129 self.name = "PeepingTom"
3230 self.plugin_version = "0.0.1"
3836 def parseOutputString(self, output):
3937 # Find data path
4038 data_path_search = re.search(r"in '(.*)\/'", output)
41 print data_path_search
39 print(data_path_search)
4240 if not data_path_search:
4341 # No data path found
4442 return True
7068
7169 def processCommandString(self, username, current_path, command_string):
7270 self._path = current_path
73 return None
7471
7572
7673 def createPlugin():
7774 return PeepingTomPlugin()
75
76
77 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
5 """
96 from faraday.client.plugins import core
107 import re
118
2623 """
2724
2825 def __init__(self):
29 core.PluginBase.__init__(self)
26 super().__init__()
3027 self.id = "ping"
3128 self.name = "Ping"
3229 self.plugin_version = "0.0.1"
6663
6764 def createPlugin():
6865 return CmdPingPlugin()
66
67
68 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
5 """
96 from faraday.client.plugins import core
107 from faraday.client.model import api
118 import re
2825 """
2926
3027 def __init__(self):
31 core.PluginBase.__init__(self)
28 super().__init__()
3229 self.id = "propecia"
3330 self.name = "propecia port scanner"
3431 self.plugin_version = "0.0.1"
7370 if count_args.__len__() == 3:
7471 self._port = count_args[2]
7572
76 return None
77
7873
7974 def createPlugin():
8075 return CmdPropeciaPlugin()
76
77
78 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 '''
0 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
6 '''
7
8 from __future__ import with_statement
9 from faraday.client.plugins import core
4 """
5 from faraday.client.plugins.plugin import PluginXMLFormat
106 import re
117 import os
128 import sys
139 import logging
1410
1511 try:
16
1712 import xml.etree.cElementTree as ET
1813 import xml.etree.ElementTree as ET_ORIG
1914 ETREE_VERSION = ET_ORIG.VERSION
7873 return
7974
8075 if type_report is 'ASSET_DATA_REPORT':
81 self.items = [data for data in self.get_items_asset_report(tree)]
76 self.items = list(self.get_items_asset_report(tree))
8277 elif type_report is 'SCAN':
83 self.items = [data for data in self.get_items_scan_report(tree)]
78 self.items = list(self.get_items_scan_report(tree))
8479
8580 def parse_xml(self, xml_output):
8681 """
105100 else:
106101 type_report = None
107102
108 except SyntaxError, err:
103 except SyntaxError as err:
109104 logger.error('SyntaxError: %s.' % (err))
110105 return None, None
111106
362357 return None
363358
364359
365 class QualysguardPlugin(core.PluginBase):
360 class QualysguardPlugin(PluginXMLFormat):
366361 """
367362 Example plugin to parse qualysguard output.
368363 """
369364
370365 def __init__(self):
371
372 core.PluginBase.__init__(self)
366 super().__init__()
367 self.identifier_tag = ["ASSET_DATA_REPORT", "SCAN"]
373368 self.id = 'Qualysguard'
374369 self.name = 'Qualysguard XML Output Plugin'
375370 self.plugin_version = '0.0.2'
456451 def createPlugin():
457452 return QualysguardPlugin()
458453
459 if __name__ == '__main__':
460 parser = QualysguardXmlParser(sys.argv[1])
461 for item in parser.items:
462 if item.status == 'up':
463 print item
454
455 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 '''
0 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
6 '''
4 """
75 import re
86 import json
97 import socket
1311 except ImportError:
1412 import xml.etree.ElementTree as ET
1513
16 from faraday.client.plugins.core import PluginBase
14 from faraday.client.plugins.plugin import PluginXMLFormat
1715
1816 __author__ = 'Leonardo Lazzaro'
1917 __copyright__ = 'Copyright (c) 2017, Infobyte LLC'
2725 logger = logging.getLogger(__name__)
2826
2927
30 class ReconngParser(object):
28 class ReconngParser:
3129 def __init__(self, output):
3230 self._format = self.report_format(output)
3331 self.hosts = []
5856 tree = ET.fromstring(xml_output)
5957 return tree
6058 except IndexError:
61 print "Syntax error"
59 print("Syntax error")
6260 return None
6361
6462 def parse_xml_report(self, tree):
123121 return info
124122
125123
126 class ReconngPlugin(PluginBase):
124 class ReconngPlugin(PluginXMLFormat):
127125 """
128126 Example plugin to parse qualysguard output.
129127 """
130128
131129 def __init__(self):
132
133 PluginBase.__init__(self)
130 super().__init__()
131 self.identifier_tag = "reconng"
134132 self.id = 'Reconng'
135133 self.name = 'Reconng XML Output Plugin'
136134 self.plugin_version = '0.0.3'
153151 )
154152 self.host_mapper[host['host']] = h_id
155153 for vuln in parser.vulns:
156 if vuln['host'] not in self.host_mapper.keys():
154 if vuln['host'] not in list(self.host_mapper.keys()):
157155 ip = self.resolve_host(vuln['host'])
158156 h_id = self.createAndAddHost(
159157 ip,
186184 def createPlugin():
187185 return ReconngPlugin()
188186
189 if __name__ == '__main__':
190 with open("~/results_hosts_vulns.xml", "r") as report:
191 parser = ReconngParser(report.read())
192 # for item in parser.items:
193 # if item.status == 'up':
194 # print item
187
188 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
10 from faraday.client.plugins import core
5 """
6 from faraday.client.plugins.plugin import PluginXMLFormat
117 import re
128 import os
139 import sys
3430 __status__ = "Development"
3531
3632
37 class RetinaXmlParser(object):
33 class RetinaXmlParser:
3834 """
3935 The objective of this class is to parse an xml file generated by the retina tool.
4036
4844 def __init__(self, xml_output):
4945 tree = self.parse_xml(xml_output)
5046 if tree:
51 self.items = [data for data in self.get_items(tree)]
47 self.items = list(self.get_items(tree))
5248 else:
5349 self.items = []
5450
6359 """
6460 try:
6561 tree = ET.fromstring(xml_output)
66 except SyntaxError, err:
67 print "SyntaxError: %s. %s" % (err, xml_output)
62 except SyntaxError as err:
63 print("SyntaxError: %s. %s" % (err, xml_output))
6864 return None
6965
7066 return tree
7773 yield Item(node)
7874
7975
80 class Item(object):
76 class Item:
8177 """
8278 An abstract representation of a Item
8379
172168 return None
173169
174170
175 class RetinaPlugin(core.PluginBase):
171 class RetinaPlugin(PluginXMLFormat):
176172 """
177173 Example plugin to parse retina output.
178174 """
179175
180176 def __init__(self):
181 core.PluginBase.__init__(self)
177 super().__init__()
178 self.identifier_tag = "scanJob"
182179 self.id = "Retina"
183180 self.name = "Retina XML Output Plugin"
184181 self.plugin_version = "0.0.1"
209206 self.createAndAddNoteToHost(
210207 h_id, "netBIOSDomain", item.netbiosdomain)
211208
212 a = {}
213 a.iteritems
214 for k, vulns in item.ports.iteritems():
209 for k, vulns in item.ports.items():
215210 if k:
216211 for v in vulns:
217212 web = False
228223 if web:
229224 v_id = self.createAndAddVulnWebToService(h_id, s_id, v.name.encode(
230225 "utf-8"), ref=v.ref, website=hostname, severity=v.severity, resolution=v.solution.encode("utf-8"), desc=v.desc.encode("utf-8"))
231 n_id = self.createAndAddNoteToService(
232 h_id, s_id, "website", "")
233 n2_id = self.createAndAddNoteToNote(
234 h_id, s_id, n_id, hostname, "")
235226 else:
236227 v_id = self.createAndAddVulnToService(h_id, s_id, v.name.encode(
237228 "utf-8"), ref=v.ref, severity=v.severity, resolution=v.solution.encode("utf-8"), desc=v.desc.encode("utf-8"))
251242 def createPlugin():
252243 return RetinaPlugin()
253244
254 if __name__ == '__main__':
255 parser = RetinaXmlParser(sys.argv[1])
256 for item in parser.items:
257 if item.status == 'up':
258 print item
245
246 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9
10 from __future__ import with_statement
5 """
116 from faraday.client.plugins import core
127 import re
138 import os
2621 __status__ = "Development"
2722
2823
29 class ReverseraiderParser(object):
24 class ReverseraiderParser:
3025 """
3126 The objective of this class is to parse an xml file generated by the reverseraider tool.
3227
4237 return
4338
4439 for line in lists:
45 if line <> "":
46 print "(%s)" % line
40 if line != "":
41 print("(%s)" % line)
4742 info = line.split("\t")
4843 if info.__len__() > 0:
4944 item = {'host': info[0], 'ip': info[1]}
50 print "host = %s, ip = %s" % (info[0], info[1])
45 print("host = %s, ip = %s" % (info[0], info[1]))
5146 self.items.append(item)
5247
5348
5752 """
5853
5954 def __init__(self):
60 core.PluginBase.__init__(self)
55 super().__init__()
6156 self.id = "Reverseraider"
6257 self.name = "Reverseraider XML Output Plugin"
6358 self.plugin_version = "0.0.1"
118113 def createPlugin():
119114 return ReverseraiderPlugin()
120115
121 if __name__ == '__main__':
122 parser = ReverseraiderParser(sys.argv[1])
123 for item in parser.items:
124 if item.status == 'up':
125 print item
116
117 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
4 """
95 from faraday.client.plugins import core
106 from faraday.config.configuration import getInstanceConfiguration
11 from urlparse import urlparse
127 import requests
13 import xmlrpclib
8 import xmlrpc.client
149 import json
1510 import uuid
1611 import re
3126 """
3227
3328 def __init__(self):
34 core.PluginBase.__init__(self)
29 super().__init__()
3530 self.id = "Sentinel"
3631 self.name = "Sentinel Online Plugin"
3732 self.plugin_version = "0.0.1"
4338 self.addSetting("Enable", str, "0")
4439
4540 self.faraday_config = 'http://' + getInstanceConfiguration().getApiConInfoHost() + ':' + str(getInstanceConfiguration().getApiConInfoPort()) + '/'
46 self.faraday_api = xmlrpclib.ServerProxy(self.faraday_config)
41 self.faraday_api = xmlrpc.client.ServerProxy(self.faraday_config)
4742 self.format = "?format=json&display_all=1&key="
4843 self._command_regex = re.compile(
4944 r'^(sudo sentinel|sentinel).*?')
184179
185180 def createPlugin():
186181 return SentinelPlugin()
182 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
5 """
106 from faraday.client.plugins import core
117 import re
128 import os
2723 __status__ = "Development"
2824
2925
30 class SkipfishParser(object):
26 class SkipfishParser:
3127 """
3228 The objective of this class is to parse an xml file generated by
3329 the skipfish tool.
114110 """
115111
116112 def __init__(self):
117 core.PluginBase.__init__(self)
113 super().__init__()
118114 self.id = "Skipfish"
119115 self.name = "Skipfish XML Output Plugin"
120116 self.plugin_version = "0.0.2"
173169 "tcp",
174170 ports=[port],
175171 status="open")
176
177 n_id = self.createAndAddNoteToService(
178 h_id,
179 s_id,
180 "website",
181 "")
182
183 self.createAndAddNoteToNote(h_id, s_id, n_id, host, "")
184172
185173 hostc[sample["url"]] = {
186174 'h_id': h_id,
255243 parser = SkipfishParser(sys.argv[1])
256244 for item in parser.items:
257245 if item.status == 'up':
258 print item
246 print(item)
247
248
249 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1
2 '''
0 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
6 '''
7 from __future__ import with_statement
8
4 """
95 import argparse
6 import base64
107 import hashlib
118 import os
129 import pickle
1512 import socket
1613 import sqlite3
1714 import sys
18 from BaseHTTPServer import BaseHTTPRequestHandler
19 from StringIO import StringIO
20 from urlparse import urlparse
15 from urllib.parse import urlparse
16 from io import StringIO
17 from http.server import BaseHTTPRequestHandler
18
2119 from collections import defaultdict
2220
2321 from faraday.client.plugins.plugin import PluginTerminalOutput
5351 SUPPORTED_HASHDB_VERSIONS = {
5452 "dPHoJRQYvs", # 1.0.11
5553 "BZzRotigLX", # 1.2.8
54 "OdqjeUpBLc", # 1.3.6..1.3.10
5655 }
5756
5857
59 class Database(object):
58 class Database:
6059
6160 def __init__(self, database):
6261 self.database = database
130129 class HTTPRequest(BaseHTTPRequestHandler):
131130
132131 def __init__(self, request_text):
132 super().__init__()
133133 self.rfile = StringIO(request_text)
134134 self.raw_requestline = self.rfile.readline()
135135 self.error_code = self.error_message = None
141141
142142 def hashKey(self, key):
143143 # from sqlmap/lib/utils/hashdb.py
144 # we don't sanitize key, because we only work
145 # with plain string
146 retVal = int(hashlib.md5(key).hexdigest(), 16) & 0x7fffffffffffffff
144 import six #pylint: disable=import-error,bad-option-value,import-outside-toplevel
145 from lib.core.convert import getBytes #pylint: disable=import-error,bad-option-value,import-outside-toplevel
146 key = getBytes(key if isinstance(key, six.text_type) else repr(key))
147 retVal = int(hashlib.md5(key).hexdigest(), 16) & 0x7fffffffffffffff # Reference: http://stackoverflow.com/a/4448400
147148 return retVal
148149
149150 def hashDBRetrieve(self, key, unserialize=False, db=False):
156157 key = "%s%s%s" % (self.url or "%s%s" % (
157158 self.hostname, self.port), key, self.HASHDB_MILESTONE_VALUE)
158159 else:
160 if not self.url:
161 self.log('No URL found while running sqlmap', 'ERROR')
159162 url = urlparse(self.url)
160163 key = '|'.join([
161164 url.hostname,
172175 try:
173176 for row in db.execute("SELECT value FROM storage WHERE id=?", (hash_,)):
174177 retVal = row[0]
175 except sqlite3.OperationalError, ex:
178 except sqlite3.OperationalError as ex:
176179 if not 'locked' in ex.message:
177180 raise
178181 else:
187190 'foobar'
188191 """
189192
190 return value.decode("base64")
193 return base64.b64decode(value)
191194
192195 def base64encode(self, value):
193196 """
196199 >>> base64encode('foobar')
197200 'Zm9vYmFy'
198201 """
199
200 return value.encode("base64")[:-1].replace("\n", "")
202 return base64.b64encode(value)[:-1].replace("\n", "")
201203
202204 def base64unpickle(self, value):
203205 """
216218 with open(filepath, "r") as f:
217219 try:
218220 tree = ET.fromstring(f.read())
219 except SyntaxError, err:
221 except SyntaxError as err:
220222 self.log("SyntaxError: %s. %s" % (err, filepath), "ERROR")
221223 return None
222224
230232 data)
231233
232234 if users:
233 return map((lambda x: x.replace("[*] ", "")), users.group(1).split("\n"))
235 return [x.replace("[*] ", "") for x in users.group(1).split("\n")]
234236
235237 def getdbs(self, data):
236238
239241 data)
240242
241243 if dbs:
242 return map((lambda x: x.replace("[*] ", "")), dbs.group(1).split("\n"))
244 return [x.replace("[*] ", "") for x in dbs.group(1).split("\n")]
243245
244246 def getpassword(self, data):
245247
408410 sys.path.append(self.getSetting("Sqlmap path"))
409411
410412 try:
411 from lib.core.settings import HASHDB_MILESTONE_VALUE #pylint: disable=import-error,bad-option-value
412 from lib.core.enums import HASHDB_KEYS #pylint: disable=import-error
413 from lib.core.settings import UNICODE_ENCODING #pylint: disable=import-error
413 from lib.core.settings import HASHDB_MILESTONE_VALUE #pylint: disable=import-error,bad-option-value,import-outside-toplevel
414 from lib.core.enums import HASHDB_KEYS #pylint: disable=import-error,import-outside-toplevel
415 from lib.core.settings import UNICODE_ENCODING #pylint: disable=import-error,import-outside-toplevel
414416 except:
415417 self.log('Remember set your Sqlmap Path Setting!... Abort plugin.', 'ERROR')
416418 return
474476 [self.port],
475477 status="open",
476478 version=webserver)
477
478 n_id = self.createAndAddNoteToService(
479 h_id,
480 s_id,
481 "website",
482 '')
483
484 self.createAndAddNoteToNote(
485 h_id,
486 s_id,
487 n_id,
488 self.hostname,
489 '')
490479
491480 db_port = 0
492481 for item in self.db_port.keys():
511500
512501 # sqlmap.py --passwords
513502 if password:
514 for k, v in password.iteritems():
503 for k, v in password.items():
515504 self.createAndAddCredToService(h_id, s_id2, k, v)
516505
517506 # sqlmap.py --file-dest
658647
659648 self._output_path = "%s%s" % (
660649 os.path.join(self.data_path, "sqlmap_output-"),
661 re.sub(r'[\n\/]', r'',
662 args.u.encode("base64")[:-1]))
650 re.sub(r'[\n\/]', r'', base64.b64encode(args.u.encode()).strip().decode()))
663651
664652 if not args.s:
665653 return "%s -s %s" % (command_string, self._output_path)
670658
671659 def createPlugin():
672660 return SqlmapPlugin()
661
662
663 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 # -*- coding: utf-8 -*-
10 """
21 Faraday Penetration Test IDE
32 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
43 See the file 'doc/LICENSE' for the license information
54 """
6
75 from faraday.client.plugins import core
86 import re
97
2422 """
2523
2624 def __init__(self):
27 core.PluginBase.__init__(self)
25 super().__init__()
2826 self.id = "sshdefaultscan"
2927 self.name = "sshdefaultscan"
3028 self.plugin_version = "0.0.1"
7371
7472 def createPlugin():
7573 return SSHDefaultScanPlugin()
74
75
76 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8 from __future__ import with_statement
4 """
95 from faraday.client.plugins import core
106 import re
117 import os
3632 __status__ = "Development"
3733
3834
39 class SslcheckParser(object):
35 class SslcheckParser:
4036 """
4137 The objective of this class is to parse an xml file generated by the ssl-check tool.
4238 TODO: Handle errors.
6864 """
6965
7066 def __init__(self):
71 core.PluginBase.__init__(self)
67 super().__init__()
7268 self.id = "Sslcheck"
7369 self.name = "Sslcheck XML Output Plugin"
7470 self.plugin_version = "0.0.2"
203199 def createPlugin():
204200 return SslcheckPlugin()
205201
206 if __name__ == '__main__':
207 parser = SslcheckParser(sys.argv[1])
208 for item in parser.items:
209 if item.status == 'up':
210 print item
202 # I'm Py3
0 #coding=utf-8
10 import re
21 import os
32 import random
4 from faraday.client.plugins import core
3 from faraday.client.plugins.plugin import PluginXMLFormat
54
65 try:
76 from lxml import etree as ET
2322 ]
2423
2524
26 class SslyzeXmlParser(object):
25 class SslyzeXmlParser:
2726
2827 def __init__(self, xml_output):
2928 self.parser = self.parse_xml(xml_output)
3837 tree = ET.fromstring(xml_output)
3938 return tree
4039 except IndexError:
41 print "Syntax error"
40 print("Syntax error")
4241 return None
4342
4443 def get_target(self, tree):
8584 return tree.xpath('//openssl_ccs')
8685
8786
88 class SslyzePlugin(core.PluginBase):
87 class SslyzePlugin(PluginXMLFormat):
8988
9089 def __init__(self):
91 core.PluginBase.__init__(self)
90 super().__init__()
91 self.identifier_tag = "document"
9292 self.id = "Sslyze"
9393 self.name = "Sslyze Plugin"
9494 self.plugin_version = "0.0.1"
9898 self._current_output = None
9999 self._command_regex = re.compile(r'^(sudo sslyze|sslyze|\.\/sslyze).*?')
100100 self.xml_arg_re = re.compile(r"^.*(--xml_output\s*[^\s]+).*$")
101
102 def report_belongs_to(self, **kwargs):
103 if super().report_belongs_to(**kwargs):
104 report_path = kwargs.get("report_path", "")
105 with open(report_path) as f:
106 output = f.read()
107 return re.search("SSLyzeVersion", output) is not None
108 return False
101109
102110 def parseOutputString(self, output, debug=False):
103111 parser = SslyzeXmlParser(output)
185193 def createPlugin():
186194 return SslyzePlugin()
187195
188 if __name__ == '__main__':
189 parser = SslyzePlugin()
190 with open("/home/javier/expired.xml","r") as report:
191 parser.parseOutputString(report.read())
196
197 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
4 """
95 import re
106 import os
117 import shlex
2723
2824 class Sublist3rPlugin(PluginTerminalOutput):
2925 def __init__(self):
30 super(Sublist3rPlugin, self).__init__()
26 super().__init__()
3127 self.id = "sublist3r"
3228 self.name = "sublist3r"
3329 self.plugin_version = "0.0.1"
106102
107103 def createPlugin():
108104 return Sublist3rPlugin()
105
106
107 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
5 """
106 from faraday.client.plugins import core
117 from faraday.client.model import api
128 import re
139 import os
1410 import socket
15 import pprint
11
1612 current_path = os.path.abspath(os.getcwd())
1713
1814 __author__ = "Facundo de Guzmán, Esteban Guillardoy"
3228 """
3329
3430 def __init__(self):
35 core.PluginBase.__init__(self)
31 super().__init__()
3632 self.id = "Telnet"
3733 self.name = "Telnet"
3834 self.plugin_version = "0.0.1"
9389
9490 c = count_args.__len__()
9591 self._port = "23"
96 if re.search("[\d]+", count_args[c - 1]):
92 if re.search(r"[\d]+", count_args[c - 1]):
9793 self._port = count_args[c - 1]
9894
9995
10096 def createPlugin():
10197 return TelnetRouterPlugin()
98
99
100 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
5 """
96 from faraday.client.plugins import core
107 import re
118 import os
2421 __status__ = "Development"
2522
2623
27 class TheharvesterParser(object):
24 class TheharvesterParser:
2825 """
2926 The objective of this class is to parse an xml file generated by the theharvester tool.
3027
7976 """
8077
8178 def __init__(self):
82 core.PluginBase.__init__(self)
79 super().__init__()
8380 self.id = "Theharvester"
8481 self.name = "Theharvester XML Output Plugin"
8582 self.plugin_version = "0.0.1"
115112 output being sent is valid.
116113 """
117114
118 print "este es el output (%s)" % output
115 print("este es el output (%s)" % output)
119116
120117 if debug:
121118 parser = TheharvesterParser(output)
123120
124121 parser = TheharvesterParser(output)
125122
126 print len(parser.items)
123 print(len(parser.items))
127124 for item in parser.items:
128125 host = []
129126 if item['host'] != item['ip']:
143140 def createPlugin():
144141 return TheharvesterPlugin()
145142
146 if __name__ == '__main__':
147 parser = TheharvesterParser(sys.argv[1])
148 for item in parser.items:
149 if item.status == 'up':
150 print item
143
144
145 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 '''
0 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
64
7 '''
8
5 """
96 from faraday.client.plugins import core
107 import re
118
1916 class traceroutePlugin(core.PluginBase):
2017
2118 def __init__(self):
22 core.PluginBase.__init__(self)
19 super().__init__()
2320 self.id = "Traceroute"
2421 self.name = "Traceroute"
2522 self.plugin_version = "1.0.0"
6259
6360 def createPlugin():
6461 return traceroutePlugin()
62
63
64 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
10 from faraday.client.plugins import core
5 """
6
7 from faraday.client.plugins.plugin import PluginXMLFormat
118 from faraday.client.model import api
129 import re
1310 import os
3734 __status__ = "Development"
3835
3936
40 class W3afXmlParser(object):
37 class W3afXmlParser:
4138 """
4239 The objective of this class is to parse an xml file generated by the w3af tool.
4340
5653 tree = self.parse_xml(xml_output)
5754
5855 if tree:
59 self.items = [data for data in self.get_items(tree)]
56 self.items = list(self.get_items(tree))
6057 else:
6158 self.items = []
6259
7168 """
7269 try:
7370 tree = ET.fromstring(xml_output)
74 except SyntaxError, err:
75 print "SyntaxError: %s. %s" % (err, xml_output)
71 except SyntaxError as err:
72 print("SyntaxError: %s. %s" % (err, xml_output))
7673 return None
7774
7875 return tree
138135 return None
139136
140137
141 class Item(object):
138 class Item:
142139 """
143140 An abstract representation of a Item
144141
208205 return None
209206
210207
211 class W3afPlugin(core.PluginBase):
208 class W3afPlugin(PluginXMLFormat):
212209 """
213210 Example plugin to parse w3af output.
214211 """
215212
216213 def __init__(self):
217 core.PluginBase.__init__(self)
214 super().__init__()
215 self.identifier_tag = "w3af-run"
218216 self.id = "W3af"
219217 self.name = "W3af XML Output Plugin"
220218 self.plugin_version = "0.0.2"
246244 ports=[parser.port],
247245 status="open")
248246
249 n_id = self.createAndAddNoteToService(h_id, s_id, "website", "")
250 n2_id = self.createAndAddNoteToNote(h_id, s_id, n_id, parser.host, "")
251
252247 for item in parser.items:
253248 v_id = self.createAndAddVulnWebToService(h_id, s_id, item.name,
254249 item.detail, pname=item.param, path=item.url, website=parser.host, severity=item.severity,
272267 def createPlugin():
273268 return W3afPlugin()
274269
275 if __name__ == '__main__':
276 parser = W3afXmlParser(sys.argv[1])
277 for item in parser.items:
278 if item.status == 'up':
279 print item
270
271 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
5 """
106 import re
117 import os
128 import socket
13 from urlparse import urlparse
14
15 from faraday.client.plugins import core
16
17
9
10 from urllib.parse import urlparse
11 from faraday.client.plugins.plugin import PluginXMLFormat
1812 try:
1913 import xml.etree.cElementTree as ET
2014 import xml.etree.ElementTree as ET_ORIG
3731 __status__ = "Development"
3832
3933
40 class WapitiXmlParser(object):
34 class WapitiXmlParser:
4135 """
4236 The objective of this class is to parse an xml file generated by the wapiti tool.
4337
4943 """
5044
5145 def __init__(self, xml_output):
52
5346 tree = self.parse_xml(xml_output)
54
5547 if tree:
56 self.items = [data for data in self.get_items(tree)]
48 self.items = list(self.get_items(tree))
5749 else:
5850 self.items = []
5951
9385 node = None
9486
9587 if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3:
96
9788 match_obj = re.search(
9889 "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr)
9990 if match_obj is not None:
10697 break
10798 else:
10899 node = xml_node.find(subnode_xpath_expr)
109
110100 else:
111101 node = xml_node.find(subnode_xpath_expr)
112
113102 if node is not None:
114103 return node.get(attrib_name)
115
116104 return None
117105
118106
119 class Item(object):
107 class Item:
120108 """
121109 An abstract representation of a Item
122110
229217 return 'low'
230218
231219
232 class WapitiPlugin(core.PluginBase):
220 class WapitiPlugin(PluginXMLFormat):
233221 """
234222 Example plugin to parse wapiti output.
235223 """
236224
237225 def __init__(self):
238 core.PluginBase.__init__(self)
226 super().__init__()
227 self.identifier_tag = "report"
239228 self.id = "Wapiti"
240229 self.name = "Wapiti XML Output Plugin"
241230 self.plugin_version = "0.0.1"
245234 self.protocol = None
246235 self.host = None
247236 self.port = "80"
237 self.xml_arg_re = re.compile(r"^.*(-oX\s*[^\s]+).*$")
248238 self._command_regex = re.compile(
249239 r'^(python wapiti|wapiti|sudo wapiti|sudo wapiti\.py|wapiti\.py|python wapiti\.py|\.\/wapiti\.py|wapiti|\.\/wapiti|python wapiti|python \.\/wapiti).*?')
250240 self._completition = {
286276 }
287277
288278 global current_path
289 self._output_file_path = os.path.join(self.data_path,
290 "wapiti_output-%s.xml" % self._rid)
279 self._output_file_path = os.path.join(self.data_path, "wapiti_output-%s.xml" % self._rid)
280
281 def report_belongs_to(self, **kwargs):
282 if super().report_belongs_to(**kwargs):
283 report_path = kwargs.get("report_path", "")
284 with open(report_path) as f:
285 output = f.read()
286 return re.search("Wapiti", output) is not None
287 return False
288
291289
292290 def parseOutputString(self, output):
293291 """
313311 request=entry['http_request'],
314312 method=entry['method'],
315313 params=entry['parameter'])
316
317
318
319 xml_arg_re = re.compile(r"^.*(-oX\s*[^\s]+).*$")
320
321
322314
323315 def processCommandString(self, username, current_path, command_string):
324316 """
327319 """
328320 host = re.search(
329321 "(http|https|ftp)\://([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&amp;%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))[\:]*([0-9]+)*([/]*($|[a-zA-Z0-9\.\,\?\'\\\+&amp;%\$#\=~_\-]+)).*?$", command_string)
330
331322 self.protocol = host.group(1)
332323 self.host = host.group(4)
333324 if host.group(11) is not None:
334325 self.port = host.group(11)
335326 if self.protocol == 'https':
336327 self.port = 443
337
338 print "host = %s, port = %s" % (self.host, self.port)
339
328 self.logger.debug("host = %s, port = %s",self.host, self.port)
340329 arg_match = self.xml_arg_re.match(command_string)
341
342330 return "%s -o %s -f xml \n" % (command_string, self._output_file_path)
343331
344332 def setHost(self):
348336 def createPlugin():
349337 return WapitiPlugin()
350338
351 if __name__ == '__main__':
352 parser = WapitiPlugin()
353 with open('/home/javier/Reports_Testing/wapiti3.0.1.xml') as report:
354 parser.parseOutputString(report.read())
339
340 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8 from __future__ import with_statement
4 """
95 from faraday.client.plugins import core
106 import re
117 import os
3430 __status__ = "Development"
3531
3632
37 class WcscanParser(object):
33 class WcscanParser:
3834 """
3935 The objective of this class is to parse an xml file generated by the wcscan tool.
4036 TODO: Handle errors.
7571 """
7672
7773 def __init__(self):
78 core.PluginBase.__init__(self)
74 super().__init__()
7975 self.id = "Wcscan"
8076 self.name = "Wcscan XML Output Plugin"
8177 self.plugin_version = "0.0.2"
184180 def createPlugin():
185181 return WcscanPlugin()
186182
187 if __name__ == '__main__':
188 parser = WcscanParser(sys.argv[1])
189 for item in parser.items:
190 if item.status == 'up':
191 print item
183
184 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 '''
0 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
64
7 '''
8 from __future__ import with_statement
5 """
96 from faraday.client.plugins import core
107 from faraday.client.model import api
118 import re
2623 __status__ = "Development"
2724
2825
29 class WebfuzzerParser(object):
26 class WebfuzzerParser:
3027 """
3128 The objective of this class is to parse an xml file generated by the webfuzzer tool.
3229
6865 1), 'url': info.group(2), 'resp': info.group(3)}
6966 self.items.append(vuln)
7067
71 except SyntaxError, err:
72 print "SyntaxError: %s. %s" % (err, filepath)
68 except SyntaxError as err:
69 print("SyntaxError: %s. %s" % (err, self.filepath))
7370 return None
7471
7572
7976 """
8077
8178 def __init__(self):
82 core.PluginBase.__init__(self)
79 super().__init__()
8380 self.id = "Webfuzzer"
8481 self.name = "Webfuzzer Output Plugin"
8582 self.plugin_version = "0.0.2"
139136 'resp'],
140137 method=item['method'], website=parser.hostname)
141138
142 n_id = self.createAndAddNoteToService(h_id, s_id, "website", "")
143 n2_id = self.createAndAddNoteToNote(
144 h_id, s_id, n_id, parser.hostname, "")
145
146139 del parser
147140
148141 return True
155148 if host is not None:
156149 self.host = host.group(2)
157150 self._output_path = current_path + "/" + self.host + ".txt"
158 return None
159151
160152
161153 def createPlugin():
162154 return WebfuzzerPlugin()
163155
164 if __name__ == '__main__':
165 parser = WebfuzzerParser(sys.argv[1])
166 for item in parser.items:
167 if item.status == 'up':
168 print item
156
157
158 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
30 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74 """
8
9 from __future__ import with_statement
105 from faraday.client.plugins import core
116 from faraday.client.plugins.plugin_utils import get_vulnweb_url_fields
12 from faraday.client.model import api
137 import re
148
159 try:
8276 "reference" : []}
8377 }
8478
85 for tag, obj_property in map_objects_fields.iteritems():
79 for tag, obj_property in map_objects_fields.items():
8680
8781 value = self.return_text(tag,issue)
8882
122116 """
123117
124118 def __init__(self):
125 core.PluginBase.__init__(self)
119 super().__init__()
126120 self.id = "Webinspect"
127121 self.name = "Webinspect"
128122 self.plugin_version = "0.0.1"
155149 query=get_vulnweb_url_fields(vuln.get("Vuln").get("website")).get("query"),
156150 method=vuln.get("Vuln").get("method"),
157151 request=vuln.get("Vuln").get("request"),
158 ref=filter(None ,vuln.get("Vuln").get("reference")),
152 ref=list(filter(None ,vuln.get("Vuln").get("reference"))),
159153 response=vuln.get("Vuln").get("response"),
160154 desc=cleanhtml(vuln.get("Vuln").get("description")),
161155 resolution=cleanhtml(vuln.get("Vuln").get("resolution")),
169163
170164 def createPlugin():
171165 return WebInspectPlugin()
166 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
00 import re
1 import sys
21 import string
3 from urlparse import urljoin, urlparse
2 from urllib.parse import urljoin, urlparse
43
54 from faraday.client.plugins import core
65
87 class WfuzzPlugin(core.PluginBase):
98
109 def __init__(self):
11 core.PluginBase.__init__(self)
10 super().__init__()
1211 self.id = "Wfuzz"
1312 self.name = "Wfuzz Plugin"
1413 self.plugin_version = "0.0.1"
104103 parser = WfuzzPlugin()
105104 with open("/home/javier/salida", "r") as report:
106105 parser.parseOutputString(report.read())
106
107
108 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
5 """
96 from faraday.client.plugins import core
107 from faraday.client.model import api
118 import re
3128 """
3229
3330 def __init__(self):
34 core.PluginBase.__init__(self)
31 super().__init__()
3532 self.id = "whois"
3633 self.name = "Whois"
3734 self.plugin_version = "0.0.1"
9895
9996 def createPlugin():
10097 return CmdWhoisPlugin()
98
99
100 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66
7 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9
5 """
106 from faraday.client.plugins import core
117 import re
128 import os
3329 Right now the plugin doesnt support being executed from another folder,
3430 like /dir/wpscan.rb
3531 """
36 core.PluginBase.__init__(self)
32 super().__init__()
3733 self.id = "wpscan"
3834 self.name = "WPscan"
3935 self.plugin_version = "0.2"
263259
264260 def createPlugin():
265261 return WPScanPlugin()
262
263
264 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
74
8 '''
9 from __future__ import with_statement
10 from faraday.client.plugins import core
5 """
6 from faraday.client.plugins.plugin import PluginXMLFormat
117 from faraday.client.model import api
128 import re
139 import os
14 import pprint
1510 import sys
1611
1712 try:
3631 __status__ = "Development"
3732
3833
39 class X1XmlParser(object):
34 class X1XmlParser:
4035 """
4136 The objective of this class is to parse an xml file generated by the x1 tool.
4237
5146
5247 tree = self.parse_xml(xml_output)
5348 if tree:
54 self.items = [data for data in self.get_items(tree)]
49 self.items = list(self.get_items(tree))
5550 else:
5651 self.items = []
5752
6661 """
6762 try:
6863 tree = ET.fromstring(xml_output)
69 except SyntaxError, err:
70 print "SyntaxError: %s. %s" % (err, xml_output)
64 except SyntaxError as err:
65 print("SyntaxError: %s. %s" % (err, xml_output))
7166 return None
7267
7368 return tree
8176 yield Item(node)
8277
8378
84 class Item(object):
79 class Item:
8580 """
8681 An abstract representation of a Item
8782
155150 return None
156151
157152
158 class X1Plugin(core.PluginBase):
153 class X1Plugin(PluginXMLFormat):
159154 """
160155 Example plugin to parse x1 output.
161156 """
162157
163158 def __init__(self):
164 core.PluginBase.__init__(self)
159 super().__init__()
160 self.identifier_tag = ["session", "landscapePolicy"]
165161 self.id = "X1"
166162 self.name = "Onapsis X1 XML Output Plugin"
167163 self.plugin_version = "0.0.1"
172168 self._command_regex = re.compile(r'^(sudo x1|\.\/x1).*?')
173169
174170 global current_path
175 self._output_file_path = os.path.join(self.data_path,
176 "x1_output-%s.xml" % self._rid)
171 self._output_file_path = os.path.join(self.data_path, "x1_output-%s.xml" % self._rid)
177172
178173 def parseOutputString(self, output, debug=False):
179174
208203 def createPlugin():
209204 return X1Plugin()
210205
211 if __name__ == '__main__':
212 parser = X1XmlParser(sys.argv[1])
213 for item in parser.items:
214 if item.status == 'up':
215 print item
206
207 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/python2.7
1 # -*- coding: utf-8 -*-
2
3 '''
0 """
41 Faraday Penetration Test IDE
52 Copyright (C) 2017 Infobyte LLC (http://www.infobytesec.com/)
63 See the file 'doc/LICENSE' for the license information
7 '''
8
4 """
95 import re
106 import socket
117 from faraday.client.plugins import core
1915 class xsssniper (core.PluginBase):
2016
2117 def __init__(self):
22 core.PluginBase.__init__(self)
18 super().__init__()
2319 self.id = "xsssniper"
2420 self.name = "xsssniper"
2521 self.plugin_version = "0.0.1"
6157 return xsssniper()
6258
6359
64 if __name__ == '__main__':
65 plugin_xss = xsssniper()
66 with open('xsssniper_out', 'r') as xsssniper_file:
67 plugin_xss.parseOutputString(xsssniper_file.read())
60 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 '''
0 """
31 Faraday Penetration Test IDE
42 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
53 See the file 'doc/LICENSE' for the license information
6 '''
7
8 from __future__ import with_statement
4 """
95 import re
106 import os
117 import socket
128 import sys
13 from faraday.client.plugins import core
9 from faraday.client.plugins.plugin import PluginXMLFormat
1410
1511
1612 try:
4238 self._data.append(data.encode("ascii", errors="backslashreplace"))
4339
4440
45 class ZapXmlParser(object):
41 class ZapXmlParser:
4642 """
4743 The objective of this class is to parse an xml
4844 file generated by the zap tool.
6157 tree = self.parse_xml(xml_output)
6258
6359 if tree is not None:
64 self.sites = [data for data in self.get_items(tree)]
60 self.sites = list(self.get_items(tree))
6561 else:
6662 self.sites = []
6763
7975 parser.feed(xml_output)
8076 tree = parser.close()
8177
82 except SyntaxError, err:
83 print "SyntaxError: %s. %s" % (err, xml_output)
78 except SyntaxError as err:
79 print("SyntaxError: %s. %s" % (err, xml_output))
8480 return None
8581
8682 return tree
131127 return None
132128
133129
134 class Site(object):
130 class Site:
135131
136132 def __init__(self, item_node):
137133
166162 return host
167163
168164
169 class Item(object):
165 class Item:
170166 """
171167 An abstract representation of a Item
172168
258254 return None
259255
260256
261 class ZapPlugin(core.PluginBase):
257 class ZapPlugin(PluginXMLFormat):
262258 """
263259 Example plugin to parse zap output.
264260 """
265261
266262 def __init__(self):
267
268 core.PluginBase.__init__(self)
263 super().__init__()
264 self.identifier_tag = "OWASPZAPReport"
269265 self.id = "Zap"
270266 self.name = "Zap XML Output Plugin"
271267 self.plugin_version = "0.0.3"
278274
279275 global current_path
280276
281 self._output_file_path = os.path.join(
282 self.data_path,
283 "zap_output-%s.xml" % self._rid
284 )
277 self._output_file_path = os.path.join(self.data_path, "zap_output-%s.xml" % self._rid)
285278
286279 def parseOutputString(self, output, debug=False):
287280 """
317310 ports=[site.port],
318311 status='open'
319312 )
320
321 n_id = self.createAndAddNoteToService(h_id, s_id, "website", "")
322 n2_id = self.createAndAddNoteToNote(
323 h_id, s_id, n_id, site.host, "")
324313
325314 for item in site.items:
326315 v_id = self.createAndAddVulnWebToService(
349338 def createPlugin():
350339 return ZapPlugin()
351340
352 if __name__ == '__main__':
353 parser = ZapXmlParser(sys.argv[1])
354 for item in parser.items:
355 if item.status == 'up':
356 print item
341
342
343 # I'm Py3
0 #!/usr/bin/env python2.7
1 '''
0 #!/usr/bin/env python3
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
5 '''
5 """
6
7 from __future__ import absolute_import
8 from __future__ import print_function
9
10 from builtins import input
611
712 import os
813 import sys
1015 import shutil
1116 import getpass
1217 import argparse
18 import requests
1319 import requests.exceptions
1420 import logging
1521
3844 from faraday.client.persistence.server import server
3945 from faraday.client.persistence.server.server import login_user, get_user_info
4046
47 import faraday
48
49 from colorama import Fore, Back, Style
50
4151 USER_HOME = os.path.expanduser(CONST_USER_HOME)
4252 # find_module returns if search is successful, the return value is a 3-element tuple (file, pathname, description):
43 FARADAY_BASE = imp.find_module("faraday")[1]
53 FARADAY_BASE = os.path.dirname(faraday.__file__)
4454 os.path.dirname(os.path.dirname(os.path.realpath(__file__))) # Use double dirname to obtain parent directory
4555 FARADAY_CLIENT_BASE = os.path.join(FARADAY_BASE, 'client')
4656
246256 Returns application status.
247257
248258 """
249 from faraday.client.model.application import MainApplication
259 from faraday.client.model.application import MainApplication # pylint:disable=import-outside-toplevel
250260
251261 logger.info("All done. Opening environment.")
252262 # TODO: Handle args in CONF and send only necessary ones.
260270 logger.info("Starting main application.")
261271 start = main_app.start
262272
263 from colorama import Fore, Back, Style
264273 serverURL = getInstanceConfiguration().getServerURI()
265274 if serverURL:
266275 url = "%s/_ui" % serverURL
391400 """
392401 Prints Faraday's ascii banner.
393402 """
394 from colorama import Fore, Back, Style
395403 print (Fore.RED + """
396404 _____ .___
397405 _/ ____\_____ ____________ __| _/_____ ___.__.
408416
409417
410418 def checkUpdates():
411 import requests
412419 uri = getInstanceConfiguration().getUpdatesUri()
413420 resp = u"OK"
414421 try:
467474 return
468475
469476 if old_server_url is None:
470 new_server_url = raw_input(
477 new_server_url = input(
471478 "\nPlease enter the Faraday Server URL (Press enter for http://localhost:5985): ") or "http://localhost:5985"
472479 else:
473 new_server_url = raw_input(
480 new_server_url = input(
474481 "\nPlease enter the Faraday Server URL (Press enter for last used: {}): ".format(old_server_url)) or old_server_url
475482
476483 CONF.setAPIUrl(new_server_url)
479486
480487 for attempt in range(1, 4):
481488
482 api_username = raw_input("Username (press enter for faraday): ") or "faraday"
489 api_username = input("Username (press enter for faraday): ") or "faraday"
483490 api_password = getpass.getpass('Password: ')
484491
485492 session_cookie = try_login_user(new_server_url, api_username, api_password)
553560
554561 if __name__ == '__main__':
555562 main()
563
564
565 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
4141 cmd_encoded=$(printf "%s" "$BUFFER"| faraday_b64)
4242 json_response=`curl -s -X POST -H "Content-Type: application/json" -d "{\"cmd\": \"$cmd_encoded\", \"pid\": $$, \"pwd\": \"$pwd_actual\"}" http://$FARADAY_ZSH_HOST:$FARADAY_ZSH_RPORT/cmd/input`
4343 if [[ $? -eq 0 ]]; then
44 code=`echo $json_response|env python2.7 -c "import sys, json;print(json.load(sys.stdin)[\"code\"])"`
44 code=`echo $json_response|env python3 -c "import sys, json;print(json.load(sys.stdin)[\"code\"])"`
4545 if [[ "$code" == "200" ]]; then
46 FARADAY_PLUGIN=`echo $json_response | env python2.7 -c "import sys, json; print(json.load(sys.stdin)[\"plugin\"])"`
47 new_cmd=`echo $json_response | env python2.7 -c "import sys, json; print(json.load(sys.stdin)[\"cmd\"])"`
46 FARADAY_PLUGIN=`echo $json_response | env python3 -c "import sys, json; print(json.load(sys.stdin)[\"plugin\"])"`
47 new_cmd=`echo $json_response | env python3 -c "import sys, json; print(json.load(sys.stdin)[\"cmd\"])"`
4848 if [[ "$new_cmd" != "None" ]]; then
4949 BUFFER=" $new_cmd"
5050 fi
5757
5858 function send-output() {
5959 if [ ! -z "$FARADAY_PLUGIN" ]; then
60 output=`env python2.7 -c "import base64; print(base64.b64encode(open(\"$FARADAY_OUTPUT\",'r').read()))"`
60 output=`base64 "$FARADAY_OUTPUT"`
6161 temp_file=`mktemp tmp.XXXXXXXXXXXXXXXXXXXXXXXXXXXXX`
6262 echo "{\"exit_code\": $?, \"pid\": $$, \"output\": \"$output\" }" >> $temp_file
6363 curl=`curl -s -X POST -H "Content-Type: application/json" -d @$temp_file http://$FARADAY_ZSH_HOST:$FARADAY_ZSH_RPORT/cmd/output`
0 #!/usr/bin/env python2.7
1 '''
0 #!/usr/bin/env python3
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
6 """
7 from __future__ import absolute_import
8 from __future__ import print_function
79
810 import json
911 import requests
5355 except:
5456 response = ''
5557 finally:
56 print response
58 print(response)
5759 return 0
5860
5961 def gen_output(pid):
60 print "%s/%s.%s.output" % (output_folder, pid, uuid.uuid4())
62 print("%s/%s.%s.output" % (output_folder, pid, uuid.uuid4()))
6163 return 0
6264
6365 def send_output(pid, exit_code, output_file):
7476 data=json.dumps(data),
7577 headers=headers)
7678 if response.status_code != 200:
77 print response.json()
79 print(response.json())
7880 return -1
7981 return 0
8082
104106
105107 if __name__ == '__main__':
106108 main(sys.argv)
109 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 __all__ = []
7 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 import os
77 import json
88 import shutil
694694 else:
695695 the_config = Configuration(os.path.join(config_dir, "config.xml"))
696696 return the_config
697
698
699 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
6
5 """
76 import os
87
98 CONST_REQUIREMENTS_FILE = 'requirements.txt'
2928 CONST_USER_HOME = "~"
3029 CONST_USER_ZSHRC = "~/.zshrc"
3130 CONST_ZSH_PATH = "zsh"
31
32
33 # I'm Py3
11 <faraday>
22
33 <appname>Faraday - Penetration Test IDE</appname>
4 <version>3.9.3</version>
4 <version>3.10.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>
00 #!/usr/bin/env python
1 '''
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
7
6 """
87 import os
98 import re
109 import sys
1110 import platform
12
11 import logging
1312 # If is linux and its installed with deb or rpm, it must run with a user in the faraday group
1413 if platform.system() == "Linux":
1514 import grp
2928 import click
3029 import requests
3130 import alembic.command
32 from urlparse import urlparse
31 from pgcli.main import PGCli
32 from requests import ConnectionError
33 from urllib.parse import urlparse
3334 from alembic.config import Config
34 from sqlalchemy.exc import ProgrammingError
35 from sqlalchemy.exc import ProgrammingError, OperationalError
3536
3637 import faraday.server.config
3738 from faraday.server.config import FARADAY_BASE
4041 from faraday.server.commands.initdb import InitDB
4142 from faraday.server.commands.faraday_schema_display import DatabaseSchema
4243 from faraday.server.commands.app_urls import show_all_urls
43 from faraday.server.commands.reports import import_external_reports
4444 from faraday.server.commands import status_check as status_check_functions
4545 from faraday.server.commands import change_password as change_pass
4646 from faraday.server.commands.custom_fields import add_custom_field_main, delete_custom_field_main
4747 from faraday.server.commands import support as support_zip
4848 from faraday.server.commands import change_username
4949 from faraday.server.models import db, User
50 from faraday.server.importer import ImportCouchDB
5150 from faraday.server.web import app
5251
5352 CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
5453
54 logger = logging.getLogger(__name__)
5555
5656 @click.group(context_settings=CONTEXT_SETTINGS)
5757 def cli():
6060
6161 def check_faraday_server(url):
6262 return requests.get(url)
63
64
65 @click.command(help="Enable importation of plugins reports in ~/.faraday folder")
66 @click.option('--debug/--no-debug', default=False)
67 @click.option('--workspace', default=None)
68 @click.option('--polling/--no-polling', default=True)
69 def process_reports(debug, workspace, polling):
70 try:
71 from requests import ConnectionError
72 except ImportError:
73 print('Python requests was not found. Please install it with: pip install requests')
74 sys.exit(1)
75 try:
76 from sqlalchemy.exc import OperationalError
77 except ImportError:
78 print('SQLAlchemy was not found please install it with: pip install sqlalchemy')
79 sys.exit(1)
80 configuration = _conf()
81 url = '{0}/_api/v2/info'.format(configuration.getServerURI() if FARADAY_UP else SERVER_URL)
82 with app.app_context():
83 try:
84 check_faraday_server(url)
85 import_external_reports(workspace, polling)
86 except OperationalError as ex:
87 print('{0}'.format(ex))
88 print('Please verify database is running or configuration on server.ini!')
89 except ConnectionError:
90 print('Can\'t connect to {0}. Please check if the server is running.'.format(url))
9163
9264
9365 @click.command(help="Show all URLs in Faraday Server API")
10375 def initdb(choose_password):
10476 with app.app_context():
10577 InitDB().run(choose_password=choose_password)
106 couchdb_config_present = faraday.server.config.couchdb
107 if couchdb_config_present and couchdb_config_present.user and couchdb_config_present.password:
108 print('Importing data from CouchDB, please wait...')
109 ImportCouchDB().run()
110 print('All users from CouchDB were imported. You can login with your old username/password to faraday now.')
111
112 @click.command(help="Import all your data from Couchdb Faraday databases")
113 def import_from_couchdb():
114 with app.app_context():
115 ImportCouchDB().run()
78
11679
11780 @click.command(help="Create a PNG image with Faraday model object")
11881 def database_schema():
11982 DatabaseSchema().run()
12083
84
12185 @click.command(help="Open a SQL Shell connected to postgresql 'Faraday DB'")
12286 def sql_shell():
123 try:
124 from pgcli.main import PGCli
125 except ImportError:
126 print('PGCli was not found, please install it with: pip install pgcli')
127 sys.exit(1)
12887 conn_string = faraday.server.config.database.connection_string.strip("'")
12988 conn_string = urlparse(conn_string)
13089 parsed_conn_string = ("user={username} password={password} host={hostname} dbname={dbname}"
169128
170129 sys.exit(exit_code)
171130
131
172132 @click.command(help="Changes the password of a user")
173133 @click.option('--username', required=True, prompt=True)
174134 @click.option('--password', required=True, prompt=True, confirmation_prompt=True, hide_input=True)
178138 except ProgrammingError:
179139 print('\n\nMissing migrations, please execute: \n\nfaraday-manage migrate')
180140 sys.exit(1)
141
142
181143 def validate_user_unique_field(ctx, param, value):
182144 with app.app_context():
183145 if User.query.filter_by(**{param.name: value}).count():
196158 def list_plugins():
197159 plugins_list = [name for name in os.listdir(FARADAY_PLUGINS_BASEPATH)
198160 if os.path.isdir(os.path.join(FARADAY_PLUGINS_BASEPATH, name))]
199 print '\n'.join(sorted(plugins_list))
161 print('\n'.join(sorted(plugins_list)))
200162
201163 @click.command(help="Create ADMIN user for Faraday application")
202164 @click.option('--username', prompt=True, callback=validate_user_unique_field)
250212 required=False,
251213 )
252214 def migrate(downgrade, revision):
253 revision = revision or ("-1" if downgrade else "head")
254 config = Config(os.path.join(FARADAY_BASE,"alembic.ini"))
255 os.chdir(FARADAY_BASE)
256 if downgrade:
257 alembic.command.downgrade(config, revision)
215 logger.info("Running migrations")
216 try:
217 revision = revision or ("-1" if downgrade else "head")
218 config = Config(os.path.join(FARADAY_BASE,"alembic.ini"))
219 os.chdir(FARADAY_BASE)
220 if downgrade:
221 alembic.command.downgrade(config, revision)
222 else:
223 alembic.command.upgrade(config, revision)
224 except OperationalError as e:
225 logger.error("Migration Error: %s", e)
226 print('Please verify your configuration on server.ini or the hba configuration!')
227 except Exception as e:
228 logger.exception("Migration Error: %s", e)
229 print('Migration failed! Please check the logs')
230 sys.exit(1)
258231 else:
259 alembic.command.upgrade(config, revision)
232 logger.info("Migrations finished")
260233
261234
262235 @click.command(help='Custom field wizard')
280253 change_username.change_username(current_username, new_username)
281254
282255
283 cli.add_command(process_reports)
284256 cli.add_command(show_urls)
285257 cli.add_command(initdb)
286 cli.add_command(import_from_couchdb)
287258 cli.add_command(database_schema)
288259 cli.add_command(create_superuser)
289260 cli.add_command(sql_shell)
298269 cli.add_command(rename_user)
299270
300271 if __name__ == '__main__':
272
301273 cli()
274
275
276 # I'm Py3
0 from __future__ import absolute_import
01 from __future__ import with_statement
12
3 import logging
24 import sys
35 import os
46 sys.path.append(os.getcwd())
2224 # for 'autogenerate' support
2325 # from myapp import mymodel
2426 target_metadata = db.metadata
27 alembic_logger = logging.getLogger('alembic.runtime.migration')
28 LOG_FILE = os.path.expanduser(os.path.join(
29 faraday.server.config.CONSTANTS.CONST_FARADAY_HOME_PATH,
30 faraday.server.config.CONSTANTS.CONST_FARADAY_LOGS_PATH, 'alembic.log'))
31 fh = logging.FileHandler(LOG_FILE)
32 fh.setLevel(logging.INFO)
33 alembic_logger.addHandler(fh)
34
2535 # target_metadata = None
2636
2737 # other values from the config, defined by the needs of env.py,
7383 run_migrations_offline()
7484 else:
7585 run_migrations_online()
86 # I'm Py3
44 Create Date: 2019-06-18 18:07:41.834191+00:00
55
66 """
7 from __future__ import absolute_import
8
79 from alembic import op
810 import sqlalchemy as sa
911
182184 op.drop_table('rule_action')
183185 op.drop_table('action')
184186 op.drop_table('rule')
187
188
189 # I'm Py3
44 Create Date: 2019-04-26 20:17:48.639684+00:00
55
66 """
7 from __future__ import absolute_import
78 from alembic import op
89 import sqlalchemy as sa
910
6869 op.drop_table('notification')
6970 #op.drop_constraint(None, 'notification_user_id_fkey', type_='foreignkey')
7071 #op.drop_constraint(None, 'notification_workspace_id_fkey', type_='foreignkey')
72 # I'm Py3
44 Create Date: 2019-04-05 16:19:11.216571+00:00
55
66 """
7 from __future__ import absolute_import
78 import json
89 from alembic import op
910 import sqlalchemy as sa
7475 'vuln_id': vuln_id
7576 })
7677
78 # I'm Py3
0 """add searchfilter table
1
2 Revision ID: 2a0de6132377
3 Revises: 1dbe9e8e4247
4 Create Date: 2019-10-16 19:58:25.709347+00:00
5
6 """
7 from alembic import op
8 import sqlalchemy as sa
9
10
11 # revision identifiers, used by Alembic.
12 revision = '2a0de6132377'
13 down_revision = '3f771124f0a2'
14 branch_labels = None
15 depends_on = None
16
17
18 def upgrade():
19 # ### commands auto generated by Alembic - please adjust! ###
20 op.create_table('search_filter',
21 sa.Column('create_date', sa.DateTime(), nullable=True),
22 sa.Column('update_date', sa.DateTime(), nullable=True),
23 sa.Column('id', sa.Integer(), nullable=False),
24 sa.Column('name', sa.String(), nullable=False),
25 sa.Column('json_query', sa.String(), nullable=False),
26 sa.Column('user_query', sa.String(), nullable=False),
27 sa.Column('creator_id', sa.Integer(), nullable=True),
28 sa.Column('update_user_id', sa.Integer(), nullable=True),
29 sa.ForeignKeyConstraint(['creator_id'], ['faraday_user.id'], ondelete='SET NULL'),
30 sa.ForeignKeyConstraint(['update_user_id'], ['faraday_user.id'], ondelete='SET NULL'),
31 sa.PrimaryKeyConstraint('id')
32 )
33
34
35 def downgrade():
36 op.drop_table('search_filter')
44 Create Date: 2019-01-15 13:02:21.000699+00:00
55
66 """
7 from __future__ import absolute_import
78 from alembic import op
89 import sqlalchemy as sa
910
2122
2223 def downgrade():
2324 op.drop_column('workspace', 'readonly')
25 # I'm Py3
44 Create Date: 2019-05-15 18:48:41.909650+00:00
55
66 """
7 from __future__ import absolute_import
78 from alembic import op
89 import sqlalchemy as sa
910
2324 def downgrade():
2425 conn = op.get_bind()
2526 conn.execute('ALTER TABLE custom_fields_schema DROP CONSTRAINT custom_fields_schema_field_name_key;')
27 # I'm Py3
0 """add metadata to cf schema
1
2 Revision ID: 3f771124f0a2
3 Revises: 1dbe9e8e4247
4 Create Date: 2019-10-24 14:47:47.177057+00:00
5
6 """
7 from alembic import op
8 import sqlalchemy as sa
9
10
11 # revision identifiers, used by Alembic.
12 revision = '3f771124f0a2'
13 down_revision = '1dbe9e8e4247'
14 branch_labels = None
15 depends_on = None
16
17
18 def upgrade():
19 op.add_column('custom_fields_schema', sa.Column('field_metadata', sa.JSON, nullable=True))
20
21
22 def downgrade():
23 op.drop_column('custom_fields_schema', 'field_metadata')
44 Create Date: 2019-03-27 19:26:28.354078+00:00
55
66 """
7 from __future__ import absolute_import
78 from alembic import op
89 import sqlalchemy as sa
910
2122
2223 def downgrade():
2324 op.drop_column('executive_report', 'markdown')
25 # I'm Py3
44 Create Date: 2019-06-18 15:38:31.879725+00:00
55
66 """
7 from __future__ import absolute_import
8
79 from alembic import op
810 import sqlalchemy as sa
911
2527 conn = op.get_bind()
2628 conn.execute('ALTER TABLE vulnerability DROP COLUMN external_id')
2729 conn.execute('ALTER TABLE vulnerability_template DROP COLUMN external_id')
30
31
32 # I'm Py3
44 Create Date: 2018-11-29 16:34:44.081899+00:00
55
66 """
7 from __future__ import absolute_import
78 from alembic import op
89 import sqlalchemy as sa
910
2930 op.drop_column('faraday_user', 'otp_secret')
3031 op.drop_column('faraday_user', 'state_otp')
3132 op.execute('DROP TYPE user_otp_states')
33 # I'm Py3
0 """Create Executor table
1
2 Revision ID: 904a517a2f0c
3 Revises: 1dbe9e8e4247
4 Create Date: 2019-11-05 16:31:27.186749+00:00
5
6 """
7 from alembic import op
8 import sqlalchemy as sa
9
10
11 # revision identifiers, used by Alembic.
12 revision = '904a517a2f0c'
13 down_revision = '2a0de6132377'
14 branch_labels = None
15 depends_on = None
16
17
18 def upgrade():
19 op.create_table(
20 'executor',
21 sa.Column('id', sa.Integer, primary_key=True),
22 sa.Column('name', sa.String, nullable=False),
23 sa.Column('agent_id', sa.Integer, nullable=False),
24 sa.Column('parameters_metadata', sa.JSON, nullable=False, default={}),
25 sa.Column('create_date', sa.DateTime),
26 sa.Column('update_date', sa.DateTime),
27 sa.Column('creator_id', sa.Integer),
28 sa.Column('update_user_id', sa.Integer)
29 )
30
31 op.create_foreign_key(
32 'executor_agent_id_fkey',
33 'executor',
34 'agent', ['agent_id'], ['id']
35 )
36
37 op.create_foreign_key(
38 'executor_creator_id_fkey',
39 'executor',
40 'faraday_user', ['creator_id'], ['id']
41 )
42
43 op.create_foreign_key(
44 'executor_update_user_id_fkey',
45 'executor',
46 'faraday_user', ['update_user_id'], ['id']
47 )
48
49 op.drop_column('agent_schedule', 'agent_id')
50 op.execute('DELETE FROM agent_schedule')
51 op.add_column('agent_schedule', sa.Column('executor_id', sa.Integer, nullable=False))
52 op.add_column('agent_schedule', sa.Column('parameters', sa.JSON, nullable=False, default={}))
53 op.create_foreign_key(
54 'agent_schedule_executor_id_fkey',
55 'agent_schedule',
56 'executor', ['executor_id'], ['id']
57 )
58
59 op.create_table(
60 'agent_execution',
61 sa.Column('id', sa.Integer, primary_key=True),
62 sa.Column('running', sa.Boolean, nullable=True),
63 sa.Column('successful', sa.Boolean, nullable=True),
64 sa.Column('message', sa.String, nullable=True),
65 sa.Column('executor_id', sa.Integer, nullable=False),
66 sa.Column('workspace_id', sa.Integer, nullable=False),
67 sa.Column('create_date', sa.DateTime),
68 sa.Column('update_date', sa.DateTime),
69 sa.Column('creator_id', sa.Integer),
70 sa.Column('update_user_id', sa.Integer)
71
72 )
73
74 op.create_foreign_key(
75 'agent_execution_executor_id_fkey',
76 'agent_execution',
77 'executor', ['executor_id'], ['id']
78 )
79
80 op.create_foreign_key(
81 'agent_execution_workspace_id_fkey',
82 'agent_execution',
83 'workspace', ['workspace_id'], ['id']
84 )
85
86 op.create_foreign_key(
87 'agent_execution_creator_id_fkey',
88 'agent_execution',
89 'faraday_user', ['creator_id'], ['id']
90 )
91
92 op.create_foreign_key(
93 'agent_execution_update_user_id_fkey',
94 'agent_execution',
95 'faraday_user', ['update_user_id'], ['id']
96 )
97
98 op.create_unique_constraint(
99 "uix_executor_table_agent_id_name",
100 "executor",
101 ["name", "agent_id"]
102 )
103
104
105 def downgrade():
106 op.add_column('agent_schedule', sa.Column('agent_id', sa.Integer, nullable=False))
107 op.create_foreign_key(
108 'agent_schedule_agent_id_fkey',
109 'agent_schedule',
110 'agent', ['agent_id'], ['id']
111 )
112
113 op.drop_column('agent_schedule', 'executor_id')
114 op.drop_column('agent_schedule', 'parameters')
115 op.drop_table('agent_execution')
116 op.drop_table('executor')
44 Create Date: 2019-05-22 19:17:31.444968+00:00
55
66 """
7 from __future__ import absolute_import
8
79 import uuid
810 from alembic import op
911 import sqlalchemy as sa
8284 def downgrade():
8385 op.drop_table('agent_schedule')
8486 op.drop_table('agent')
87
88
89 # I'm Py3
44 Create Date: 2019-05-14 18:12:52.724079+00:00
55
66 """
7 from __future__ import absolute_import
8
79 from alembic import op
810 import sqlalchemy as sa
911
2426 def downgrade():
2527 conn = op.get_bind()
2628 conn.execute('ALTER TABLE executive_report DROP COLUMN filter')
29
30
31 # I'm Py3
44 Create Date: 2018-10-23 15:43:52.612619+00:00
55
66 """
7 from __future__ import absolute_import
78 from alembic import op
89 import sqlalchemy as sa
910
3334 conn.execute('ALTER TABLE vulnerability DROP COLUMN custom_fields')
3435 conn.execute('ALTER TABLE vulnerability_template DROP COLUMN custom_fields')
3536 conn.execute('DROP TABLE custom_fields_schema')
37 # I'm Py3
33 ## See the file 'doc/LICENSE' for the license information
44 ###
55
6 # I'm Py3
00 import json
11 import logging
22 import socket
3 import urllib
3 from urllib.parse import urlencode
44
55 from requests.adapters import ConnectionError, ReadTimeout
66
189189 self._get(self._url('vulnerability_template/', True), 'templates')['rows']]
190190
191191 def filter_vulnerabilities(self, **kwargs):
192 if len(kwargs.keys()) > 1:
193 params = urllib.urlencode(kwargs)
192 if len(list(kwargs.keys())) > 1:
193 params = urlencode(kwargs)
194194 url = self._url('ws/{}/vulns/?{}'.format(self.workspace, params))
195195 else:
196196 params = self.parse_args(**kwargs)
199199 self._get(url, 'vulnerabilities')['vulnerabilities']]
200200
201201 def filter_services(self, **kwargs):
202 params = urllib.urlencode(kwargs)
202 params = urlencode(kwargs)
203203 url = self._url('ws/{}/services/?{}'.format(self.workspace, params), True)
204204 return [Structure(**item['value']) for item in
205205 self._get(url, 'services')['services']]
206206
207207 def filter_hosts(self, **kwargs):
208 params = urllib.urlencode(kwargs)
208 params = urlencode(kwargs)
209209 url = self._url('ws/{}/hosts/?{}'.format(self.workspace, params), True)
210210 return [Structure(**item['value']) for item in
211211 self._get(url, 'hosts')['rows']]
247247
248248 @staticmethod
249249 def parse_args(**kwargs):
250 if len(kwargs.keys()) > 0:
251 key = kwargs.keys()[0]
250 if len(list(kwargs.keys())) > 0:
251 key = list(kwargs.keys())[0]
252252 value = kwargs.get(key, '')
253253 item = '"name":"{}","op":"eq","val":"{}"'.format(key, value)
254254 params = 'filter?q={"filters":[{' + item + '}]}'
55 ## Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
66 ## See the file 'doc/LICENSE' for the license information
77 ###
8 from __future__ import absolute_import
9 from __future__ import print_function
10 from __future__ import division
11 from builtins import str
12 from imp import reload
813
914 import ast
1015 import json
99104 f_m2 = getattr(m2, field, None)
100105
101106 if f_m1 is not None and f_m2 is not None:
102 if field == 'severity' or field == 'owner' or field == 'status':
107 if field in ['severity', 'owner', 'status']:
103108 if f_m1 == f_m2:
104109 ratio = 1.0
105110 else:
106111 ratio = min_weight
107112
108 elif isinstance(f_m1, str) or isinstance(f_m1, unicode) and isinstance(f_m2, str) or isinstance(f_m2,
109 unicode):
113 elif isinstance(f_m1, str) or isinstance(f_m2, str):
110114 ratio = compare(f_m1.lower().replace('\n', ' '), f_m2.lower().replace('\n', ' '))
111115
112116 elif isinstance(f_m1, bool) and isinstance(f_m2, bool):
122126 count_fields += 1
123127
124128 if total_ratio != 0:
125 percent = (total_ratio * 100) / count_fields
129 percent = (total_ratio * 100.0) / count_fields
126130 else:
127131 percent = 0.0
128132 logger.debug("Verify result with %.2f %% evaluating rule %s:" % (percent, rule['id']))
139143 if is_same_level(model, md):
140144 environment.append(md)
141145 return environment
146
147
148 def process_models_by_similarity(api, _models, rule, mail_notificacion):
149 logger.debug("--> Start Process models by similarity")
150 for index_m1, m1 in zip(list(range(len(_models) - 1)), _models):
151 for index_m2, m2 in zip(list(range(index_m1 + 1, len(_models))), _models[index_m1 + 1:]):
152 if m1.id != m2.id and is_same_level(m1, m2):
153 if equals(m1, m2, rule):
154 environment = [m1, m2]
155 _objs_value = None
156 if 'object' in rule:
157 _objs_value = rule['object']
158 _object = get_object(environment, _objs_value)
159 if _object is not None:
160 if 'conditions' in rule:
161 environment = get_model_environment(m2, _models)
162 if can_execute_action(environment, rule['conditions']):
163 execute_action(api, _object, rule, mail_notificacion)
164 else:
165 execute_action(api, _object, rule, mail_notificacion)
166 logger.debug("<-- Finish Process models by similarity")
142167
143168
144169 def get_field(obj, field):
151176 except AttributeError:
152177 logger.error("ERROR: Field %s is invalid" % field)
153178 return None
179
180
181 def set_array(field, value, add=True):
182 if isinstance(field, list):
183 if add:
184 if value not in field:
185 field.append(value)
186 else:
187 if value in field:
188 field.remove(value)
189
190
191 def update_vulnerability(api, vuln, key, value):
192 if key == 'template':
193 cwe = get_cwe(api, value)
194 if cwe is None:
195 logger.error("%s: cwe not found" % value)
196 return False
197
198 vuln.name = cwe.name
199 vuln.description = cwe.description
200 vuln.desc = cwe.description
201 vuln.resolution = cwe.resolution
202
203 logger.info("Applying template '%s' to vulnerability '%s' with id '%s'" % (value, vuln.name, vuln.id))
204
205 elif key == 'confirmed':
206 value = value == 'True'
207 vuln.confirmed = value
208 logger.info("Changing property %s to %s in vulnerability '%s' with id %s" % (key, value, vuln.name, vuln.id))
209 elif key == 'owned':
210 value = value == 'True'
211 vuln.owned = value
212 logger.info("Changing property %s to %s in vulnerability '%s' with id %s" % (key, value, vuln.name, vuln.id))
213 else:
214 to_add = True
215 if key.startswith('-'):
216 key = key.strip('-')
217 to_add = False
218
219 is_custom_field = False
220 if key in vuln.custom_fields:
221 field = vuln.custom_fields
222 is_custom_field = True
223 else:
224 field = get_field(vuln, key)
225
226 if field is not None and is_custom_field is False:
227 if isinstance(field, str):
228 setattr(vuln, key, value)
229 logger.info(
230 "Changing property %s to %s in vulnerability '%s' with id %s" % (key, value, vuln.name, vuln.id))
231 else:
232 set_array(field, value, add=to_add)
233 action = 'Adding %s to %s list in vulnerability %s with id %s' % (value, key, vuln.name, vuln.id)
234 if not to_add:
235 action = 'Removing %s from %s list in vulnerability %s with id %s' % (
236 value, key, vuln.name, vuln.id)
237
238 logger.info(action)
239
240 if field is not None and is_custom_field is True:
241 vuln.custom_fields[key] = value
242 logger.info(
243 "Changing custom field %s to %s in vulnerability '%s' with id %s" % (key, value, vuln.name, vuln.id))
244
245 api.update_vulnerability(vuln)
246
247 logger.info("Done")
248 return True
249
250
251 def update_service(api, service, key, value):
252 if key == 'owned':
253 value = value == 'True'
254 service.owned = value
255 logger.info("Changing property %s to %s in service '%s' with id %s" % (key, value, service.name, service.id))
256 else:
257 to_add = True
258 if key.startswith('-'):
259 key = key.strip('-')
260 to_add = False
261
262 field = get_field(service, key)
263 if field is not None:
264 if isinstance(field, str):
265 setattr(service, key, value)
266 logger.info(
267 "Changing property %s to %s in service '%s' with id %s" % (key, value, service.name, service.id))
268 else:
269 set_array(field, value, add=to_add)
270 action = 'Adding %s to %s list in service %s with id %s' % (value, key, service.name, service.id)
271 if not to_add:
272 action = 'Removing %s from %s list in service %s with id %s' % (
273 value, key, service.name, service.id)
274
275 logger.info(action)
276
277 api.update_service(service)
278
279 logger.info("Done")
280 return True
281
282
283 def update_host(api, host, key, value):
284 if key == 'owned':
285 value = value == 'True'
286 host.owned = value
287 logger.info("Changing property %s to %s in host '%s' with id %s" % (key, value, host.name, host.id))
288 else:
289 to_add = True
290 if key.startswith('-'):
291 key = key.strip('-')
292 to_add = False
293
294 field = get_field(host, key)
295 if field is not None:
296 if isinstance(field, str):
297 setattr(host, key, value)
298 logger.info("Changing property %s to %s in host '%s' with id %s" % (key, value, host.name, host.id))
299 else:
300 set_array(field, value, add=to_add)
301 action = 'Adding %s to %s list in host %s with id %s' % (value, key, host.name, host.id)
302 if not to_add:
303 action = 'Removing %s from %s list in host %s with id %s' % (
304 value, key, host.name, host.id)
305
306 logger.info(action)
307 api.update_host(host)
308
309 logger.info("Done")
310 return True
311
312
313 def get_parent(api, parent_tag):
314 logger.debug("Getting parent")
315 return api.get_filtered_services(id=parent_tag, name=parent_tag) or \
316 api.get_filtered_hosts(id=parent_tag, name=parent_tag)
317
318
319 def filter_objects_by_parent(_objects, parent):
320 objects = []
321 parents = []
322 if isinstance(parent, list):
323 parents.extend(parent)
324 else:
325 parents.append(parent)
326 for obj in _objects:
327 for p in parents:
328 if p.id == obj.parent_id:
329 objects.append(obj)
330 return objects
154331
155332
156333 def evaluate_condition(model, condition):
263440 os.remove(".lock.pod")
264441 logger.info('Killed')
265442 sys.exit(0)
266
267443
268444 class Searcher:
269445 def __init__(self, api, mail_notification=None, tool_name='Searcher'):
478654 array_exp = expression.split('=')
479655 key = array_exp[0]
480656 value = str('=').join(array_exp[1:])
481 if object_type == 'Vulnerabilityweb' or object_type == 'Vulnerability_web' or object_type == 'Vulnerability':
657 if object_type in ['Vulnerabilityweb', 'Vulnerability_web', 'Vulnerability']:
482658 self._update_vulnerability(obj, key, value)
483659
484660 if object_type == 'Service':
488664 self._update_host(obj, key, value)
489665
490666 elif command == 'DELETE':
491 if object_type == 'Vulnerabilityweb' or object_type == 'Vulnerability_web' or object_type == 'Vulnerability':
667 if object_type in ['Vulnerabilityweb', 'Vulnerability_web', 'Vulnerability']:
492668 self.api.delete_vulnerability(obj.id)
493669 logger.info("Deleting vulnerability '%s' with id '%s':" % (obj.name, obj.id))
494670
558734 vuln.refs.append(value)
559735 elif field:
560736 if not is_custom_field:
561 if isinstance(field, (str, unicode)):
737 if isinstance(field, str):
562738 setattr(vuln, key, value)
563739 logger.info(
564740 "Changing property %s to %s in vulnerability '%s' with id %s" % (
599775
600776 field = get_field(service, key)
601777 if field is not None:
602 if isinstance(field, (str, unicode)):
778 if isinstance(field, str):
603779 setattr(service, key, value)
604780 logger.info(
605781 "Changing property %s to %s in service '%s' with id %s" % (
631807
632808 field = get_field(host, key)
633809 if field is not None:
634 if isinstance(field, (str, unicode)):
810 if isinstance(field, str):
635811 setattr(host, key, value)
636812 logger.info("Changing property %s to %s in host '%s' with id %s" % (key, value, host.ip, host.id))
637813 else:
649825
650826 def _process_models_by_similarity(self, _models, rule):
651827 logger.debug("--> Start Process models by similarity")
652 for index_m1, m1 in zip(range(len(_models) - 1), _models):
653 for index_m2, m2 in zip(range(index_m1 + 1, len(_models)), _models[index_m1 + 1:]):
828 for index_m1, m1 in zip(list(range(len(_models) - 1)), _models):
829 for index_m2, m2 in zip(list(range(index_m1 + 1, len(_models))), _models[index_m1 + 1:]):
654830 if m1.id != m2.id and is_same_level(m1, m2):
655831 if equals(m1, m2, rule):
656832 environment = [m1, m2]
746922
747923 if __name__ == "__main__":
748924 main()
925 # I'm Py3
0 from __future__ import absolute_import
1
02 import json
13 import logging
24 import socket
5658 data = self._command_info(duration)
5759 command = Command.query.get(command_id)
5860 if command:
59 for (key, value) in data.iteritems():
61 for (key, value) in data.items():
6062 setattr(command, key, value)
6163 self.session.commit()
6264
99101 vulnerabilities = []
100102 vulnerabilities_query = self.session.query(Vulnerability, Workspace.id).join(Workspace).filter(
101103 Workspace.name == self.workspace.name)
102 for attr, value in kwargs.iteritems():
104 for attr, value in kwargs.items():
103105 if attr == 'regex':
104106 vulnerabilities_query = vulnerabilities_query.filter(Vulnerability.name.op('~')(value))
105107 vulnerabilities = [vulnerability for vulnerability, pos in
112114 web_vulnerabilities = []
113115 web_vulnerabilities_query = self.session.query(VulnerabilityWeb, Workspace.id).join(Workspace).filter(
114116 Workspace.name == self.workspace.name)
115 for attr, value in kwargs.iteritems():
117 for attr, value in kwargs.items():
116118 if attr == 'regex':
117119 web_vulnerabilities_query = web_vulnerabilities_query.filter(VulnerabilityWeb.name.op('~')(value))
118120 web_vulnerabilities = [web_vulnerability for web_vulnerability, pos in
128130 services = []
129131 services_query = self.session.query(Service, Workspace.id).join(Workspace).filter(
130132 Workspace.name == self.workspace.name)
131 for attr, value in kwargs.iteritems():
133 for attr, value in kwargs.items():
132134 if attr == 'regex':
133135 services_query = services_query.filter(Service.name.op('~')(value))
134136 services = [service for service, pos in
144146 hosts = []
145147 hosts_query = self.session.query(Host, Workspace.id).join(Workspace).filter(
146148 Workspace.name == self.workspace.name)
147 for attr, value in kwargs.iteritems():
149 for attr, value in kwargs.items():
148150 if attr == 'regex':
149151 hosts_query = hosts_query.filter(Host.ip.op('~')(value))
150152 hosts = [host for host, pos in
159161 def filter_templates(self, **kwargs):
160162 templates = []
161163 templates_query = self.session.query(VulnerabilityTemplate)
162 for attr, value in kwargs.iteritems():
164 for attr, value in kwargs.items():
163165 if hasattr(VulnerabilityTemplate, attr):
164166 templates_query = templates_query.filter(getattr(VulnerabilityTemplate, attr) == str(value))
165 templates = [template for template in
166 templates_query.distinct(VulnerabilityTemplate.id)]
167 templates = list(templates_query.distinct(VulnerabilityTemplate.id))
167168
168169 return templates
169170
55 ## Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
66 ## See the file 'doc/LICENSE' for the license information
77 ###
8
98 import re
109 import json
1110 import logging
4241 if model in vfields and len(fields) != 0:
4342 for field in fields:
4443 if field not in vfields[model]:
45 print "ERROR: The field '%s' doesn't exist in model '%s'" % (field, model)
44 print("ERROR: The field '%s' doesn't exist in model '%s'" % (field, model))
4645 logger.error("The field '%s' doesn't exist in model '%s'" % (field, model))
4746 return False
4847 return True
197196
198197 logger.info('<-- Rules OK')
199198 return True
199 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 import json
43 import logging
54 import threading
65 from time import sleep
1413
1514 class TimerClass(threading.Thread):
1615 def __init__(self):
17 threading.Thread.__init__(self)
16 super().__init__(name="TimerClassThread")
1817 self.__event = threading.Event()
19
20 def sendNewstoLogGTK(self, json_response):
21
22 information = json.loads(json_response)
23
24 for news in information.get("news", []):
25 faraday.client.model.guiapi.notification_center.sendCustomLog(
26 "NEWS -" + news["url"] + "|" + news["description"])
2718
2819 def run(self):
2920 while not self.__event.is_set():
3021 try:
3122 sleep(5)
32 res = requests.get(
33 "https://portal.faradaysec.com/api/v1/license_check",
34 params={'version': faraday.__version__,
35 'key': 'white'},
36 timeout=1,
37 verify=True)
38
39 self.sendNewstoLogGTK(res.text)
23 res = requests.get("https://portal.faradaysec.com/api/v1/license_check",
24 params={'version': faraday.__version__, 'key': 'white'},
25 timeout=1,
26 verify=True)
4027 logger.info('License status {0}'.format(res.json().get('license_status', 'FAILED!')))
4128 except Exception as ex:
4229 logger.exception(ex)
4633 self.__event.wait(43200)
4734
4835 def stop(self):
49 self.__event.set()
36 self.__event.set()
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 import json
77 import logging
88
1919 from marshmallow.validate import Length
2020 from marshmallow_sqlalchemy import ModelConverter
2121 from marshmallow_sqlalchemy.schema import ModelSchemaMeta, ModelSchemaOpts
22 from webargs.flaskparser import FlaskParser, parser
22 from webargs.flaskparser import FlaskParser, parser as parser_imported
2323 from webargs.core import ValidationError
2424 from faraday.server.models import Workspace, db, Command, CommandObject
2525 from faraday.server.schemas import NullToBlankString
2828 is_unique_constraint_violation
2929 )
3030
31 from faraday.server.utils.py3 import BytesJSONEncoder
32
3133 logger = logging.getLogger(__name__)
3234
3335
3436 def output_json(data, code, headers=None):
3537 content_type = 'application/json'
36 dumped = json.dumps(data)
38 dumped = json.dumps(data, cls=BytesJSONEncoder)
3739 if headers:
3840 headers.update({'Content-Type': content_type})
3941 else:
385387 flask.abort(403, "Altering a readonly workspace is not allowed")
386388
387389
388 class ListMixin(object):
390 class ListMixin:
389391 """Add GET / route"""
390392
391393 #: If set (to a SQLAlchemy attribute instance) use this field to order the
435437 pagination_metadata)
436438
437439
438 class SortableMixin(object):
440 class SortableMixin:
439441 """Enables custom sorting by a field specified by the user
440442
441443 See the example of :ref:`pagination-and-sorting-recipe` to learn
524526 ))
525527
526528
527 class PaginatedMixin(object):
529 class PaginatedMixin:
528530 """Add pagination for list route"""
529531 per_page_parameter_name = 'page_size'
530532 page_number_parameter_name = 'page'
549551 return super(PaginatedMixin, self)._paginate(query)
550552
551553
552 class FilterAlchemyMixin(object):
554 class FilterAlchemyMixin:
553555 """Add querystring parameter filtering to list route
554556
555557 It is done by setting the ViewClass.filterset_class class
567569 """Add GET /<workspace_name>/<route_base>/ route"""
568570 # There are no differences with the non-workspaced implementations. The code
569571 # inside the view generic methods is enough
570 pass
571
572
573 class RetrieveMixin(object):
572
573
574 class RetrieveMixin:
574575 """Add GET /<id>/ route"""
575576
576577 def get(self, object_id, **kwargs):
582583 """Add GET /<workspace_name>/<route_base>/<id>/ route"""
583584 # There are no differences with the non-workspaced implementations. The code
584585 # inside the view generic methods is enough
585 pass
586586
587587
588588 class ReadOnlyView(SortableMixin,
594594 It is just a GenericView inheriting also from ListMixin,
595595 RetrieveMixin and SortableMixin.
596596 """
597 pass
598597
599598
600599 class ReadOnlyWorkspacedView(SortableMixin,
605604
606605 It is just a GenericWorkspacedView inheriting also from
607606 ListWorkspacedMixin, RetrieveWorkspacedMixin and SortableMixin"""
608 pass
609
610
611 class CreateMixin(object):
607
608
609 class CreateMixin:
612610 """Add POST / route"""
613611
614612 def post(self, **kwargs):
732730 return obj
733731
734732
735 class UpdateMixin(object):
733 class UpdateMixin:
736734 """Add PUT /<id>/ route"""
737735
738736 def put(self, object_id, **kwargs):
806804 object_id, obj, data, workspace_name)
807805
808806
809 class DeleteMixin(object):
807 class DeleteMixin:
810808 """Add DELETE /<id>/ route"""
811809 def delete(self, object_id, **kwargs):
812810 obj = self._get_object(object_id, **kwargs)
829827 obj, workspace_name)
830828
831829
832 class CountWorkspacedMixin(object):
830 class CountWorkspacedMixin:
833831 """Add GET /<workspace_name>/<route_base>/count/ route
834832
835833 Group objects by the field set in the group_by GET parameter. If it
892890 RetrieveMixin, SortableMixin, CreateMixin, UpdateMixin and
893891 DeleteMixin.
894892 """
895 pass
896893
897894
898895 class ReadWriteWorkspacedView(CreateWorkspacedMixin,
908905 CreateWorkspacedMixin, DeleteWorkspacedMixin and
909906 CountWorkspacedMixin.
910907 """
911 pass
912908
913909
914910 class CustomModelConverter(ModelConverter):
955951
956952 class FilterSetMeta:
957953 """Base Meta class of FilterSet objects"""
958 parser = parser
954 parser = parser_imported
959955 converter = FilterAlchemyModelConverter()
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66
7 # I'm Py3
8080
8181
8282 ActivityFeedView.register(activityfeed_api)
83 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2019 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 import json
4
5 import flask
36 import wtforms
47
58 from flask import Blueprint, abort, request
1013 from faraday.server.api.base import (AutoSchema, UpdateWorkspacedMixin, DeleteWorkspacedMixin,
1114 CountWorkspacedMixin, ReadOnlyWorkspacedView, CreateWorkspacedMixin,
1215 GenericWorkspacedView)
13 from faraday.server.models import Agent
14 from faraday.server.schemas import PrimaryKeyRelatedField
16 from faraday.server.models import Agent, Executor
17 from faraday.server.schemas import PrimaryKeyRelatedField, MutableField, SelfNestedField
1518 from faraday.server.config import faraday_server
1619 from faraday.server.events import changes_queue
1720
1821 agent_api = Blueprint('agent_api', __name__)
22
23
24 class ExecutorSchema(AutoSchema):
25
26 parameters_metadata = fields.Dict(
27 dump_only=True
28 )
29 id = fields.Integer(dump_only=True)
30 name = fields.String(dump_only=True)
31
32 class Meta:
33 model = Executor
34 fields = (
35 'id',
36 'name',
37 'parameters_metadata',
38 )
1939
2040
2141 class AgentSchema(AutoSchema):
2646 create_date = fields.DateTime(dump_only=True)
2747 update_date = fields.DateTime(dump_only=True)
2848 is_online = fields.Boolean(dump_only=True)
49 executors = fields.Nested(ExecutorSchema(), dump_only=True, many=True)
2950
3051 class Meta:
3152 model = Agent
4061 'token',
4162 'is_online',
4263 'active',
64 'executors'
4365 )
4466
4567
6789 return agent
6890
6991
92 class ExecutorDataSchema(Schema):
93 executor = fields.String(default=None)
94 args = fields.Dict(default=None)
95
96
97 class AgentRunSchema(Schema):
98 executorData = fields.Nested(ExecutorDataSchema(), required=True)
99
100
70101 class AgentView(UpdateWorkspacedMixin,
71102 DeleteWorkspacedMixin,
72103 CountWorkspacedMixin,
74105 route_base = 'agents'
75106 model_class = Agent
76107 schema_class = AgentSchema
77 get_joinedloads = [Agent.creator]
108 get_joinedloads = [Agent.creator, Agent.executors]
78109
79110 @route('/<int:agent_id>/run/', methods=['POST'])
80111 def run_agent(self, workspace_name, agent_id):
81 try:
82 validate_csrf(request.form.get('csrf_token'))
83 except wtforms.ValidationError:
84 abort(403)
112 if flask.request.content_type != 'application/json':
113 abort(400, "Only application/json is a valid content-type")
114 data = self._parse_data(AgentRunSchema(strict=True), request)
85115 agent = self._get_object(agent_id, workspace_name)
116 executor_data = data['executorData']
86117 changes_queue.put({
87118 'agent_id': agent.id,
88119 'action': 'RUN',
120 "executor": executor_data.get('executor'),
121 "args": executor_data.get('args')
89122 })
90 return 'OK'
123 return flask.jsonify({
124 'successful': True,
125 })
91126
92127
93128 AgentView.register(agent_api)
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2019 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 import random
4 import string
5 from ConfigParser import ConfigParser
6
73 import flask
84 from flask import Blueprint
95 from flask_wtf.csrf import validate_csrf
139 GenericView,
1410 )
1511 from faraday.server.config import faraday_server
16 from faraday.server.config import LOCAL_CONFIG_FILE
1712
1813
1914 agent_auth_token_api = Blueprint('agent_auth_token_api', __name__)
3227 {'token': faraday_server.agent_token}).data
3328
3429 def post(self):
35 from faraday.server.app import save_new_agent_creation_token
30 from faraday.server.app import save_new_agent_creation_token # pylint:disable=import-outside-toplevel
3631 try:
3732 validate_csrf(flask.request.form.get('csrf_token'))
3833 except ValidationError:
4338
4439
4540 AgentAuthTokenView.register(agent_auth_token_api)
41
42
43 # I'm Py3
2020 )
2121 from faraday.server.utils.database import (
2222 get_conflict_object,
23 is_unique_constraint_violation
24 )
23 is_unique_constraint_violation,
24 get_object_type_for)
2525 from faraday.server.api.modules import (
2626 hosts,
2727 services,
140140
141141 I don't need that here, so I'll write a schema from scratch."""
142142
143 duration = fields.TimeDelta('seconds', required=True)
143 duration = fields.TimeDelta('microseconds', required=True)
144144
145145 class Meta:
146146 model = Command
159159 hosts = fields.Nested(
160160 HostSchema(many=True),
161161 many=True,
162 missing=[],
162 required=True,
163163 )
164164 command = fields.Nested(
165165 CommandSchema(),
236236
237237 def _create_command_object_for(ws, created, obj, command):
238238 assert command is not None
239 db.session.add(CommandObject(
240 obj,
241 command=command,
242 created_persistent=created,
243 workspace=ws))
239 data = {
240 'object_id': obj.id,
241 'object_type': get_object_type_for(obj),
242 'command': command,
243 'created_persistent': created,
244 'workspace': ws,
245 }
246 get_or_create(ws, CommandObject, data)
244247 db.session.commit()
245248
246249
288291 if command is not None:
289292 _create_command_object_for(ws, created, vuln, command)
290293
291 if created:
294 def update_vuln(policyviolations, references, vuln):
292295 vuln.references = references
293296 vuln.policyviolations = policyviolations
294297 # TODO attachments
295298 db.session.add(vuln)
296299 db.session.commit()
300
301 if created:
302 update_vuln(policyviolations, references, vuln)
303 elif vuln.status == "closed": # Implicit not created
304 vuln.status = "re-opened"
305 update_vuln(policyviolations, references, vuln)
297306
298307
299308 def _create_hostvuln(ws, host, vuln_data, command=None):
334343 post.is_public = True
335344
336345 BulkCreateView.register(bulk_create_api)
346
347
348 # I'm Py3
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
33 import time
4
54 import datetime
65 from flask import Blueprint
76 from flask_classful import route
8 from marshmallow import fields, post_load
7 from marshmallow import fields, post_load, ValidationError
98
109 from faraday.server.api.base import AutoSchema, ReadWriteWorkspacedView, PaginatedMixin
1110 from faraday.server.models import Command, Workspace
2221 creator = PrimaryKeyRelatedField('username', dump_only=True)
2322
2423 def load_itime(self, value):
25 return datetime.datetime.fromtimestamp(value)
24 try:
25 return datetime.datetime.fromtimestamp(value)
26 except ValueError:
27 raise ValidationError('Invalid Itime Value')
2628
2729 def get_itime(self, obj):
2830 return time.mktime(obj.start_date.utctimetuple()) * 1000
8991 return res
9092
9193 CommandView.register(commandsrun_api)
94 # I'm Py3
7979
8080 CommentView.register(comment_api)
8181 UniqueCommentView.register(comment_api)
82 # I'm Py3
126126
127127
128128 CredentialView.register(credentials_api)
129 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3
43 from flask import Blueprint
54
6 from faraday.server.models import db, CustomFieldsSchema
5 from faraday.server.models import CustomFieldsSchema
76 from faraday.server.api.base import (
87 AutoSchema,
98 ReadWriteView,
2019 fields = ('id',
2120 'field_name',
2221 'field_type',
22 'field_metadata',
2323 'field_display_name',
2424 'field_order',
2525 'table_name'
4141 return super(CustomFieldsSchemaView, self)._update_object(obj, data)
4242
4343 CustomFieldsSchemaView.register(custom_fields_schema_api)
44 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3
43 import flask
54 import requests
65 import logging
76
87 from flask import Blueprint, abort, make_response, jsonify
9 from faraday.server.utils.logger import get_logger
108 from faraday.server.utils.web import gzipped
119
1210 exploits_api = Blueprint('exploits_api', __name__)
6563 json_response["exploitdb"].append(obj_module)
6664
6765 except KeyError as ex:
68 abort(make_response(jsonify(message='Could not find {0}'.format(ex.message)), 400))
66 abort(make_response(jsonify(message='Could not find {0}'.format(str(ex))), 400))
6967
7068 return flask.jsonify(json_response)
69 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 from flask import jsonify, Blueprint
77
88 handlers_api = Blueprint('handlers_api', __name__)
1818
1919
2020 #.register(commandsrun_api)
21 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 from collections import defaultdict
3 from io import StringIO
44
55 import logging
66 import csv
1111 from flask_classful import route
1212 from marshmallow import fields, Schema
1313 from filteralchemy import Filter, FilterSet, operators
14 from sqlalchemy import or_, desc
14 from sqlalchemy import desc
1515 import wtforms
1616 from flask_wtf.csrf import validate_csrf
1717
3737 host_api = Blueprint('host_api', __name__)
3838
3939 logger = logging.getLogger(__name__)
40
4041
4142 class HostSchema(AutoSchema):
4243 _id = fields.Integer(dump_only=True, attribute='id')
157158 if 'file' not in flask.request.files:
158159 abort(400, "Missing File in request")
159160 hosts_file = flask.request.files['file']
161 stream = StringIO(hosts_file.stream.read().decode("utf-8"), newline=None)
160162 FILE_HEADERS = {'description', 'hostnames', 'ip', 'os'}
161163 try:
162 hosts_reader = csv.DictReader(hosts_file, skipinitialspace=True)
164 hosts_reader = csv.DictReader(stream)
163165 if set(hosts_reader.fieldnames) != FILE_HEADERS:
164166 logger.error("Missing Required headers in CSV (%s)", FILE_HEADERS)
165167 abort(400, "Missing Required headers in CSV (%s)" % FILE_HEADERS)
280282
281283
282284 HostsView.register(host_api)
285 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 import os
43
54 import flask
65 from flask import Blueprint
2625
2726 get_config.is_public = True
2827 show_info.is_public = True
28 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3
43 from flask import Blueprint
54 from marshmallow import fields
65
3736
3837
3938 LicenseView.register(license_api)
39 # I'm Py3
0 # Faraday Penetration Test IDE
1 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
2 # See the file 'doc/LICENSE' for the license information
3 from flask import Blueprint, g
4 from marshmallow import fields
5
6 from faraday.server.models import SearchFilter
7 from faraday.server.api.base import (
8 ReadWriteView,
9 AutoSchema,
10 )
11
12 searchfilter_api = Blueprint('searchfilter_api', __name__)
13
14
15 class SearchFilterSchema(AutoSchema):
16
17 id = fields.Integer(dump_only=True, attribute='id')
18
19 class Meta:
20 model = SearchFilter
21 fields = ('id', 'name',
22 'json_query', 'user_query')
23
24
25 class SearchFilterView(ReadWriteView):
26 route_base = 'searchfilter'
27 model_class = SearchFilter
28 schema_class = SearchFilterSchema
29
30 def _get_base_query(self):
31 query = super()._get_base_query()
32 return query.filter(SearchFilter.creator_id == g.user.id)
33
34
35 SearchFilterView.register(searchfilter_api)
36 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3
4 from flask import Blueprint
3 from flask import Blueprint, abort, make_response, jsonify
54 from filteralchemy import FilterSet, operators
65 from marshmallow import fields, post_load, ValidationError
76 from marshmallow.validate import OneOf, Range
5150 raise ValidationError('ports must be a list with exactly one'
5251 'element')
5352 port = value.pop()
54 if port < 0:
55 raise ValidationError('The value must be greater than or equal to 0')
53 if isinstance(port, str):
54 try:
55 port = int(port)
56 except ValueError:
57 raise ValidationError('The value must be a number')
58 if port > 65535 or port < 1:
59 raise ValidationError('The value must be in the range [1-65535]')
5660
5761 return str(port)
5862
120124 'services': services,
121125 }
122126
127 def _perform_create(self, data, **kwargs):
128 port_number = data.get("port", "1")
129 if not port_number.isdigit():
130 abort(make_response(jsonify(message="Invalid Port number"), 400))
131 return super(ServiceView, self)._perform_create(data, **kwargs)
123132
124133 ServiceView.register(services_api)
134 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 from flask import jsonify, session, Blueprint, current_app
77 from flask_wtf.csrf import generate_csrf
88
1414 data = user.get_security_payload()
1515 data['csrf_token'] = generate_csrf()
1616 return jsonify(data)
17 # I'm Py3
00 from itsdangerous import TimedJSONWebSignatureSerializer
11 from flask import Blueprint, g
22 from flask_security.utils import hash_data
3 from flask import current_app as app
4
35
46 from faraday.server.config import faraday_server
57 from faraday.server.api.base import GenericView
1113 route_base = 'token'
1214
1315 def get(self):
14 from faraday.server.web import app
1516 user_id = g.user.id
1617 serializer = TimedJSONWebSignatureSerializer(
1718 app.config['SECRET_KEY'],
1920 expires_in=faraday_server.api_token_expiration
2021 )
2122 hashed_data = hash_data(g.user.password) if g.user.password else None
22 return serializer.dumps({'user_id': user_id, "validation_check": hashed_data})
23 return serializer.dumps({'user_id': user_id, "validation_check": hashed_data}).decode('utf-8')
2324
2425
25 TokenAuthView.register(token_api)
26 TokenAuthView.register(token_api)
27
28 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3
4 from Queue import Empty, Queue
5 from multiprocessing import Queue as MultiProcessingQueue
6 from threading import Thread, Event
7
83 import os
9 import time
104 import string
115 import random
126 import logging
13 import faraday.server.config as FaradayServerConfig
14
7 from faraday.server.threads.reports_processor import REPORTS_QUEUE
158 from flask import (
169 request,
1710 abort,
2518 from wtforms import ValidationError
2619
2720 from faraday.server.utils.web import gzipped
28
29 from faraday.client.model.controller import ModelController
30
31 from faraday.client.plugins.controller import PluginController
32 from faraday.client.plugins.manager import PluginManager
33
34 from faraday.client.managers.mapper_manager import MapperManager
35 from faraday.client.managers.reports_managers import ReportProcessor
36
3721 from faraday.server.models import Workspace
38 from faraday.client.persistence.server import server
39
4022 from faraday.config.configuration import getInstanceConfiguration
4123
4224 CONF = getInstanceConfiguration()
43 logger = logging.getLogger(__name__)
44 UPLOAD_REPORTS_QUEUE = MultiProcessingQueue()
45 UPLOAD_REPORTS_CMD_QUEUE = MultiProcessingQueue()
4625 upload_api = Blueprint('upload_reports', __name__)
4726
4827 logger = logging.getLogger(__name__)
49
50 class RawReportProcessor(Thread):
51 def __init__(self):
52
53 super(RawReportProcessor, self).__init__()
54 from faraday.client.start_client import setupPlugins
55 setupPlugins()
56
57 self.pending_actions = Queue()
58
59 try:
60 plugin_manager = PluginManager(os.path.join(CONF.getConfigPath(), "plugins"))
61 except AttributeError:
62 logger.warning(
63 "Upload reports in WEB-UI not configurated, run Faraday client and try again...")
64 self._stop = True
65 return
66
67 mappers_manager = MapperManager()
68
69 self.model_controller = ModelController(mappers_manager, self.pending_actions)
70 self.model_controller.start()
71 self.end_event = Event()
72
73 plugin_controller = PluginController(
74 'PluginController',
75 plugin_manager,
76 mappers_manager,
77 self.pending_actions,
78 self.end_event)
79
80 self.processor = ReportProcessor(plugin_controller, None)
81 self._stop = False
82
83 def stop(self):
84 self.model_controller.stop()
85 self._stop = True
86
87 def run(self):
88 logger.info('Tool report processor started')
89 while not self._stop:
90 try:
91
92 workspace, file_path, cookie = UPLOAD_REPORTS_QUEUE.get(False, timeout=0.1)
93 logger.info('Processing raw report {0}'.format(file_path))
94
95 # Cookie of user, used to create objects in server with the right owner.
96 server.FARADAY_UPLOAD_REPORTS_WEB_COOKIE = cookie
97 server.FARADAY_UPLOAD_REPORTS_OVERWRITE_SERVER_URL = "http://{0}:{1}".format(
98 FaradayServerConfig.faraday_server.bind_address, FaradayServerConfig.faraday_server.port)
99
100 self.processor.ws_name = workspace
101
102 command_id = self.processor.processReport(file_path)
103 UPLOAD_REPORTS_CMD_QUEUE.put(command_id)
104 if not command_id:
105 continue
106
107 self.end_event.wait()
108 logger.info('Report processing of report {0} finished'.format(file_path))
109 self.end_event.clear()
110
111 except Empty:
112 time.sleep(0.1)
113
114 except KeyboardInterrupt as ex:
115 logger.info('Keyboard interrupt, stopping report processing thread')
116 self.stop()
117
118 except Exception as ex:
119 logger.exception(ex)
120 continue
121
12228
12329 @gzipped
12430 @upload_api.route('/v2/ws/<workspace>/upload_report', methods=['POST'])
13036
13137 # Authorization code copy-pasted from server/api/base.py
13238 ws = Workspace.query.filter_by(name=workspace).first()
133 if not ws or not (ws.active):
39 if not ws or not ws.active:
13440 # Don't raise a 403 to prevent workspace name enumeration
13541 abort(404, "Workspace disabled: %s" % workspace)
13642
14854
14955 chars = string.ascii_uppercase + string.digits
15056 random_prefix = ''.join(random.choice(chars) for x in range(12))
151 raw_report_filename = '{0}{1}'.format(random_prefix, secure_filename(report_file.filename))
57 raw_report_filename = '{0}_{1}'.format(random_prefix, secure_filename(report_file.filename))
15258
15359 try:
154 file_path = os.path.join(
155 CONF.getConfigPath(),
156 'uploaded_reports/{0}'.format(raw_report_filename))
60 file_path = os.path.join(CONF.getConfigPath(), 'uploaded_reports', raw_report_filename)
61 with open(file_path, 'wb') as output:
62 output.write(report_file.read())
15763 except AttributeError:
15864 logger.warning(
15965 "Upload reports in WEB-UI not configurated, run Faraday client and try again...")
16066 abort(make_response(jsonify(message="Upload reports not configurated: Run faraday client and start Faraday server again"), 500))
161
162 with open(file_path, 'w') as output:
163 output.write(report_file.read())
164
165 UPLOAD_REPORTS_QUEUE.put((workspace, file_path, request.cookies))
166 return make_response(jsonify(message="ok"), 200)
67 else:
68 REPORTS_QUEUE.put((workspace, file_path))
69 return make_response(jsonify(message="ok"), 200)
70 else:
71 abort(make_response(jsonify(message="Missing report file"), 400))
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 import time
3 from builtins import str, bytes
4 from io import TextIOWrapper
5
46 import threading
57 import logging
68 import csv
3436
3537 from faraday.server.models import (
3638 db,
37 CustomFieldsSchema,
3839 Vulnerability,
3940 VulnerabilityTemplate,
4041 )
8889 return ', '.join(map(lambda ref_tmpl: ref_tmpl.name, obj.reference_template_instances))
8990
9091 def load_references(self, value):
92 if isinstance(value, bytes):
93 value = value.decode('utf-8')
9194 if isinstance(value, list):
9295 references = value
93 elif isinstance(value, (unicode, str)):
96 elif isinstance(value, str):
9497 if len(value) == 0:
9598 # Required because "".split(",") == [""]
9699 return []
167170 vulns_file = request.files['file']
168171 FILE_HEADERS = {'cwe', 'name', 'description', 'resolution', 'exploitation', 'references'}
169172 try:
170 vulns_reader = csv.DictReader(vulns_file, skipinitialspace=True)
173 io_wrapper = TextIOWrapper(vulns_file, encoding=request.content_encoding or "utf8")
174
175 vulns_reader = csv.DictReader(io_wrapper, skipinitialspace=True)
171176 if set(vulns_reader.fieldnames) != FILE_HEADERS:
172177 logger.error("Missing Required headers in CSV (%s)", FILE_HEADERS)
173178 abort(400, "Missing Required headers in CSV (%s)" % FILE_HEADERS)
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 from builtins import str
4
35 import os
4 import re
56 import io
6 import csv
77 import json
88 import logging
9 import cStringIO
109 from base64 import b64encode, b64decode
11
1210
1311 import flask
1412 import wtforms
1614 from flask import request, send_file
1715 from flask import Blueprint
1816 from flask_classful import route
19 from flask_restless.search import search, Filter as RestlessFilter
17 from flask_restless.search import search
2018 from flask_wtf.csrf import validate_csrf
2119 from marshmallow import Schema, fields, post_load, ValidationError
2220 from marshmallow.validate import OneOf
3735 db,
3836 File,
3937 Host,
40 Comment,
4138 Service,
4239 Hostname,
4340 Workspace,
820817 as_attachment=True,
821818 cache_timeout=-1)
822819
820 @route('bulk_delete/', methods=['DELETE'])
821 def bulk_delete(self, workspace_name):
822 workspace = self._get_workspace(workspace_name)
823 json_quest = request.get_json()
824 vulnerability_ids = json_quest.get('vulnerability_ids', [])
825 vulnerability_severities = json_quest.get('severities', [])
826 deleted_vulns = 0
827 vulns = []
828 if vulnerability_ids:
829 logger.info("Delete Vuln IDs: %s", vulnerability_ids)
830 vulns = VulnerabilityGeneric.query.filter(VulnerabilityGeneric.id.in_(vulnerability_ids),
831 VulnerabilityGeneric.workspace_id == workspace.id)
832 elif vulnerability_severities:
833 logger.info("Delete Vuln Severities: %s", vulnerability_severities)
834 vulns = VulnerabilityGeneric.query.filter(VulnerabilityGeneric.severity.in_(vulnerability_severities),
835 VulnerabilityGeneric.workspace_id == workspace.id)
836 else:
837 flask.abort(400, "Invalid Request")
838 for vuln in vulns:
839 db.session.delete(vuln)
840 deleted_vulns += 1
841 db.session.commit()
842 response = {'deleted_vulns': deleted_vulns}
843 return flask.jsonify(response)
844
823845 VulnerabilityView.register(vulns_api)
846
847 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3
4
53 import logging
64 import flask
75 from flask import Blueprint
2321 def post(self, workspace_name):
2422 workspace = self._get_workspace(workspace_name)
2523 signer = TimestampSigner(app.config['SECRET_KEY'], salt="websocket")
26 token = signer.sign(str(workspace.id))
24 token = signer.sign(str(workspace.id)).decode('utf-8')
2725 return {"token": token}
2826
2927
5048 signer = TimestampSigner(app.config['SECRET_KEY'],
5149 salt="websocket_agent")
5250 try:
53 agent_id = signer.unsign(token, max_age=60)
51 agent_id = signer.unsign(token, max_age=60).decode('utf-8')
5452 except BadData as e:
5553 raise ValueError("Invalid Token")
5654 agent = Agent.query.get(agent_id)
7977 except NoResultFound:
8078 flask.abort(403)
8179 return agent
80
81 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 from builtins import str
4
35 import os
46 import json
7 import logging
58
69 import flask
7 from flask import Blueprint
10 from flask import Blueprint, abort, make_response, jsonify
811 from flask_classful import route
912 from marshmallow import Schema, fields, post_load, validate, ValidationError
1013 from sqlalchemy.orm import (
2326 from faraday.server.api.base import ReadWriteView, AutoSchema
2427 from faraday.config.configuration import getInstanceConfiguration
2528
29 logger = logging.getLogger(__name__)
30
2631 workspace_api = Blueprint('workspace_api', __name__)
32
2733
2834
2935 class WorkspaceSummarySchema(Schema):
8793 class WorkspaceView(ReadWriteView):
8894 route_base = 'ws'
8995 lookup_field = 'name'
90 lookup_field_type = unicode
96 lookup_field_type = str
9197 model_class = Workspace
9298 schema_class = WorkspaceSchema
9399 order_field = Workspace.name.asc()
96102 query = self._get_base_query()
97103 objects = []
98104 for workspace_stat in query:
99 workspace_stat = dict(workspace_stat)
100 for key, value in workspace_stat.items():
105 workspace_stat_dict = dict(workspace_stat)
106 for key, value in list(workspace_stat_dict.items()):
101107 if key.startswith('workspace_'):
102108 new_key = key.replace('workspace_', '')
103 workspace_stat[new_key] = workspace_stat[key]
104 workspace_stat['scope'] = []
105 if workspace_stat['scope_raw']:
106 workspace_stat['scope_raw'] = workspace_stat['scope_raw'].split(',')
107 for scope in workspace_stat['scope_raw']:
108 workspace_stat['scope'].append({'name': scope})
109 objects.append(workspace_stat)
109 workspace_stat_dict[new_key] = workspace_stat_dict[key]
110 workspace_stat_dict['scope'] = []
111 if workspace_stat_dict['scope_raw']:
112 workspace_stat_dict['scope_raw'] = workspace_stat_dict['scope_raw'].split(',')
113 for scope in workspace_stat_dict['scope_raw']:
114 workspace_stat_dict['scope'].append({'name': scope})
115 objects.append(workspace_stat_dict)
110116 return self._envelope_list(self._dump(objects, kwargs, many=True))
111117
112118 def _get_querystring_boolean_field(self, field_name, default=None):
169175 try:
170176 obj = query.one()
171177 except NoResultFound:
172 flask.abort(404, 'Object with id "%s" not found' % object_id)
178 flask.abort(404, 'Object with name "%s" not found' % object_id)
173179 return obj
174180
175181 def _perform_create(self, data, **kwargs):
182 start_date = data.get("start_date", None)
183 end_date = data.get("end_date", None)
184 if start_date and end_date:
185 if start_date > end_date:
186 abort(make_response(jsonify(message="Workspace start date can't be greater than the end date"), 400))
176187
177188 scope = data.pop('scope', [])
178189 workspace = super(WorkspaceView, self)._perform_create(data, **kwargs)
231242
232243
233244 WorkspaceView.register(workspace_api)
245 # I'm Py3
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
33 import logging
4
54 import os
65 import string
76 import datetime
8 from future.builtins import range # __future__
97 from itsdangerous import TimedJSONWebSignatureSerializer, SignatureExpired, BadSignature
10 from os.path import join, expanduser
8 from os.path import join
119 from random import SystemRandom
1210
1311 from faraday.server.config import LOCAL_CONFIG_FILE, copy_default_config_to_local
1412 from faraday.server.models import User
15
16 try:
17 # py2.7
18 from faraday.client.configparser import ConfigParser, NoSectionError, NoOptionError, DuplicateSectionError
19 except ImportError:
20 # py3
21 from ConfigParser import ConfigParser, NoSectionError, NoOptionError, DuplicateSectionError
13 from configparser import ConfigParser, NoSectionError, NoOptionError, DuplicateSectionError
2214
2315 import flask
2416 from flask import Flask, session, g
4537 # Load SQLAlchemy Events
4638 import faraday.server.events
4739 from faraday.server.utils.logger import LOGGING_HANDLERS
40 from faraday.server.utils.invalid_chars import remove_null_caracters
4841 from faraday.config.constant import CONST_FARADAY_HOME_PATH
4942
5043
7063
7164
7265 def register_blueprints(app):
73 from faraday.server.api.modules.info import info_api
74 from faraday.server.api.modules.commandsrun import commandsrun_api
75 from faraday.server.api.modules.activity_feed import activityfeed_api
76 from faraday.server.api.modules.credentials import credentials_api
77 from faraday.server.api.modules.hosts import host_api
78 from faraday.server.api.modules.licenses import license_api
79 from faraday.server.api.modules.services import services_api
80 from faraday.server.api.modules.session import session_api
81 from faraday.server.api.modules.vulns import vulns_api
82 from faraday.server.api.modules.vulnerability_template import vulnerability_template_api
83 from faraday.server.api.modules.workspaces import workspace_api
84 from faraday.server.api.modules.handlers import handlers_api
85 from faraday.server.api.modules.comments import comment_api
86 from faraday.server.api.modules.upload_reports import upload_api
87 from faraday.server.api.modules.websocket_auth import websocket_auth_api
88 from faraday.server.api.modules.get_exploits import exploits_api
89 from faraday.server.api.modules.custom_fields import custom_fields_schema_api
90 from faraday.server.api.modules.agent_auth_token import agent_auth_token_api
91 from faraday.server.api.modules.agent import agent_api
92 from faraday.server.api.modules.bulk_create import bulk_create_api
93 from faraday.server.api.modules.token import token_api
66
67 from faraday.server.api.modules.info import info_api # pylint:disable=import-outside-toplevel
68 from faraday.server.api.modules.commandsrun import commandsrun_api # pylint:disable=import-outside-toplevel
69 from faraday.server.api.modules.activity_feed import activityfeed_api # pylint:disable=import-outside-toplevel
70 from faraday.server.api.modules.credentials import credentials_api # pylint:disable=import-outside-toplevel
71 from faraday.server.api.modules.hosts import host_api # pylint:disable=import-outside-toplevel
72 from faraday.server.api.modules.licenses import license_api # pylint:disable=import-outside-toplevel
73 from faraday.server.api.modules.services import services_api # pylint:disable=import-outside-toplevel
74 from faraday.server.api.modules.session import session_api # pylint:disable=import-outside-toplevel
75 from faraday.server.api.modules.vulns import vulns_api # pylint:disable=import-outside-toplevel
76 from faraday.server.api.modules.vulnerability_template import vulnerability_template_api # pylint:disable=import-outside-toplevel
77 from faraday.server.api.modules.workspaces import workspace_api # pylint:disable=import-outside-toplevel
78 from faraday.server.api.modules.handlers import handlers_api # pylint:disable=import-outside-toplevel
79 from faraday.server.api.modules.comments import comment_api # pylint:disable=import-outside-toplevel
80 from faraday.server.api.modules.upload_reports import upload_api # pylint:disable=import-outside-toplevel
81 from faraday.server.api.modules.websocket_auth import websocket_auth_api # pylint:disable=import-outside-toplevel
82 from faraday.server.api.modules.get_exploits import exploits_api # pylint:disable=import-outside-toplevel
83 from faraday.server.api.modules.custom_fields import custom_fields_schema_api # pylint:disable=import-outside-toplevel
84 from faraday.server.api.modules.agent_auth_token import agent_auth_token_api # pylint:disable=import-outside-toplevel
85 from faraday.server.api.modules.agent import agent_api # pylint:disable=import-outside-toplevel
86 from faraday.server.api.modules.bulk_create import bulk_create_api # pylint:disable=import-outside-toplevel
87 from faraday.server.api.modules.token import token_api # pylint:disable=import-outside-toplevel
88 from faraday.server.api.modules.search_filter import searchfilter_api # pylint:disable=import-outside-toplevel
89
9490 app.register_blueprint(commandsrun_api)
9591 app.register_blueprint(activityfeed_api)
9692 app.register_blueprint(credentials_api)
112108 app.register_blueprint(agent_auth_token_api)
113109 app.register_blueprint(bulk_create_api)
114110 app.register_blueprint(token_api)
111 app.register_blueprint(searchfilter_api)
115112
116113
117114 def check_testing_configuration(testing, app):
306303 'plaintext', # TODO: remove it
307304 ],
308305 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(hours=12),
306 'SESSION_COOKIE_NAME': 'faraday_session',
309307 })
310308
311309 store = FilesystemStore(app.config['SESSION_FILE_DIR'])
337335 except NoOptionError:
338336 logger.info('Missing connection_string on [database] section on server.ini. Please configure the database before running the server.')
339337
340 from faraday.server.models import db
338 from faraday.server.models import db # pylint:disable=import-outside-toplevel
341339 db.init_app(app)
342340 #Session(app)
343341
390388 # want to skip the LoginForm validate logic
391389 if not super(LoginForm, self).validate():
392390 return False
391 self.email.data = remove_null_caracters(self.email.data)
392
393393 self.user = _datastore.get_user(self.email.data)
394394
395395 if self.user is None:
396396 self.email.errors.append(get_message('USER_DOES_NOT_EXIST')[0])
397397 return False
398
399 self.user.password = remove_null_caracters(self.user.password)
398400 if not self.user.password:
399401 self.email.errors.append(get_message('USER_DOES_NOT_EXIST')[0])
400402 return False
403 self.password.data = remove_null_caracters(self.password.data)
401404 if not verify_and_update_password(self.password.data, self.user):
402405 self.email.errors.append(get_message('USER_DOES_NOT_EXIST')[0])
403406 return False
408411 self.email.errors.append(get_message('DISABLED_ACCOUNT')[0])
409412 return False
410413 return True
414
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66
7 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 from faraday.server.web import app
77
88
99 def show_all_urls():
1010 print(app.url_map)
11 # I'm Py3
00 from faraday.server.web import app
11 from faraday.server.models import User, db
2
23
34 def changes_password(username, password):
45 with app.app_context():
78 user.password = password
89 db.session.add(user)
910 db.session.commit()
10 print "Password changed succesfully"
11 print("Password changed succesfully")
1112 else:
12 print "User not found in Faraday's Database"
13
14
13 print("User not found in Faraday's Database")
14 # I'm Py3
33 from faraday.server.web import app
44 from faraday.server.models import User, db
55
6
67 def change_username(current_username, new_username):
78 with app.app_context():
89 user = User.query.filter_by(username=current_username).first()
910 if not user:
10 print("\nERROR: User '{username}' was not found in Faraday's Database.".format(username=current_username))
11 print(f"\nERROR: User {current_username} was not found in Faraday's Database.")
1112 sys.exit(1)
1213 else:
13 print("\nThe user named '{old}' will be changed to '{new}'.".\
14 format(old=current_username, new=new_username))
14 print(f"\nThe user named {current_username} will be changed to {new_username}.")
1515 confirm = click.prompt("Do you want to continue? (y/n)")
1616 print("")
1717
1919 user.username = new_username
2020 db.session.add(user)
2121 db.session.commit()
22 print("Username '{old}' changed to '{new}'" \
23 .format(old=current_username, new=new_username))
22 print(f"Username {current_username} changed to {new_username}")
2423 else:
2524 print("Username not changed.")
25
26
27 # I'm Py3
1111 def add_custom_field_main():
1212 with app.app_context():
1313 add_custom_field_wizard()
14
1415
1516 def delete_custom_field_main():
1617 with app.app_context():
3031 db.session.commit()
3132 else:
3233 print('Custom field not found')
34
3335
3436 def add_custom_field_wizard():
3537 print('This wizard will guide you to ADD custom field to the vulneraiblity model.')
8385 if not created:
8486 print('Custom field already exists, skipping')
8587 sys.exit(1)
86 custom_field_data.field_display_name = field_display_name,
88 custom_field_data.field_display_name = field_display_name
8789 custom_field_data.field_type = field_type
8890 db.session.commit()
91 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
6 from __future__ import print_function
5 """
76 import sys
87 from sqlalchemy import MetaData
98 try:
6766 )
6867 graph.write_png('uml_schema.png') # write out the file
6968 print("Graph written to fle uml_schema.png")
69 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 from builtins import input
7
68 import getpass
79 import shutil
810 import string
1113 import sys
1214 import click
1315 import psycopg2
14 from future.builtins import range # __future__
16 from alembic.config import Config
17 from alembic import command
1518 from random import SystemRandom
1619 from tempfile import TemporaryFile
1720 from subprocess import Popen
2629 )
2730 from faraday.server.utils.database import is_unique_constraint_violation
2831
29 try:
30 # py2.7
31 from faraday.client.configparser import ConfigParser, NoSectionError, NoOptionError
32 except ImportError:
33 # py3
34 from ConfigParser import ConfigParser, NoSectionError, NoOptionError
32 from configparser import ConfigParser, NoSectionError
3533
3634 from flask import current_app
3735 from colorama import init
5149 config.get('database', 'connection_string')
5250 reconfigure = None
5351 while not reconfigure:
54 reconfigure = raw_input('Database section {yellow} already found{white}. Do you want to reconfigure database? (yes/no) '.format(yellow=Fore.YELLOW, white=Fore.WHITE))
52 reconfigure = input('Database section {yellow} already found{white}. Do you want to reconfigure database? (yes/no) '.format(yellow=Fore.YELLOW, white=Fore.WHITE))
5553 if reconfigure.lower() == 'no':
5654 return False
5755 elif reconfigure.lower() == 'yes':
8179 # current_psql_output is for checking psql command already known errors for each execution.
8280 psql_log_filename = os.path.join(faraday_path_conf, 'logs', 'psql_log.log')
8381 current_psql_output = TemporaryFile()
84 with open(psql_log_filename, 'a+') as psql_log_file:
82 with open(psql_log_filename, 'ab+') as psql_log_file:
8583 hostname = 'localhost'
8684 username, password, process_status = self._configure_new_postgres_user(current_psql_output)
8785 current_psql_output.seek(0)
159157 conf.saveConfig(user_xml)
160158
161159 def _configure_existing_postgres_user(self):
162 username = raw_input('Please enter the postgresql username: ')
160 username = input('Please enter the postgresql username: ')
163161 password = getpass.getpass('Please enter the postgresql password: ')
164162
165163 return username, password
166164
167165 def _check_psql_output(self, current_psql_output_file, process_status):
168166 current_psql_output_file.seek(0)
169 psql_output = current_psql_output_file.read()
167 psql_output = current_psql_output_file.read().decode('utf-8')
170168 if 'unknown user: postgres' in psql_output:
171169 print('ERROR: Postgres user not found. Did you install package {blue}postgresql{white}?'.format(blue=Fore.BLUE, white=Fore.WHITE))
172170 elif 'could not connect to server' in psql_output:
200198 p.wait()
201199 psql_log_file.seek(0)
202200 output = psql_log_file.read()
201 if isinstance(output, bytes):
202 output = output.decode('utf-8')
203203 already_exists_error = 'role "{0}" already exists'.format(username)
204204 return_code = p.returncode
205205 if already_exists_error in output:
250250 p.wait()
251251 return_code = p.returncode
252252 psql_log_file.seek(0)
253 output = psql_log_file.read()
253 output = psql_log_file.read().decode('utf-8')
254254 already_exists_error = 'database creation failed: ERROR: database "{0}" already exists'.format(database_name)
255255 if already_exists_error in output:
256256 print('{yellow}WARNING{white}: Database already exists.'.format(yellow=Fore.YELLOW, white=Fore.WHITE))
276276
277277 def _create_tables(self, conn_string):
278278 print('Creating tables')
279 from faraday.server.models import db
279 from faraday.server.models import db # pylint:disable=import-outside-toplevel
280280 current_app.config['SQLALCHEMY_DATABASE_URI'] = conn_string
281281
282282 # Check if the alembic_version exists
293293 try:
294294 db.create_all()
295295 except OperationalError as ex:
296 if 'could not connect to server' in ex.message:
296 if 'could not connect to server' in str(ex):
297297 print('ERROR: {red}PostgreSQL service{white} is not running. Please verify that it is running in port 5432 before executing setup script.'.format(red=Fore.RED, white=Fore.WHITE))
298298 sys.exit(1)
299 elif 'password authentication failed' in ex.message:
299 elif 'password authentication failed' in str(ex):
300300 print('ERROR: ')
301301 sys.exit(1)
302302 else:
306306 print('Please check postgres user permissions.')
307307 sys.exit(1)
308308 except ImportError as ex:
309 if 'psycopg2' in ex:
309 if 'psycopg2' in str(ex):
310310 print(
311311 'ERROR: Missing python depency {red}psycopg2{white}. Please install it with {blue}pip install psycopg2'.format(red=Fore.RED, white=Fore.WHITE, blue=Fore.BLUE))
312312 sys.exit(1)
313313 else:
314314 raise
315315 else:
316 from alembic.config import Config
317 from alembic import command
318316 alembic_cfg = Config(os.path.join(FARADAY_BASE, 'alembic.ini'))
319317 os.chdir(FARADAY_BASE)
320318 command.stamp(alembic_cfg, "head")
319 # I'm Py3
+0
-69
faraday/server/commands/reports.py less more
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 '''
6 import os
7 import logging
8 from Queue import Queue
9
10 from faraday.client.managers.mapper_manager import MapperManager
11 from faraday.client.managers.reports_managers import ReportManager, CONF
12 from faraday.client.managers.workspace_manager import WorkspaceManager
13 from faraday.client.model.api import setUpAPIs
14 from faraday.client.model.controller import ModelController
15
16 from faraday.client.plugins.controller import PluginController
17 from faraday.client.plugins.manager import PluginManager
18 from faraday.server.models import Workspace
19
20 logger = logging.getLogger(__name__)
21
22
23 def import_external_reports(workspace_name=None, disable_polling=False):
24 plugins_path = os.path.join(CONF.getConfigPath(), "plugins")
25 plugin_manager = PluginManager(plugins_path)
26 mappers_manager = MapperManager()
27
28 if workspace_name:
29 query = Workspace.query.filter_by(name=workspace_name)
30 else:
31 query = Workspace.query
32
33 process_workspaces(mappers_manager, plugin_manager, query, disable_polling)
34 #controller._pending_actions.join()
35
36
37 def process_workspaces(mappers_manager, plugin_manager, query, disable_polling):
38 report_managers = []
39 controllers = []
40 for workspace in query.all():
41 logger.debug('Processing workspace {0}'.format(workspace.name))
42 pending_actions = Queue()
43 plugin_controller = PluginController(
44 'PluginController',
45 plugin_manager,
46 mappers_manager,
47 pending_actions
48 )
49 mappers_manager.createMappers(workspace.name)
50 controller = ModelController(mappers_manager, pending_actions)
51 workspace_manager = WorkspaceManager(mappers_manager)
52 setUpAPIs(controller, workspace_manager, hostname=None, port=None)
53 controller.start()
54 controllers.append(controller)
55 report_manager = ReportManager(
56 0.1,
57 workspace.name,
58 plugin_controller,
59 polling=not disable_polling
60 )
61 report_managers.append(report_manager)
62 report_manager.start()
63
64 for report_manager in report_managers:
65 report_manager.join()
66
67 for controller in controllers:
68 controller.stop()
0 #!/usr/bin/env python2.7
1 '''
0 #!/usr/bin/env python3
1 """
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
44 See the file 'doc/LICENSE' for the license information
55
6 '''
7
6 """
87 import sys
98 import os
109 sys.path.append(os.getcwd())
1110
1211 import click
12
1313 from faraday.server.models import db
1414 from faraday.server.web import app
15 from faraday.server.commands.initdb import InitDB
16 import faraday.server.config
1517
1618
1719 def reset_db_all():
2729
2830 # db.create_all()
2931 # Ugly hack to create tables and also setting alembic revision
30 import faraday.server.config
3132 conn_string = faraday.server.config.database.connection_string
32 from faraday.server.commands.initdb import InitDB
3333 InitDB()._create_tables(conn_string)
3434
3535
4747
4848 if __name__ == '__main__':
4949 main()
50 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 import os
77 import socket
88
192192 exit_code = 0
193193 result = check_postgres()
194194
195
195196 if result == False:
196197 print('[{red}-{white}] Could not connect to PostgreSQL, please check if database is running'\
197198 .format(red=Fore.RED, white=Fore.WHITE))
202203 .format(red=Fore.RED, white=Fore.WHITE))
203204 exit_code = 1
204205 return exit_code
205 elif result[1]<90400:
206 elif int(result[1][0])<90400:
206207 print('[{red}-{white}] PostgreSQL is running, but needs to be 9.4 or newer, please update PostgreSQL'.\
207208 format(red=Fore.RED, white=Fore.WHITE))
208209 elif result:
258259 """Prints Status of the dependencies using check_server_dependencies() and check_client_dependencies()"""
259260
260261 status, server_dep = check_server_dependencies()
262 red = Fore.RED
263 white = Fore.WHITE
264 green = Fore.GREEN
261265 if status == True:
262 print('[{red}-{white}] Some server dependencies are old: [' + ', '.join(server_dep) + ']. Update them with \"pip install -r requirements_server.txt -U\"') \
263 .format(red=Fore.RED, white=Fore.WHITE)
266 print(f"[{red}-{white}] Some server dependencies are old: [{', '.join(server_dep)}']. Update them with \"pip install -r requirements_server.txt -U\"")
264267
265268 elif status == 0:
266 print('[{red}-{white}] Client dependencies not met: [' + ', '.join(server_dep) + '] Install them with \"pip install -r requirements_server.txt -U\"')\
267 .format(red=Fore.RED, white=Fore.WHITE)
268
269 else:
270 print('[{green}+{white}] Server dependencies met' \
271 .format(green=Fore.GREEN, white=Fore.WHITE))
269 print(f"[{red}-{white}] Client dependencies not met: [{', '.join(server_dep)}'] Install them with \"pip install -r requirements_server.txt -U\"")
270
271 else:
272 print(f'[{green}+{white}] Server dependencies met')
272273
273274 status, client_dep = check_client_dependencies()
274275 if status == True:
275 print('[{red}-{white}] Some client dependencies are old: [' + ', '.join(client_dep) + ']. Update them with \"pip install -r requirements.txt -U\"') \
276 .format(red=Fore.RED, white=Fore.WHITE)
276 print(f"[{red}-{white}] Some client dependencies are old: [{', '.join(client_dep)}]. Update them with \"pip install -r requirements.txt -U\"")
277277
278278 elif status == 0:
279 print('[{red}-{white}] Client dependencies not met: [' + ', '.join(client_dep) + ']. Install them with \"pip install -r requirements.txt -U\"')\
280 .format(red=Fore.RED, white=Fore.WHITE)
281
282 else:
283 print('[{green}+{white}] Client dependencies met'\
284 .format(green=Fore.GREEN, white=Fore.WHITE))
279 print(f"[{red}-{white}] Client dependencies not met: [{', '.join(client_dep)}]. Install them with \"pip install -r requirements.txt -U\"")
280
281 else:
282 print(f'[{green}+{white}] Client dependencies met')
285283
286284
287285 def print_config_status():
307305 .format(red=Fore.RED, white=Fore.WHITE))
308306
309307 if check_open_ports():
310 print "[{green}+{white}] Port {PORT} in {ad} is open"\
311 .format(PORT=faraday.server.config.faraday_server.port, green=Fore.GREEN,white=Fore.WHITE,ad=faraday.server.config.faraday_server.bind_address)
312 else:
313 print "[{red}-{white}] Port {PORT} in {ad} is not open"\
314 .format(PORT=faraday.server.config.faraday_server.port,red=Fore.RED,white=Fore.WHITE,ad =faraday.server.config.faraday_server.bind_address)
308 print("[{green}+{white}] Port {PORT} in {ad} is open"\
309 .format(PORT=faraday.server.config.faraday_server.port, green=Fore.GREEN,white=Fore.WHITE,ad=faraday.server.config.faraday_server.bind_address))
310 else:
311 print("[{red}-{white}] Port {PORT} in {ad} is not open"\
312 .format(PORT=faraday.server.config.faraday_server.port,red=Fore.RED,white=Fore.WHITE,ad =faraday.server.config.faraday_server.bind_address))
315313
316314
317315 def full_status_check():
327325
328326 print('\n{white}Checking Faraday config...{white}'.format(white=Fore.WHITE))
329327 print_config_status()
328 # I'm Py3
0 import os
01 import sys
12 import shutil
23 import tempfile
34 from tqdm import tqdm
45 from colorama import init
56 from colorama import Fore, Style
7
8 import distro
69
710 try:
811 from pip._internal.operations import freeze
4043 pip_file.write('\n')
4144 pip_file.close()
4245
46
4347 def get_logs(path):
44 #Copies the logs using the logs path saved on constants
45 shutil.copytree(constants.CONST_FARADAY_HOME_PATH +'/logs', path + '/logs')
48 #Copies the logs using the logs path saved on constants
49 orig_path = os.path.join(constants.CONST_FARADAY_HOME_PATH, 'logs')
50 dst_path = os.path.join(path, 'logs')
51 shutil.copytree(orig_path, dst_path, ignore=shutil.ignore_patterns('access*.*'))
52
4653
4754 def make_zip(path):
4855 #Makes a zip file of the new folder with all the information obtained inside
5259 #Deletes recursively the directory created on the init_config
5360 shutil.rmtree(path)
5461
62 def revise_os(path):
63 with open(path + '/os_distro.txt','wt') as os_file:
64 os_file.write("{}".format(distro.linux_distribution()))
65
5566 def all_for_support():
56 with tqdm(total=5) as pbar:
67 with tqdm(total=6) as pbar:
5768 path = init_config()
5869 get_status_check(path)
5970 pbar.update(1)
6172 pbar.update(1)
6273 get_pip_freeze(path)
6374 pbar.update(1)
75 revise_os(path)
76 pbar.update(1)
6477 make_zip(path)
6578 pbar.update(1)
6679 end_config(path)
6780 pbar.update(1)
6881
6982 print('[{green}+{white}] Process Completed. A {bright}faraday_support.zip{normal} was generated'
70 .format(green=Fore.GREEN, white=Fore.WHITE, bright=Style.BRIGHT, normal=Style.NORMAL))
83 .format(green=Fore.GREEN, white=Fore.WHITE, bright=Style.BRIGHT, normal=Style.NORMAL))# I'm Py3
0 from __future__ import absolute_import
10 # Faraday Penetration Test IDE
21 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
32 # See the file 'doc/LICENSE' for the license information
54 import os
65 import shutil
76 import errno
8 try:
9 import ConfigParser
10 except ImportError:
11 import faraday.client.configparser as ConfigParser
7 from configparser import ConfigParser
128
139 from logging import (
1410 DEBUG,
6258 # Copy default config file into faraday local config
6359 shutil.copyfile(DEFAULT_CONFIG_FILE, LOCAL_CONFIG_FILE)
6460
65 from faraday.server.utils.logger import get_logger
61 from faraday.server.utils.logger import get_logger # pylint:disable=import-outside-toplevel
6662 get_logger(__name__).info(u"Local faraday-server configuration created at {}".format(LOCAL_CONFIG_FILE))
6763
6864
7066 """Load configuration from files declared in this module and put them
7167 on this module's namespace for convenient access"""
7268
73 __parser = ConfigParser.SafeConfigParser()
69 __parser = ConfigParser()
7470 __parser.read(CONFIG_FILES)
7571
7672 for section_name in __parser.sections():
8884 return LOGGING_LEVEL is DEBUG
8985
9086
91 class ConfigSection(object):
87 class ConfigSection:
9288 def parse(self, __parser):
9389 for att in self.__dict__:
9490 if isinstance(self.__dict__[att], bool):
156152 self.api_token_expiration = 2592000
157153 self.agent_token = None
158154 self.debug = False
155 self.custom_plugins_folder = None
159156
160157 class LDAPConfigObject(ConfigSection):
161158 def __init__(self):
223220 return doc
224221
225222
223 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 import sys
77 import logging
88 import inspect
9 from Queue import Queue
9 from queue import Queue
1010
1111 from sqlalchemy import event
1212
9393 "This should never happen!!!"
9494
9595
96
97 assert (inserted_instance.workspace_id ==
98 inserted_instance.parent.workspace_id), \
99 "Conflicting workspace_id assignation for objects. " \
100 "This should never happen!!!"
101
102
96103 # register the workspace verification for all objs that has workspace_id
97104 for name, obj in inspect.getmembers(sys.modules['faraday.server.models']):
98105 if inspect.isclass(obj) and getattr(obj, 'workspace_id', None):
113120 # Update object bindings
114121 event.listen(Host, 'after_update', update_object_event)
115122 event.listen(Service, 'after_update', update_object_event)
123 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 from builtins import str
7
68 import json
79 import imghdr
810 from tempfile import SpooledTemporaryFile
4345 thumbnail_size = (128, 128)
4446
4547 def process_content(self, content, filename=None, content_type=None):
48 if isinstance(content, str):
49 content = content.encode('utf-8')
4650 image_format = imghdr.what(None, h=content[:32])
4751 if image_format:
4852 content_type = 'image/{0}'.format(image_format)
120124 if value is not None:
121125 value = json.loads(value)
122126 return value
127 # I'm Py3
+0
-1505
faraday/server/importer.py less more
0 # -*- coding: utf8 -*-
1 # Faraday Penetration Test IDE
2 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
3 # See the file 'doc/LICENSE' for the license information
4 import string
5 from random import SystemRandom
6
7 import os
8 import re
9 import sys
10 import json
11 import logging
12 import datetime
13 import multiprocessing
14 from future.builtins import range # __future__
15
16 import requests
17 from requests.exceptions import HTTPError, RequestException
18 from tempfile import NamedTemporaryFile
19
20 from collections import (
21 Counter,
22 defaultdict,
23 OrderedDict
24 )
25 from sqlalchemy import Text, String
26 from binascii import unhexlify
27 try:
28 from urllib import quote
29 except ImportError:
30 from urllib.parse import quote
31
32 from IPy import IP
33 from passlib.utils.binary import ab64_encode
34 from tqdm import tqdm
35 import faraday.server.config
36
37 import faraday.server.models
38 import faraday.server.utils.logger
39 from faraday.server.models import (
40 db,
41 Command,
42 CommandObject,
43 Comment,
44 Credential,
45 ExecutiveReport,
46 Host,
47 Hostname,
48 License,
49 Methodology,
50 MethodologyTemplate,
51 PolicyViolation,
52 Reference,
53 Service,
54 Scope,
55 Tag,
56 TagObject,
57 Task,
58 TaskTemplate,
59 User,
60 Vulnerability,
61 VulnerabilityTemplate,
62 VulnerabilityWeb,
63 Workspace,
64 WorkspacePermission,
65 File,
66 )
67 from faraday.server.utils import invalid_chars
68 from faraday.server.utils.database import get_or_create
69 from faraday.server.web import app
70
71 COUCHDB_USER_PREFIX = 'org.couchdb.user:'
72 COUCHDB_PASSWORD_PREFIX = '-pbkdf2-'
73
74 logger = faraday.server.utils.logger.get_logger(__name__)
75
76 importer_logfile = os.path.expanduser(os.path.join(
77 faraday.server.config.CONSTANTS.CONST_FARADAY_HOME_PATH,
78 faraday.server.config.CONSTANTS.CONST_FARADAY_LOGS_PATH, 'couchdb-importer.log'))
79 importer_file_handler = logging.FileHandler(importer_logfile)
80 formatter = logging.Formatter(
81 '%(asctime)s - %(name)s:%(lineno)d - %(levelname)s - %(message)s')
82 importer_file_handler.setFormatter(formatter)
83 importer_file_handler.setLevel(logging.DEBUG)
84 logger.addHandler(importer_file_handler)
85
86
87 session = db.session
88
89 MAPPED_VULN_SEVERITY = OrderedDict([
90 ('critical', 'critical'),
91 ('high', 'high'),
92 ('med', 'medium'),
93 ('low', 'low'),
94 ('info', 'informational'),
95 ('unclassified', 'unclassified'),
96 ('unknown', 'unclassified'),
97 ('', 'unclassified'),
98 ])
99
100 # The objects are imported in this order (the order of the list, the integer
101 # isn't related to this)
102 OBJ_TYPES = [
103 (1, 'CommandRunInformation'),
104 (1, 'Host'),
105 (1, 'EntityMetadata'),
106 (1, 'Note'),
107 (1, 'TaskGroup'),
108 (1, 'Task'),
109 (1, 'Workspace'),
110 (1, 'Reports'),
111 (1, 'Communication'),
112 (1, 'Note'),
113 (2, 'Service'),
114 (2, 'Credential'),
115 (2, 'Vulnerability'),
116 (2, 'VulnerabilityWeb'),
117 (3, 'Service'),
118 (4, 'Credential'), # Level 4 is for interface
119 (4, 'Vulnerability'),
120 (4, 'VulnerabilityWeb'),
121 ]
122
123
124 # Really ugly hack to avoid setting to null non-nullable text columns
125 for model in (
126 Command,
127 CommandObject,
128 Comment,
129 Credential,
130 ExecutiveReport,
131 Host,
132 Hostname,
133 License,
134 Methodology,
135 MethodologyTemplate,
136 PolicyViolation,
137 Reference,
138 Service,
139 Scope,
140 Tag,
141 TagObject,
142 Task,
143 TaskTemplate,
144 User,
145 Vulnerability,
146 VulnerabilityWeb,
147 VulnerabilityTemplate,
148 Workspace,
149 File,
150 ):
151 old_setattr = model.__setattr__
152
153 def __setattr__(self, key, value):
154 assert self.__table__ is not None
155 try:
156 column = self.__table__.columns[key]
157 except KeyError:
158 pass
159 else:
160 if (isinstance(column.type, (Text, String))
161 and not column.nullable
162 and value is None):
163 value = ''
164 return old_setattr(self, key, value)
165
166 model.__setattr__ = __setattr__
167
168
169 def get_object_from_couchdb(couchdb_id, workspace):
170 doc_url = 'http://{username}:{password}@{hostname}:{port}/{workspace_name}/{doc_id}'.format(
171 username=faraday.server.config.couchdb.user,
172 password=faraday.server.config.couchdb.password,
173 hostname=faraday.server.config.couchdb.host,
174 port=faraday.server.config.couchdb.port,
175 workspace_name=workspace.name,
176 doc_id=couchdb_id
177 )
178 return requests.get(doc_url).json()
179
180
181 def get_children_from_couch(workspace, parent_couchdb_id, child_type):
182 """
183 Performance for temporary views suck, so this method uploads a view and queries it instead
184
185 :param workspace: workspace to upload the view
186 :param parent_couchdb_id: ID of the parent document
187 :param child_type: type of the child obj we're looking for
188 :return:
189 """
190
191 couch_url = "http://{username}:{password}@{hostname}:{port}/{workspace_name}/".format(
192 username=faraday.server.config.couchdb.user,
193 password=faraday.server.config.couchdb.password,
194 hostname=faraday.server.config.couchdb.host,
195 port=faraday.server.config.couchdb.port,
196 workspace_name=workspace.name,
197 )
198
199 # create the new view
200 view_url = "{}_design/importer".format(couch_url)
201 view_data = {
202 "views": {
203 "children_by_parent_and_type": {
204 "map":
205 "function(doc) { id_parent = doc._id.split('.').slice(0, -1).join('.');"
206 "key = [id_parent,doc.type]; emit(key, doc); }"
207 }
208 }
209 }
210
211 try:
212 r = requests.put(view_url, json=view_data)
213 except RequestException as e:
214 logger.exception(e)
215 return []
216
217 # and now, finally query it!
218 couch_url += "_design/importer/_view/children_by_parent_and_type?" \
219 "startkey=[\"{parent_id}\",\"{child_type}\"]&" \
220 "endkey=[\"{parent_id}\",\"{child_type}\"]".format(
221 parent_id=parent_couchdb_id,
222 child_type=child_type,
223 )
224
225 try:
226 r = requests.get(couch_url)
227 except RequestException as e:
228 logger.error('Network error in CouchDB request {}'.format(
229 couch_url,
230 r.status_code,
231 r.text))
232 logger.exception(e)
233 return []
234
235 try:
236 r.raise_for_status()
237 except RequestException as e:
238 logger.error('Error in CouchDB request {}. '
239 'Status code: {}. '
240 'Body: {}'.format(couch_url,
241 r.status_code,
242 r.text))
243 logger.exception(e)
244 return []
245
246 return r.json()['rows']
247
248
249 def create_tags(raw_tags, parent_id, parent_type):
250 from slugify import slugify # pylint: disable=import-error
251 for tag_name in [x.strip() for x in raw_tags if x.strip()]:
252 tag, tag_created = get_or_create(session, Tag, slug=slugify(tag_name))
253 tag.name = tag_name
254 session.commit()
255 parent_type = parent_type.lower()
256 parent_type = parent_type.replace('web', '')
257 relation, relation_created = get_or_create(
258 session,
259 TagObject,
260 object_id=parent_id,
261 object_type=parent_type,
262 tag_id=tag.id,
263 )
264 session.commit()
265
266
267 def set_metadata(document, obj):
268 if 'metadata' in document:
269 for key, value in document['metadata'].iteritems():
270 if not value:
271 continue
272 try:
273 if key == 'create_time':
274 obj.create_date = datetime.datetime.fromtimestamp(document['metadata']['create_time'])
275 if obj.create_date > datetime.datetime.now():
276 raise Exception('Invalid date!')
277 if key == 'owner':
278 creator = User.query.filter_by(username=value).first()
279 obj.creator = creator
280 except ValueError:
281 if key == 'create_time':
282 obj.create_date = datetime.datetime.fromtimestamp(document['metadata']['create_time'] / 1000)
283 except TypeError:
284 print('')
285
286
287 def map_tool_with_command_id(command_tool_map, document):
288 try:
289 metadata = document['metadata']
290 tool = metadata['creator']
291 command_id = metadata['command_id']
292 except KeyError:
293 # Ignore objects without any of these keys
294 return
295 if not tool or not command_id:
296 # it could be blank
297 return
298 old_tool = command_tool_map.get(command_id)
299 if old_tool is not None and old_tool != tool:
300 logger.warn('Conflicting tool names for command {}: "{}" and "{}". '
301 'Using "{}"'.format(
302 command_id,
303 old_tool,
304 tool,
305 tool
306 ))
307 command_tool_map[command_id] = tool
308
309
310 def update_command_tools(workspace, command_tool_map, id_map):
311 if command_tool_map:
312 logger.debug("Setting the tool to {} commands".format(
313 len(command_tool_map)))
314 for (command_couchid, tool) in (command_tool_map.items()):
315 try:
316 map_data = id_map[command_couchid]
317
318 # There should be only one command created
319 assert len(map_data) <= 1
320 map_data = map_data[0]
321 except IndexError:
322 logger.warn("Couldn't find new numeric ID of command {}".format(
323 command_couchid
324 ))
325 continue
326 else:
327 assert map_data['type'] == 'CommandRunInformation'
328 command_id = map_data['id']
329 command = Command.query.get(command_id)
330 if command is None:
331 logger.warn("Couldn't get command {}, mapped to ID {}".format(
332 command_couchid,
333 command_id
334 ))
335 continue
336 assert workspace.id == command.workspace_id, (workspace.id, command.workspace_id)
337 if command.tool and command.tool != 'unknown':
338 logger.warn("Command {} (Couch ID {}) has already a tool. "
339 "Overriding it".format(command_id,
340 command_couchid))
341 command.tool = tool
342 session.add(command)
343 session.commit()
344 missing_tool_count = Command.query.filter_by(
345 workspace=workspace, tool="unknown").count()
346 if missing_tool_count:
347 logger.debug("Couldn't find the tool name of {} commands in "
348 "workspace {}".format(
349 missing_tool_count, workspace.name))
350
351
352 class EntityNotFound(Exception):
353 def __init__(self, entity_id):
354 super(EntityNotFound, self).__init__("Entity (%s) wasn't found" % entity_id)
355
356
357 class EntityMetadataImporter(object):
358
359 def update_from_document(self, document, workspace, level=None, couchdb_relational_map=None):
360 return
361
362 def __truncate_to_epoch_in_seconds(self, timestamp):
363 """ In a not so elegant fashion, identifies and truncate
364 epoch timestamps expressed in milliseconds to seconds"""
365 limit = 32503680000 # 01 Jan 3000 00:00:00 GMT
366 if timestamp > limit:
367 return timestamp / 1000
368 else:
369 return timestamp
370
371
372 def check_ip_address(ip_str):
373 if not ip_str:
374 return False
375 if ip_str == '0.0.0.0':
376 return False
377 if ip_str == '0000:0000:0000:0000:0000:0000:0000:0000':
378 return False
379 try:
380 IP(ip_str)
381 except ValueError:
382 return False
383 return True
384
385
386 class HostImporter(object):
387 """
388 Class interface was removed in the new model.
389 We will merge the interface data with the host.
390 For ports we will create new services for open ports
391 if it was not previously created.
392 """
393
394 def retrieve_ips_from_host_document(self, document):
395 """
396
397 :param document: json document from couchdb with host data
398 :return: str with ip or name if no valid ip was found.
399 """
400 try:
401 IP(document.get('name')) # this will raise ValueError on invalid IPs
402 yield document.get('name')
403 except ValueError:
404 host_ip = document.get('ipv4')
405 created_ipv4 = False
406 created_ipv6 = False
407 if check_ip_address(host_ip):
408 yield host_ip
409 created_ipv4 = True
410 host_ip = document.get('ipv6')
411 if check_ip_address(host_ip):
412 yield host_ip
413 if not created_ipv4 or not created_ipv6:
414 # sometimes the host lacks the ip.
415 yield document.get('name')
416 if created_ipv4 and created_ipv6:
417 logger.warn('Two host will be created one with ipv4 and another one with ipv6. Couch id is {0}'.format(document.get('_id')))
418
419 def update_from_document(self, document, workspace, level=None, couchdb_relational_map=None):
420 hosts = []
421 host_ips = [name_or_ip for name_or_ip in self.retrieve_ips_from_host_document(document)]
422 interfaces = get_children_from_couch(workspace, document.get('_id'), 'Interface')
423 command = None
424 try:
425 command = session.query(Command).get(couchdb_relational_map[document['metadata']['command_id']][0])
426 except (KeyError, IndexError):
427 command = None
428
429 for interface in interfaces:
430 interface = interface['value']
431 if check_ip_address(interface['ipv4']['address']):
432 interface_ip = interface['ipv4']['address']
433 host, created = get_or_create(session, Host, ip=interface_ip, workspace=workspace)
434 session.flush()
435 host.default_gateway_ip = interface['ipv4']['gateway']
436 self.merge_with_host(host, interface, workspace)
437 hosts.append((host, created))
438 if check_ip_address(interface['ipv6']['address']):
439 interface_ip = interface['ipv6']['address']
440 host, created = get_or_create(session, Host, ip=interface_ip, workspace=workspace)
441 session.flush()
442 host.default_gateway_ip = interface['ipv6']['gateway']
443 self.merge_with_host(host, interface, workspace)
444 hosts.append((host, created))
445 if not hosts:
446 # if not host were created after inspecting interfaces
447 # we create a host with "name" as ip to avoid losing hosts.
448 # some hosts lacks of interface
449 for name_or_ip in host_ips:
450 host, created = get_or_create(session, Host, ip=name_or_ip, workspace=workspace)
451 hosts.append((host, created))
452
453 if len(hosts) > 1:
454 logger.warning('Total hosts found {0} for couchdb id {1}'.format(len(hosts), document.get('_id')))
455
456 for host, created in hosts:
457 # we update or set other host attributes in this cycle
458 # Ticket #3387: if the 'os' field is None, we default to 'unknown
459 if command and created:
460 session.flush()
461 CommandObject.create(host, command)
462
463 if not document.get('os'):
464 document['os'] = 'unknown'
465
466 default_gateway = document.get('default_gateway', None)
467
468 host.description = document.get('description')
469 host.os = document.get('os')
470 host.default_gateway_ip = default_gateway and default_gateway[0]
471 host.default_gateway_mac = default_gateway and default_gateway[1]
472 host.owned = document.get('owned', False)
473 host.workspace = workspace
474 yield host
475
476 def merge_with_host(self, host, interface, workspace):
477 if interface['mac']:
478 host.mac = interface['mac']
479 if interface['owned']:
480 host.owned = interface['owned']
481
482 #host.default_gateway_mac
483 if interface['network_segment']:
484 host.net_segment = interface['network_segment']
485 if interface['description']:
486 if not host.description:
487 host.description = ''
488 host.description += '\n Interface data: {0}'.format(interface['description'])
489 if type(interface['hostnames']) in (str, unicode):
490 interface['hostnames'] = [interface['hostnames']]
491
492 for hostname_str in interface['hostnames'] or []:
493 if not hostname_str:
494 # skip empty hostnames
495 continue
496 hostname, created = get_or_create(
497 session,
498 Hostname,
499 name=hostname_str,
500 host=host,
501 workspace=workspace
502 )
503 host.owned = host.owned or interface['owned']
504 return host
505
506
507 class ServiceImporter(object):
508 DOC_TYPE = 'Service'
509
510 def update_from_document(self, document, workspace, level=None, couchdb_relational_map=None):
511 # service was always below interface, not it's below host.
512 command = None
513 try:
514 command = session.query(Command).get(couchdb_relational_map[document['metadata']['command_id']][0])
515 except (KeyError, IndexError):
516 command = None
517
518 # This should be safe because _id is always present and split never
519 # returns an empty list
520 parent_id = (document.get('parent') or document.get('_id')).split('.')[0]
521
522 for relational_parent_id in couchdb_relational_map[parent_id]:
523 host, created = get_or_create(session, Host, id=relational_parent_id)
524 if command and created:
525 session.flush()
526 CommandObject.create(host, command)
527 ports = document.get('ports')
528 if len(ports) > 1:
529 logger.warn('More than one port found in services!')
530 for port in ports:
531 try:
532 port = int(port)
533 except ValueError:
534 logger.warn('Port {} of service {} is not a valid '
535 'integer. Using port 65534'.format(repr(port)))
536 port = 65534
537 if port > (2**31 - 1):
538 # Bigger than the maximum int supported by postgres
539 logger.warn('Port number {} too big for service {}. '
540 'Using port 65535'.format(
541 port, document['_id']
542 ))
543 port = 65535
544 service, created = get_or_create(session,
545 Service,
546 protocol=document.get('protocol'),
547 port=port,
548 host=host)
549 service.description = document.get('description')
550 service.owned = document.get('owned', False)
551 service.banner = document.get('banner')
552 service.name = document.get('name')
553 if not document.get('status'):
554 logger.warning('Service {0} with empty status. Using \'open\' as status'.format(document['_id']))
555 document['status'] = 'open'
556 status_mapper = {
557 'open': 'open',
558 'opened': 'open',
559 'up': 'open',
560 'closed': 'closed',
561 'down': 'closed',
562 'filtered': 'filtered',
563 'open|filtered': 'filtered',
564 'unknown': 'closed',
565 '-': 'closed',
566 'running': 'open',
567 }
568 couchdb_status = document.get('status', 'open')
569 if couchdb_status.lower() not in status_mapper:
570 logger.warn('Service with unknown status "{0}" found! Status will default to open. Host is {1}'.format(couchdb_status, host.ip))
571 service.status = status_mapper.get(couchdb_status, 'open')
572 service.version = document.get('version')
573 service.workspace = workspace
574 session.flush()
575 if command and created:
576 CommandObject.create(service, command)
577
578 yield service
579
580
581 def get_or_create_user(session, username):
582 rng = SystemRandom()
583 password = "".join(
584 [rng.choice(string.ascii_letters + string.digits) for _ in
585 range(12)])
586 creator, created = get_or_create(session, User, username=username)
587 if created:
588 creator.active = False
589 creator.password = password
590 session.add(creator) # remove me
591 session.commit() # remove me
592 return creator
593
594
595 class VulnerabilityImporter(object):
596 DOC_TYPE = ['Vulnerability', 'VulnerabilityWeb']
597
598 def update_from_document(self, document, workspace, level=None, couchdb_relational_map=None):
599 command = None
600 try:
601 command = session.query(Command).get(couchdb_relational_map[document['metadata']['command_id']][0])
602 except (KeyError, IndexError):
603 command = None
604 vulnerability = None
605 couch_parent_id = document.get('parent', None)
606 if not couch_parent_id:
607 couch_parent_id = '.'.join(document['_id'].split('.')[:-1])
608 parent_ids = couchdb_relational_map[couch_parent_id]
609 mapped_severity = MAPPED_VULN_SEVERITY
610 try:
611 severity = mapped_severity[document.get('severity')]
612 except KeyError:
613 logger.warn("Unknown severity value '%s' of vuln with id %s. "
614 "Using 'unclassified'",
615 document.get('severity'), document['_id'])
616 severity = 'unclassified'
617 for parent_id in parent_ids:
618 if level == 2:
619 parent = session.query(Host).filter_by(id=parent_id).first()
620 if level == 4:
621 parent = session.query(Service).filter_by(id=parent_id).first()
622 owner_name = document.get('owner', None)
623 creator = get_or_create_user(session, owner_name)
624 if document['type'] == 'VulnerabilityWeb':
625 method = document.get('method')
626 path = document.get('path')
627 pname = document.get('pname')
628 website = document.get('website')
629 vulnerability, created = get_or_create(
630 session,
631 VulnerabilityWeb,
632 name=document.get('name'),
633 description=document.get('desc').strip().strip('\n'),
634 service_id=parent.id,
635 method=method or '',
636 parameter_name=pname or '',
637 path=path or '',
638 website=website or '',
639 workspace=workspace,
640 )
641
642 if document['type'] == 'Vulnerability':
643 vuln_params = {
644 'name': document.get('name'),
645 'workspace': workspace,
646 'description': document.get('desc').strip().strip('\n'),
647 }
648 if type(parent) == Host:
649 vuln_params.update({'host_id': parent.id})
650 elif type(parent) == Service:
651 vuln_params.update({'service_id': parent.id})
652 vulnerability, created = get_or_create(
653 session,
654 Vulnerability,
655 **vuln_params
656 )
657 vulnerability.severity = severity
658 vulnerability.creator = creator
659 vulnerability.confirmed = document.get('confirmed', False) or False
660 vulnerability.data = document.get('data')
661 vulnerability.ease_of_resolution = document.get('easeofresolution') if document.get('easeofresolution') else None
662 vulnerability.resolution = document.get('resolution')
663
664 vulnerability.owned = document.get('owned', False)
665 #vulnerability.attachments = json.dumps(document.get('_attachments', {}))
666 vulnerability.impact_accountability = document.get('impact', {}).get('accountability') or False
667 vulnerability.impact_availability = document.get('impact', {}).get('availability') or False
668 vulnerability.impact_confidentiality = document.get('impact', {}).get('confidentiality') or False
669 vulnerability.impact_integrity = document.get('impact', {}).get('integrity') or False
670 session.flush()
671 if command and created:
672 CommandObject.create(vulnerability, command)
673 if document['type'] == 'VulnerabilityWeb':
674 vulnerability.query_string = document.get('query')
675 vulnerability.request = document.get('request')
676 vulnerability.response = document.get('response')
677
678 params = document.get('params', u'')
679 if isinstance(params, (list, tuple)):
680 vulnerability.parameters = (u' '.join(params)).strip()
681 else:
682 vulnerability.parameters = params if params is not None else u''
683 status_map = {
684 'opened': 'open',
685 'open': 'open',
686 'closed': 'closed',
687 're-opened': 're-opened',
688 'risk-accepted': 'risk-accepted',
689 }
690 try:
691 status = status_map[document.get('status', 'opened')]
692 except KeyError:
693 logger.warn('Could not map vulnerability status {0}'.format(document['status']))
694 continue
695 vulnerability.status = status
696
697 vulnerability.reference_instances.update(
698 self.add_references(document, vulnerability, workspace))
699 vulnerability.policy_violation_instances.update(
700 self.add_policy_violations(document, vulnerability, workspace))
701
702 # need the vuln ID before creating Tags for it
703 session.flush()
704 tags = document.get('tags', [])
705 if tags and len(tags):
706 create_tags(tags, vulnerability.id, document['type'])
707
708 self.add_attachments(document, vulnerability, workspace)
709
710
711 yield vulnerability
712
713 def add_attachments(self, document, vulnerability, workspace):
714 attachments_data = document.get('_attachments') or {}
715 for attachment_name, attachment_data in attachments_data.items():
716 # http://localhost:5984/evidence/334389048b872a533002b34d73f8c29fd09efc50.c7b0f6cba2fae8e446b7ffedfdb18026bb9ba41d/forbidden.png
717 attachment_url = "http://{username}:{password}@{hostname}:{port}/{path}".format(
718 username=faraday.server.config.couchdb.user,
719 password=faraday.server.config.couchdb.password,
720 hostname=faraday.server.config.couchdb.host,
721 port=faraday.server.config.couchdb.port,
722 path='{0}/{1}/{2}'.format(
723 workspace.name,
724 document.get('_id'),
725 quote(attachment_name))
726 )
727 try:
728 response = requests.get(attachment_url)
729 response.raise_for_status()
730 except HTTPError:
731 logger.warn(
732 'Unable to fetch attachment {} from workspace '
733 '{}'.format(
734 attachment_name, workspace.name
735 )
736 )
737 logger.debug('Attachment URL: {}'.format(attachment_url))
738 continue
739 response.raw.decode_content = True
740 attachment_file = NamedTemporaryFile()
741 attachment_file.write(response.content)
742 attachment_file.seek(0)
743 session.commit()
744 file, created = get_or_create(
745 session,
746 File,
747 filename=attachment_name,
748 object_id=vulnerability.id,
749 object_type='vulnerability')
750 file.content = attachment_file.read()
751
752 attachment_file.close()
753
754 def add_policy_violations(self, document, vulnerability, workspace):
755 policy_violations = set()
756 for policy_violation in document.get('policyviolations', []):
757 if not policy_violation:
758 continue
759 pv, created = get_or_create(
760 session,
761 PolicyViolation,
762 name=policy_violation,
763 workspace=workspace
764 )
765 session.flush()
766 if created and pv.name not in map(lambda pva: pva.name, policy_violations):
767 policy_violations.add(pv)
768 return policy_violations
769
770 def add_references(self, document, vulnerability, workspace):
771 references = set()
772 for ref in document.get('refs', []):
773 if not ref:
774 continue
775 reference, created = get_or_create(
776 session,
777 Reference,
778 name=ref,
779 workspace=workspace
780 )
781 session.flush()
782 if created and reference not in map(lambda ref: ref.name, references):
783 references.add(reference)
784 return references
785
786
787 class CommandImporter(object):
788
789 DOC_TYPE = 'CommandRunInformation'
790 def update_from_document(self, document, workspace, level=None, couchdb_relational_map=None):
791 import_source = 'shell'
792 if document.get('command', '').startswith('Import '):
793 import_source = 'report'
794 # Now that we have a field that distinguished between shell commands
795 # and imported reports, it is no longer required to directly format
796 # in hte command field
797 document['command'] = document['command'][len('Import '):-1]
798
799 start_date = datetime.datetime.fromtimestamp(document.get('itime'))
800
801 command, instance = get_or_create(
802 session,
803 Command,
804 command=document.get('command', None),
805 start_date=start_date,
806 workspace=workspace,
807
808 )
809 if document.get('duration'):
810 command.end_date = start_date + datetime.timedelta(seconds=document.get('duration'))
811
812 command.import_source = import_source
813 command.command = document.get('command', None)
814 command.ip = document.get('ip', None)
815 command.hostname = document.get('hostname', None)
816 command.params = document.get('params', None)
817 command.user = document.get('user', None)
818 command.tool = 'unknown' # It will be updated later
819 command.workspace = workspace
820
821 yield command
822
823
824 class NoteImporter(object):
825
826 def update_from_document(self, document, workspace, level=None, couchdb_relational_map=None):
827 couch_parent_id = '.'.join(document['_id'].split('.')[:-1])
828 parent_document = get_object_from_couchdb(couch_parent_id, workspace)
829 comment, created = get_or_create(
830 session,
831 Comment,
832 text='{0}\n{1}'.format(document.get('text', ''), document.get('description', '')),
833 object_id=couchdb_relational_map[parent_document.get('_id')],
834 object_type=parent_document['type'].lower(),
835 workspace=workspace)
836 yield comment
837
838
839 class CredentialImporter(object):
840
841 DOC_TYPE = 'Cred'
842 def update_from_document(self, document, workspace, level=None, couchdb_relational_map=None):
843 command = None
844 try:
845 command = session.query(Command).get(couchdb_relational_map[document['metadata']['command_id']][0])
846 except (KeyError, IndexError):
847 command = None
848 parents = []
849 if level == 2:
850 parent_ids = couchdb_relational_map[document['_id'].split('.')[0]]
851 parents = session.query(Host).filter(Host.id.in_(parent_ids)).all()
852 if level == 4:
853 parent_ids = couchdb_relational_map['.'.join(document['_id'].split('.')[:3])]
854 parents = session.query(Service).filter(Service.id.in_(parent_ids)).all()
855 if not parents:
856 raise Exception('Missing host or service for credential {0}'.format(document['_id']))
857 for parent in parents:
858 service = None
859 host = None
860 if isinstance(parent, Host):
861 host = parent
862 if isinstance(parent, Service):
863 service = parent
864 credential, created = get_or_create(
865 session,
866 Credential,
867 username=document.get('username'),
868 password=document.get('password'),
869 host=host,
870 service=service,
871 workspace=workspace,
872 )
873 credential.password = document.get('password', None)
874 credential.owned = document.get('owned', False)
875 credential.description = document.get('description', None)
876 credential.name = document.get('name', None)
877 credential.workspace = workspace
878 if command and created:
879 session.flush()
880 CommandObject.create(credential, command)
881 yield credential
882
883
884 class WorkspaceImporter(object):
885
886 DOC_TYPE = 'Workspace'
887 def update_from_document(self, document, workspace, level=None, couchdb_relational_map=None):
888 workspace.description = document.get('description')
889 if document.get('duration') and document.get('duration')['start']:
890 workspace.start_date = datetime.datetime.fromtimestamp(float(document.get('duration')['start'])/1000)
891 if document.get('duration') and document.get('duration')['end']:
892 workspace.end_date = datetime.datetime.fromtimestamp(float(document.get('duration')['end'])/1000)
893 for scope in [x.strip() for x in document.get('scope', '').split('\n') if x.strip()]:
894 scope_obj, created = get_or_create(session, Scope, name=scope, workspace=workspace)
895 session.flush() # This fixes integrity errors for duplicate scope elements
896 users = document.get('users', [])
897 if not users:
898 workspace.public = True
899 for username in users:
900 user = session.query(User).filter_by(username=username).first()
901 if user is None:
902 logger.warn('User {} not found but it has permissions for '
903 'workspace {}. Ignoring'.format(username,
904 workspace.name))
905 continue
906 (perm, created) = get_or_create(session, WorkspacePermission,
907 user=user, workspace=workspace)
908 yield workspace
909
910
911 class MethodologyImporter(object):
912 def update_from_document(self, document, workspace, level=None, couchdb_relational_map=None):
913 if document.get('group_type') == 'template':
914 methodology, created = get_or_create(session, MethodologyTemplate, name=document.get('name'))
915 yield methodology
916
917 if document.get('group_type') == 'instance':
918 methodology, created = get_or_create(session, Methodology, name=document.get('name'), workspace=workspace)
919 yield methodology
920
921
922 class TaskImporter(object):
923
924 def update_from_document(self, document, workspace, level=None, couchdb_relational_map=None):
925 try:
926 methodology_id = couchdb_relational_map[document.get('group_id')][0]
927 except KeyError:
928 logger.warn('Could not found methodology with id {0}'.format(document.get('group_id')))
929 return []
930 except IndexError:
931 logger.warn('Could not find methodology {0} of task {1}'.format(document.get('group_id'), document.get('_id')))
932 return []
933
934 if len(couchdb_relational_map[document.get('group_id')]) > 1:
935 logger.error('It was expected only one parent in methodology {0}'.format(document.get('_id')))
936
937 methodology = session.query(Methodology).filter_by(id=methodology_id, workspace=workspace).first()
938 task_class = Task
939 if not methodology:
940 methodology = session.query(MethodologyTemplate).filter_by(id=methodology_id).first()
941 task_class = TaskTemplate
942
943 if task_class == TaskTemplate:
944 task, task_created = get_or_create(session, task_class, name=document.get('name'))
945 task.template = methodology
946 else:
947 task, task_created = get_or_create(session, task_class, name=document.get('name'), workspace=workspace)
948 task.methodology = methodology
949
950 task.description = document.get('description')
951
952 assigned_users = []
953
954 for username in document.get('assigned_to', []):
955 if username:
956 user = session.query(User).filter_by(username=username).first()
957 if user:
958 assigned_users.append(user)
959
960 task.assigned_to = assigned_users
961
962 mapped_status = {
963 'New': 'new',
964 'In Progress': 'in progress',
965 'Review': 'review',
966 'Completed': 'completed'
967 }
968 task.status = mapped_status[document.get('status')]
969
970 # we need the ID of the Task in order to add tags to it
971 session.commit()
972 tags = document.get('tags', [])
973 if len(tags):
974 create_tags(tags, task.id, 'task')
975 #task.due_date = datetime.datetime.fromtimestamp(document.get('due_date'))
976 return [task]
977
978
979
980 class ReportsImporter(object):
981
982 def update_from_document(self, document, workspace, level=None, couchdb_relational_map=None):
983 report, created = get_or_create(session, ExecutiveReport, name=document.get('name'))
984 report.template_name = document.get('template_name', 'generic_default.docx')
985 report.title = document.get('title')
986 report.status = document.get('status')
987 # TODO: add tags
988 report.conclusions = document.get('conclusions')
989 report.confirmed = document.get('confirmed', False)
990 report.summary = document.get('summary')
991 report.recommendations = document.get('recommendations')
992 report.enterprise = document.get('enterprise')
993 report.summary = document.get('summary')
994 report.scope = document.get('scope')
995 report.objectives = document.get('objectives')
996 report.grouped = document.get('grouped', False)
997 report.workspace = workspace
998 try:
999 report.vuln_count = document['totalVulns']['total']
1000 except KeyError:
1001 logger.warning("Couldn't load vuln count of report".format(document.get('_id')))
1002 if document.get('owner'):
1003 report.creator = get_or_create_user(session, document.get('owner'))
1004 session.flush()
1005 old_attachments = session.query(File).filter_by(
1006 object_id=report.id,
1007 object_type='vulnerability',
1008 )
1009 for old_attachment in old_attachments:
1010 db.session.delete(old_attachment)
1011 for filename, attachment in document.get('_attachments', {}).items():
1012 attachment_url = "http://{username}:{password}@{hostname}:{port}/{path}".format(
1013 username=faraday.server.config.couchdb.user,
1014 password=faraday.server.config.couchdb.password,
1015 hostname=faraday.server.config.couchdb.host,
1016 port=faraday.server.config.couchdb.port,
1017 path='{0}/{1}/{2}'.format(workspace.name, document.get('_id'),
1018 filename)
1019 )
1020 response = requests.get(attachment_url)
1021 response.raw.decode_content = True
1022 faraday_file = response.content
1023 file, created = get_or_create(
1024 session,
1025 File,
1026 object_id=report.id,
1027 object_type='executive_report',
1028 name=os.path.splitext(os.path.basename(filename))[0],
1029 filename=os.path.basename(filename),
1030 )
1031 file.content=faraday_file
1032 yield report
1033
1034
1035 class CommunicationImporter(object):
1036 def update_from_document(self, document, workspace, level=None, couchdb_relational_map=None):
1037 comment, created = get_or_create(
1038 session,
1039 Comment,
1040 text=document.get('text'),
1041 object_id=workspace.id,
1042 object_type='workspace',
1043 workspace=workspace)
1044 yield comment
1045
1046
1047 class FaradayEntityImporter(object):
1048 # Document Types: [u'Service', u'Communication', u'Vulnerability', u'CommandRunInformation', u'Reports', u'Host', u'Workspace']
1049
1050 def __init__(self, workspace_name):
1051 self.workspace_name = workspace_name
1052
1053 def parse(self, document):
1054 """Get an instance of a DAO object given a document"""
1055 importer_class = self.get_importer_from_document(document)
1056 if importer_class is not None:
1057 importer = importer_class()
1058 entity = importer.update_from_document(document)
1059 metadata = EntityMetadataImporter().update_from_document(document)
1060 entity.entity_metadata = metadata
1061 return importer, entity
1062 return None, None
1063
1064 def get_importer_from_document(self, doc_type):
1065 logger.debug('Getting class importer for {0} in workspace {1}'.format(doc_type, self.workspace_name))
1066 importer_class_mapper = {
1067 'EntityMetadata': EntityMetadataImporter,
1068 'Host': HostImporter,
1069 'Service': ServiceImporter,
1070 'Note': NoteImporter,
1071 'Credential': CredentialImporter,
1072 'CommandRunInformation': CommandImporter,
1073 'Workspace': WorkspaceImporter,
1074 'Vulnerability': VulnerabilityImporter,
1075 'VulnerabilityWeb': VulnerabilityImporter,
1076 'TaskGroup': MethodologyImporter,
1077 'Task': TaskImporter,
1078 'Reports': ReportsImporter,
1079 'Communication': CommunicationImporter
1080 }
1081 importer_self = importer_class_mapper.get(doc_type, None)
1082 if not importer_self:
1083 raise NotImplementedError('Class importer for {0} not implemented'.format(doc_type))
1084 return importer_self
1085
1086
1087 class ImportCouchDBUsers():
1088
1089 def modular_crypt_pbkdf2_sha1(self, checksum, salt, iterations=1000):
1090 return '$pbkdf2${iterations}${salt}${checksum}'.format(
1091 iterations=iterations,
1092 salt=ab64_encode(salt),
1093 checksum=ab64_encode(unhexlify(checksum)),
1094 )
1095
1096 def convert_couchdb_hash(self, original_hash):
1097 if not original_hash.startswith(COUCHDB_PASSWORD_PREFIX):
1098 # Should be a plaintext password
1099 return original_hash
1100 checksum, salt, iterations = original_hash[
1101 len(COUCHDB_PASSWORD_PREFIX):].split(',')
1102 iterations = int(iterations)
1103 return self.modular_crypt_pbkdf2_sha1(checksum, salt, iterations)
1104
1105 def get_hash_from_document(self, doc):
1106 scheme = doc.get('password_scheme', 'unset')
1107 if scheme != 'pbkdf2':
1108 # Flask Security will encrypt the password next time the user logs in.
1109 logger.warning('Found user {0} without password. Setting its '
1110 'password to "changeme"'.format(doc.get('name')))
1111 return 'changeme'
1112 return self.modular_crypt_pbkdf2_sha1(doc['derived_key'], doc['salt'],
1113 doc['iterations'])
1114
1115 def parse_all_docs(self, doc):
1116 return [row['doc'] for row in doc['rows']]
1117
1118 def get_users_and_admins(self):
1119 admins_url = "http://{username}:{password}@{hostname}:{port}/{path}".format(
1120 username=faraday.server.config.couchdb.user,
1121 password=faraday.server.config.couchdb.password,
1122 hostname=faraday.server.config.couchdb.host,
1123 port=faraday.server.config.couchdb.port,
1124 path='_config/admins'
1125 )
1126
1127 users_url = "http://{username}:{password}@{hostname}:{port}/{path}".format(
1128 username=faraday.server.config.couchdb.user,
1129 password=faraday.server.config.couchdb.password,
1130 hostname=faraday.server.config.couchdb.host,
1131 port=faraday.server.config.couchdb.port,
1132 path='_users/_all_docs?include_docs=true'
1133 )
1134 admins = requests.get(admins_url).json()
1135 users = requests.get(users_url).json()
1136 return users, admins
1137
1138 def import_admins(self, admins):
1139 # Import admin users
1140 for (username, password) in admins.items():
1141 logger.debug('Creating user {0}'.format(username))
1142 admin = db.session.query(User).filter_by(username=username).first()
1143 if not admin:
1144 app.user_datastore.create_user(
1145 username=username,
1146 email=username + '@test.com',
1147 password=self.convert_couchdb_hash(password),
1148 is_ldap=False,
1149 role='admin'
1150 )
1151 else:
1152 admin.password=self.convert_couchdb_hash(password)
1153
1154 def import_users(self, all_users, admins):
1155 # Import non admin users
1156 if 'error' in all_users:
1157 raise Exception(all_users['reason'])
1158 for user in all_users['rows']:
1159 user = user['doc']
1160 if not user['_id'].startswith(COUCHDB_USER_PREFIX):
1161 # It can be a view or something other than a user
1162 continue
1163 if user['name'] in admins.keys():
1164 # This is an already imported admin user, skip
1165 continue
1166 try:
1167 role = user['roles'][0]
1168 except (KeyError, IndexError):
1169 role = 'client'
1170 else:
1171 if role not in ['admin', 'client', 'pentester']:
1172 logger.warn(
1173 "Invalid role for user {}: {}".format(user['name'],
1174 role)
1175 )
1176 role = 'client'
1177 logger.debug(u'Importing user {0}'.format(user['name']))
1178 old_user = db.session.query(User).filter_by(username=user['name']).first()
1179 if not old_user:
1180 app.user_datastore.create_user(
1181 username=user['name'],
1182 email=user['name'] + '@test.com',
1183 password=self.get_hash_from_document(user),
1184 is_ldap=False,
1185 role=role,
1186 )
1187 else:
1188 old_user.password = self.get_hash_from_document(user)
1189
1190 def run(self):
1191 all_users, admins = self.get_users_and_admins()
1192 self.import_users(all_users, admins)
1193 self.import_admins(admins)
1194 db.session.commit()
1195
1196
1197 class ImportVulnerabilityTemplates():
1198
1199 def __init__(self):
1200 self.names = Counter()
1201
1202 def run(self):
1203 logger.debug("Importing vulnerability templates")
1204 cwe_url = "http://{username}:{password}@{hostname}:{port}/{path}".format(
1205 username=faraday.server.config.couchdb.user,
1206 password=faraday.server.config.couchdb.password,
1207 hostname=faraday.server.config.couchdb.host,
1208 port=faraday.server.config.couchdb.port,
1209 path='cwe/_all_docs?include_docs=true'
1210 )
1211
1212 try:
1213 cwes = requests.get(cwe_url)
1214 cwes.raise_for_status()
1215 except HTTPError:
1216 logger.warn('Unable to retrieve Vulnerability Templates Database. Moving on.')
1217 return
1218 except RequestException as e:
1219 logger.exception(e)
1220 return
1221
1222 for cwe in (cwes.json()['rows']):
1223 document = cwe['doc']
1224 new_severity = self.get_severity(document)
1225
1226 new_name = self.get_name(document)
1227
1228 vuln_template, created = get_or_create(session,
1229 VulnerabilityTemplate,
1230 name=new_name)
1231
1232 vuln_template.description = document.get('description')
1233 vuln_template.resolution = document.get('resolution')
1234 vuln_template.severity = new_severity
1235
1236 if isinstance(document.get('references'), list):
1237 references = document.get('references')
1238 elif isinstance(document.get('references'), (str, unicode)):
1239 references = [x.strip()
1240 for x in document.get('references').split(',')
1241 if x.strip()]
1242 else:
1243 logger.warn("Unknown type of vuln template references: {}. "
1244 "Reference data: {}".format(
1245 type(document.get('references')),
1246 document))
1247 continue
1248 cwe_field = document.get('cwe')
1249 if cwe_field not in references:
1250 references.append(cwe_field)
1251 for ref_doc in references:
1252 vuln_template.references.add(ref_doc)
1253
1254
1255
1256 def get_name(self, document):
1257 doc_name = document.get('name')
1258 count = self.names[doc_name]
1259
1260 if count > 0:
1261 name = u'{0} ({1})'.format(doc_name, count)
1262 else:
1263 name = doc_name
1264
1265 self.names[doc_name] += 1
1266
1267 return name
1268
1269 def get_severity(self, document):
1270 default = 'unclassified'
1271
1272 mapped_exploitation = MAPPED_VULN_SEVERITY
1273
1274 for key in mapped_exploitation.keys():
1275 if key in document.get('exploitation','').lower():
1276 return mapped_exploitation[key]
1277
1278 logger.warn(
1279 'Vuln template exploitation \'{0}\' not found. Using \'{1}\' instead.'.format(document.get('exploitation'), default)
1280 )
1281
1282 return default
1283
1284
1285 class ImportLicense():
1286
1287 def run(self):
1288 licenses_url = "http://{username}:{password}@{hostname}:{port}/{path}".format(
1289 username=faraday.server.config.couchdb.user,
1290 password=faraday.server.config.couchdb.password,
1291 hostname=faraday.server.config.couchdb.host,
1292 port=faraday.server.config.couchdb.port,
1293 path='faraday_licenses/_all_docs?include_docs=true'
1294 )
1295
1296 try:
1297 licenses = requests.get(licenses_url)
1298 licenses.raise_for_status()
1299 except HTTPError:
1300 logger.warn('Unable to retrieve Licenses Database. Moving on.')
1301 return
1302 except RequestException as e:
1303 logger.exception(e)
1304 return
1305
1306 for license in licenses.json()['rows']:
1307 document = license['doc']
1308
1309 license_obj, created = get_or_create(session,
1310 License,
1311 product=document.get('product'),
1312 start_date=datetime.datetime.strptime(document['start'], "%Y-%m-%dT%H:%M:%S.%fZ"),
1313 end_date=datetime.datetime.strptime(document['end'], "%Y-%m-%dT%H:%M:%S.%fZ"),
1314 )
1315 license_obj.notes=document.get('notes')
1316 license_obj.type=document.get('lictype')
1317
1318
1319 class ImportCouchDB():
1320 def _open_couchdb_conn(self):
1321
1322 self.couch_url = "http://{username}:{password}@{hostname}:{port}".format(
1323 username=faraday.server.config.couchdb.user,
1324 password=faraday.server.config.couchdb.password,
1325 hostname=faraday.server.config.couchdb.host,
1326 port=faraday.server.config.couchdb.port,
1327 )
1328
1329 try:
1330 workspaces_list = requests.get('{0}/_all_dbs'.format(self.couch_url)).json()
1331 except Exception as ex:
1332 print(ex)
1333 sys.exit(1)
1334
1335 return workspaces_list
1336
1337 def has_access_to(self, workspace_name):
1338 response = requests.get('{0}/{1}/_security'.format(self.couch_url, workspace_name))
1339 if response.status_code == 401:
1340 return False
1341 return True
1342
1343 def run(self):
1344 """
1345 Main entry point for couchdb import
1346 """
1347 workspaces_list = self._open_couchdb_conn()
1348 license_import = ImportLicense()
1349 license_import.run()
1350 vuln_templates_import = ImportVulnerabilityTemplates()
1351 vuln_templates_import.run()
1352 users_import = ImportCouchDBUsers()
1353 users_import.run()
1354
1355 logger.info('Importing workspaces. Using {0} threads'.format(multiprocessing.cpu_count() * 2))
1356 for workspace_name in workspaces_list:
1357 logger.debug(u'Setting up workspace {}'.format(workspace_name))
1358
1359 if not self.has_access_to(workspace_name):
1360 logger.error(u"Unauthorized access to CouchDB. Make sure faraday-server's"\
1361 " configuration file has CouchDB admin's credentials set")
1362 sys.exit(1)
1363 self.import_workspace_into_database(workspace_name)
1364
1365 def get_objs(self, host, obj_type, level, workspace):
1366 if obj_type == 'Credential':
1367 obj_type = 'Cred'
1368 data = {
1369 "map": "function(doc) { if(doc.type == '%s' && doc._id.split('.').length == %d && !doc._deleted) emit(null, doc); }" % (obj_type, level)
1370 }
1371
1372 documents = requests.post(host, json=data).json()
1373 return documents
1374
1375 def verify_host_vulns_count_is_correct(self, couchdb_relational_map, couchdb_relational_map_by_type, workspace):
1376 hosts = session.query(Host).filter_by(workspace=workspace)
1377 logger.info('Verifying data migration')
1378 for host in tqdm(hosts, total=hosts.count()):
1379 parent_couchdb_id = None
1380 for couchdb_id, relational_ids in couchdb_relational_map_by_type.items():
1381 for obj_data in relational_ids:
1382 if obj_data['type'] == 'Host' and host.id == obj_data['id']:
1383 parent_couchdb_id = couchdb_id
1384 break
1385 if parent_couchdb_id:
1386 break
1387 if not parent_couchdb_id:
1388 logger.warn('Could not found couchdb id! This is fine if you created hosts after migration')
1389 continue
1390 vulns = get_children_from_couch(workspace, parent_couchdb_id, 'Vulnerability')
1391 interfaces = get_children_from_couch(workspace, parent_couchdb_id, 'Interface')
1392 for interface in interfaces:
1393 interface = interface['value']
1394 vulns += get_children_from_couch(workspace, interface.get('_id'), 'Vulnerability')
1395
1396 old_host_count = len(set(map(lambda vuln: vuln['value'].get('name'), vulns)))
1397 new_host_count = len(set(map(lambda vuln: vuln.name, host.vulnerabilities)))
1398 if old_host_count != new_host_count:
1399 logger.info("Host count didn't match")
1400 if old_host_count < new_host_count:
1401 logger.warn('More host were found in postgreSQL. This is normal if you used the workspace {0}'.format(workspace.id))
1402 if new_host_count < old_host_count:
1403 logger.error('Some hosts were not imported!!')
1404
1405 def verify_import_data(self, couchdb_relational_map, couchdb_relational_map_by_type, workspace):
1406 self.verify_host_vulns_count_is_correct(couchdb_relational_map, couchdb_relational_map_by_type, workspace)
1407 all_docs_url = "http://{username}:{password}@{hostname}:{port}/{workspace_name}/_all_docs?include_docs=true".format(
1408 username=faraday.server.config.couchdb.user,
1409 password=faraday.server.config.couchdb.password,
1410 hostname=faraday.server.config.couchdb.host,
1411 port=faraday.server.config.couchdb.port,
1412 workspace_name=workspace.name
1413 )
1414 all_ids = map(lambda x: x['doc']['_id'], requests.get(all_docs_url).json()['rows'])
1415 if len(all_ids) != len(couchdb_relational_map.keys()):
1416 missing_objs_filename = os.path.join(os.path.expanduser('~/.faraday'), 'logs', 'import_missing_objects_{0}.json'.format(workspace.name))
1417 missing_ids = set(all_ids) - set(couchdb_relational_map.keys())
1418 missing_ids = set([x for x in missing_ids if not re.match(r'^\_design', x)])
1419 objs_diff = []
1420 if missing_ids:
1421 logger.debug('Downloading missing couchdb docs')
1422 for missing_id in (missing_ids):
1423 not_imported_obj = get_object_from_couchdb(missing_id, workspace)
1424
1425 if not_imported_obj.get('type', None) == 'Interface':
1426 # we know that interface obj was not imported
1427 continue
1428 filter_keys = ['views', 'validate_doc_update']
1429 if not any(map(lambda x: x not in filter_keys, not_imported_obj.keys())):
1430 # we filter custom views, validation funcs, etc
1431 logger.warning(
1432 'Not all objects were imported. Saving difference to file {0}'.format(missing_objs_filename))
1433 objs_diff.append(not_imported_obj)
1434
1435 with open(missing_objs_filename, 'w') as missing_objs_file:
1436 missing_objs_file.write(json.dumps(objs_diff))
1437
1438 def import_level_objects(self, couch_url, faraday_importer, couchdb_relational_map_by_type, couchdb_relational_map, command_tool_map, level, obj_type, workspace):
1439 obj_importer = faraday_importer.get_importer_from_document(obj_type)()
1440 objs_dict = self.get_objs(couch_url, obj_type, level, workspace)
1441 print('Importing {0} from workspace {1}'.format(obj_type, workspace.name))
1442 for raw_obj in tqdm(objs_dict.get('rows', [])):
1443 # we use no_autoflush since some queries triggers flush and some relationship are missing in the middle
1444 with session.no_autoflush:
1445 raw_obj = raw_obj['value']
1446 couchdb_id = raw_obj['_id']
1447
1448 # first let's make sure no invalid chars are present in the Raw objects
1449 raw_obj = invalid_chars.clean_dict(raw_obj)
1450
1451 for new_obj in obj_importer.update_from_document(raw_obj, workspace, level, couchdb_relational_map):
1452 if not new_obj:
1453 continue
1454 set_metadata(raw_obj, new_obj)
1455 map_tool_with_command_id(command_tool_map,
1456 raw_obj)
1457 session.commit()
1458 couchdb_relational_map_by_type[couchdb_id].append({'type': obj_type, 'id': new_obj.id})
1459 couchdb_relational_map[couchdb_id].append(new_obj.id)
1460
1461 def import_workspace_into_database(self, workspace_name):
1462 with app.app_context():
1463
1464 faraday_importer = FaradayEntityImporter(workspace_name)
1465 workspace, created = get_or_create(session, Workspace, name=workspace_name)
1466 session.commit()
1467
1468 couch_url = "http://{username}:{password}@{hostname}:{port}/{workspace_name}/_temp_view?include_docs=true".format(
1469 username=faraday.server.config.couchdb.user,
1470 password=faraday.server.config.couchdb.password,
1471 hostname=faraday.server.config.couchdb.host,
1472 port=faraday.server.config.couchdb.port,
1473 workspace_name=workspace_name
1474 )
1475
1476 # obj_types are tuples. the first value is the level on the tree
1477 # for the desired obj.
1478 obj_types = OBJ_TYPES
1479 couchdb_relational_map = defaultdict(list)
1480 couchdb_relational_map_by_type = defaultdict(list)
1481 command_tool_map = {}
1482 for level, obj_type in obj_types:
1483 try:
1484 self.import_level_objects(
1485 couch_url,
1486 faraday_importer,
1487 couchdb_relational_map_by_type,
1488 couchdb_relational_map,
1489 command_tool_map,
1490 level,
1491 obj_type,
1492 workspace
1493 )
1494 except Exception as ex:
1495 logger.exception(ex)
1496 continue
1497 update_command_tools(workspace, command_tool_map,
1498 couchdb_relational_map_by_type)
1499 session.commit()
1500 self.verify_import_data(couchdb_relational_map, couchdb_relational_map_by_type, workspace)
1501
1502 session.expunge_all()
1503 session.close()
1504 return created
488488 id = Column(Integer, primary_key=True)
489489 field_name = Column(Text, unique=True)
490490 field_type = Column(Text)
491 field_metadata = Column(JSONType, nullable=True)
491492 field_display_name = Column(Text)
492493 field_order = Column(Integer)
493494 table_name = Column(Text)
19231924 value = Column(String, nullable=True)
19241925
19251926
1927 class Executor(Metadata):
1928 __tablename__ = 'executor'
1929 id = Column(Integer, primary_key=True)
1930 name = Column(String, nullable=False)
1931 agent_id = Column(Integer, ForeignKey('agent.id'), index=True, nullable=False)
1932 agent = relationship(
1933 'Agent',
1934 backref=backref('executors', cascade="all, delete-orphan"),
1935 )
1936 parameters_metadata = Column(JSONType, nullable=False, default={})
1937 # workspace_id = Column(Integer, ForeignKey('workspace.id'), index=True, nullable=False)
1938 # workspace = relationship('Workspace', backref=backref('executors', cascade="all, delete-orphan"))
1939
1940 __table_args__ = (
1941 UniqueConstraint('name', 'agent_id',
1942 name='uix_executor_table_agent_id_name'),)
1943
1944
19261945 class AgentsSchedule(Metadata):
19271946 __tablename__ = 'agent_schedule'
19281947 id = Column(Integer, primary_key=True)
19371956 'Workspace',
19381957 backref=backref('schedules', cascade="all, delete-orphan"),
19391958 )
1940
1941 agent_id = Column(Integer, ForeignKey('agent.id'), index=True, nullable=False)
1942 agent = relationship(
1943 'Agent',
1959 executor_id = Column(Integer, ForeignKey('executor.id'), index=True, nullable=False)
1960 executor = relationship(
1961 'Executor',
19441962 backref=backref('schedules', cascade="all, delete-orphan"),
19451963 )
1964
1965 parameters = Column(JSONType, nullable=False, default={})
19461966
19471967 @property
19481968 def next_run(self):
19832003
19842004 @property
19852005 def is_online(self):
1986 from faraday.server.websocket_factories import connected_agents
2006 from faraday.server.websocket_factories import connected_agents # pylint:disable=import-outside-toplevel
19872007 return self.id in connected_agents
19882008
19892009 @property
19972017 return 'paused'
19982018
19992019
2020 class AgentExecution(Metadata):
2021 __tablename__ = 'agent_execution'
2022 id = Column(Integer, primary_key=True)
2023 running = Column(Boolean, nullable=True)
2024 successful = Column(Boolean, nullable=True)
2025 message = Column(String, nullable=True)
2026 executor_id = Column(Integer, ForeignKey('executor.id'), index=True, nullable=False)
2027 executor = relationship('Executor', foreign_keys=[executor_id], backref=backref('executions', cascade="all, delete-orphan"))
2028 workspace_id = Column(Integer, ForeignKey('workspace.id'), index=True, nullable=False)
2029 workspace = relationship(
2030 'Workspace',
2031 backref=backref('agent_executions', cascade="all, delete-orphan"),
2032 )
2033
2034 @property
2035 def parent(self):
2036 return
2037
2038
20002039 class Condition(Metadata):
20012040 __tablename__ = 'condition'
20022041
20302069 @property
20312070 def parent(self):
20322071 return
2072
2073
2074 class SearchFilter(Metadata):
2075
2076 __tablename__ = 'search_filter'
2077 id = Column(Integer, primary_key=True)
2078 name = Column(String, nullable=False)
2079 json_query = Column(String, nullable=False) # meant to store json but just readonly
2080 user_query = Column(String, nullable=False)
20332081
20342082
20352083 # This constraint uses Columns from different classes
20712119
20722120 # We have to import this after all models are defined
20732121 import faraday.server.events
2122 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 import time
7 import json
87 import datetime
98 from flask import g
109 from marshmallow import fields, Schema
6362 def _deserialize(self, value, attr, data, **kwargs):
6463 serialized = {}
6564 if value is not None and value:
66 for key, raw_data in value.iteritems():
65 for key, raw_data in value.items():
6766 if not raw_data:
6867 continue
6968 field_schema = db.session.query(CustomFieldsSchema).filter_by(
8382 raise ValidationError("Can not convert custom type to int")
8483 elif field_schema.field_type == 'list':
8584 serialized[key] = raw_data
85 elif field_schema.field_type == 'choice':
86 serialized[key] = str(raw_data)
8687 else:
8788 raise ValidationError("Custom Field datatype not supported yet")
8889
276277 date.astimezone(tzutc())
277278 date = date.replace(tzinfo=None)
278279 return date
280 # I'm Py3
0 import logging
1 from threading import Thread
2 from queue import Queue, Empty
3 import time
4 import os
5 from faraday_plugins.plugins.manager import PluginsManager, ReportAnalyzer
6 from faraday.server.api.modules.bulk_create import bulk_create
7
8 from faraday.server.models import Workspace
9
10 logger = logging.getLogger(__name__)
11
12
13 REPORTS_QUEUE = Queue()
14
15
16 class ReportsManager(Thread):
17
18 def __init__(self, upload_reports_queue, *args, **kwargs):
19 super().__init__(*args, **kwargs)
20 self.upload_reports_queue = upload_reports_queue
21 self.plugins_manager = PluginsManager()
22 self._must_stop = False
23
24 def stop(self):
25 logger.debug("Stop Reports Manager")
26 self._must_stop = True
27
28 def send_report_request(self, workspace_name, report_json):
29 logger.info("Send Report data to workspace [%s]", workspace_name)
30 from faraday.server.web import app # pylint:disable=import-outside-toplevel
31 with app.app_context():
32 ws = Workspace.query.filter_by(name=workspace_name).one()
33 bulk_create(ws, report_json, False)
34
35 def process_report(self, workspace, file_path):
36 report_analyzer = ReportAnalyzer(self.plugins_manager)
37 plugin = report_analyzer.get_plugin(file_path)
38 if plugin:
39 try:
40 logger.info("Processing report [%s] with plugin [%s]", file_path, plugin.id)
41 plugin.processReport(file_path)
42 vulns_data = plugin.get_data()
43 except Exception as e:
44 logger.error("Processing Error: %s", e)
45 logger.exception(e)
46 else:
47 try:
48 self.send_report_request(workspace, vulns_data)
49 logger.info("Report processing finished")
50 except Exception as e:
51 logger.exception(e)
52 logger.error("Save Error: %s", e)
53 else:
54 logger.info("No plugin detected for report [%s]", file_path)
55
56 def run(self):
57 logger.debug("Start Reports Manager")
58 while not self._must_stop:
59 try:
60 workspace, file_path = self.upload_reports_queue.get(False, timeout=0.1)
61 logger.info("Processing raw report %s", file_path)
62 if os.path.isfile(file_path):
63 self.process_report(workspace, file_path)
64 else:
65 logger.warning("Report file [%s] don't exists", file_path)
66 except Empty:
67 time.sleep(0.1)
68 except KeyboardInterrupt as ex:
69 logger.info("Keyboard interrupt, stopping report processing thread")
70 self.stop()
71 except Exception as ex:
72 logger.exception(ex)
73 continue
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
33
4 # I'm Py3
1717 return rv
1818 return decorated_function
1919 return decorator
20 # I'm Py3
44 # Copyright (C) 2005 Chad J. Schroeder
55 # Modified version of a script created by Chad J. Schroeder, obtained from
66 # http://code.activestate.com/recipes/278731-creating-a-daemon-the-python-way/
7
87 import os
98 import re
109 import sys
121120 # don't close them off after successfully forking the process
122121
123122 # Close and redirect std file descriptors to /dev/null
124 std_fileno = map(lambda s: s.fileno(), [sys.stdin, sys.stdout, sys.stderr])
123 std_fileno = list(map(lambda s: s.fileno(), [sys.stdin, sys.stdout, sys.stderr]))
125124 null = os.open(REDIRECT_TO, os.O_RDWR)
126125
127126 for fd in std_fileno:
228227 ports.append(int(port))
229228
230229 return ports
230 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3 from functools import reduce
34
45 import operator
56 from sqlalchemy import distinct, Boolean
3435 # Apply the proper sqlalchemy function for sorting direction over every
3536 # column declared on field_to_col_map[order_field]
3637 dir_func = asc if order_dir == ORDER_DIRECTIONS.ASCENDING else desc
37 order_cols = map(dir_func, order_cols)
38 order_cols = list(map(dir_func, order_cols))
3839 else:
3940 # Use default ordering if declared if any parameter didn't met the requirements
4041 order_cols = [default] if default is not None else None
7879 # currently we are not supporting searches on this
7980 # kind of fields since they are usually referred to
8081 # query built values (like counts)
81 if isinstance(column, basestring):
82 if isinstance(column, str):
8283 continue
8384
8485 # Prepare a SQL search term according to the columns type.
190191 )
191192 return res
192193
194
193195 class BooleanToIntColumn(expression.FunctionElement):
194196
195197 def __init__(self, expression):
200202 @compiler.compiles(BooleanToIntColumn, 'postgresql')
201203 def _integer_to_boolean_postgresql(element, compiler, **kw):
202204 return '{0}::int'.format(element.expression_str)
205
203206
204207 @compiler.compiles(BooleanToIntColumn, 'sqlite')
205208 def _integer_to_boolean_sqlite(element, compiler, **kw):
252255 def get_conflict_object(session, obj, data, workspace=None):
253256 unique_fields_gen = get_unique_fields(session, obj)
254257 for unique_fields in unique_fields_gen:
255 relations_fields = filter(
258 relations_fields = list(filter(
256259 lambda unique_field: unique_field.endswith('_id'),
257 unique_fields)
258 unique_fields = filter(
260 unique_fields))
261 unique_fields = list(filter(
259262 lambda unique_field: not unique_field.endswith('_id'),
260 unique_fields)
263 unique_fields))
261264
262265 if get_object_type_for(obj) == 'vulnerability':
263266 # This is a special key due to model inheritance
264 from faraday.server.models import VulnerabilityGeneric
267 from faraday.server.models import VulnerabilityGeneric # pylint:disable=import-outside-toplevel
265268 klass = VulnerabilityGeneric
266269 else:
267270 klass = obj.__class__
308311
309312
310313 def is_unique_constraint_violation(exception):
311 from faraday.server.models import db
314 from faraday.server.models import db # pylint:disable=import-outside-toplevel
312315 if db.engine.dialect.name != 'postgresql':
313316 # Not implemened for RDMS other than postgres, we can live without
314317 # this since it is just an extra check
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3
43 import time
54 import cProfile
6 import StringIO
75 import pstats
86 import contextlib
7 from io import StringIO
8
99 import faraday.server.utils.logger
1010
1111 debug_logger = faraday.server.utils.logger.get_logger(__name__)
1212
13 class Timer(object):
13 class Timer:
1414 def __init__(self, tag, logger=None):
1515 self.__tag = tag
1616 self.__logger = debug_logger if logger is None else logger
3333 pr.enable()
3434 yield
3535 pr.disable()
36 s = StringIO.StringIO()
36 s = StringIO()
3737 ps = pstats.Stats(pr, stream=s).sort_stats('cumulative')
3838 ps.print_stats()
3939 # uncomment this to see who's calling what
4040 # ps.print_callers()
4141 debug_logger.debug(s.getvalue())
4242
43 # I'm Py3
0 import re
01 import csv
1 from StringIO import StringIO
2 from io import BytesIO
3 import re
2 from io import StringIO, BytesIO
43 import logging
54
65 from faraday.server.models import (
3635 vuln_service = " - ".join(service_fields_values)
3736 else:
3837 vuln_service = ""
39 if all(isinstance(hostname, (str, unicode)) for hostname in vuln['hostnames']):
38 if all(isinstance(hostname, str) for hostname in vuln['hostnames']):
4039 vuln_hostnames = vuln['hostnames']
4140 else:
4241 vuln_hostnames = [str(hostname['name']) for hostname in vuln['hostnames']]
7776 for field_name, value in vuln['custom_fields'].items():
7877 if field_name in custom_fields_columns:
7978 vuln_dict.update({field_name: value})
80 res = {}
81 for key, value in vuln_dict.items():
82 if isinstance(value, (str, unicode)):
83 res[key] = value.encode('utf8')
84 else:
85 res[key] = value
86 writer.writerow(res)
79 writer.writerow(vuln_dict)
8780 memory_file = BytesIO()
88 memory_file.write(buffer.getvalue())
81 memory_file.write(buffer.getvalue().encode('utf8'))
8982 memory_file.seek(0)
9083 return memory_file
9184
92
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 from builtins import chr #In py3 this is unicode!
7
68 import re
79 import sys
10 import binascii
11
812
913 def clean_dict(d):
1014 if not isinstance(d, dict):
1115 return d
1216 else:
1317 new_dict = dict()
14 for key, value in d.iteritems():
15 if isinstance(value, basestring):
18 for key, value in d.items():
19 if isinstance(value, str):
1620 new_dict[key] = clean_string(value)
1721 elif isinstance(value, dict):
1822 new_dict[key] = clean_dict(value)
2933 else:
3034 new_list = list()
3135 for item in l:
32 if isinstance(item, basestring):
36 if isinstance(item, str):
3337 new_list.append(clean_string(item))
3438 elif isinstance(item, dict):
3539 new_list.append(clean_dict(item))
8589 (0xDFFFE, 0xDFFFF), (0xEFFFE, 0xEFFFF), (0xFFFFE, 0xFFFFF),
8690 (0x10FFFE, 0x10FFFF) ]
8791
88 illegal_ranges = ["%s-%s" % (unichr(low), unichr(high))
92 illegal_ranges = ["%s-%s" % (chr(low), chr(high))
8993 for (low, high) in illegal_unichrs
9094 if low < sys.maxunicode]
9195
9498 # ret = hex(ord(c))
9599 # ret = binascii.b2a_uu(c)
96100 # ret_final = ret[1:-1]
97 ret = '\\x'+c.encode('hex')
101 ret = '\\x'+binascii.hexlify(c)
98102 return ret
99103 else:
100104 return c
105
106
107 def remove_null_caracters(string):
108 string = string.replace('\x00', '')
109 string = string.replace('\00', '')
110 string = string.replace('\0', '')
111 return string
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3
43 import os
54 import logging
65 import logging.handlers
6362 for non-class loggings."""
6463 if obj is None:
6564 logger = logging.getLogger(ROOT_LOGGER)
66 elif isinstance(obj, basestring):
65 elif isinstance(obj, str):
6766 if obj != ROOT_LOGGER:
6867 logger = logging.getLogger(u'{}.{}'.format(ROOT_LOGGER, obj))
6968 else:
9089 setup_logging()
9190
9291
92 # I'm Py3
0 import json
1
2
3 class BytesJSONEncoder(json.JSONEncoder):
4 def default(self, obj):
5 if isinstance(obj, (bytes, bytearray)):
6 return obj.decode("ASCII")
7 return json.JSONEncoder.default(self, obj)
8
9 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3
43 import gzip
54 import functools
65 import requests
7 from cStringIO import StringIO as IO
6 from io import BytesIO as IO
87
98 from flask import after_this_request, request, abort, jsonify
109
105104 res = False
106105 if not res:
107106 abort(401)
107 # I'm Py3
00 # Faraday Penetration Test IDE
11 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
22 # See the file 'doc/LICENSE' for the license information
3
43 import os
54 import sys
65 import functools
1817 from autobahn.twisted.websocket import (
1918 listenWS
2019 )
20
21 from OpenSSL.SSL import Error as SSLError
22
2123 import faraday.server.config
2224
2325 from faraday.config.constant import CONST_FARADAY_HOME_PATH
2426 from faraday.server import TimerClass
2527 from faraday.server.utils import logger
26
28 from faraday.server.threads.reports_processor import ReportsManager, REPORTS_QUEUE
2729 from faraday.server.app import create_app
2830 from faraday.server.websocket_factories import (
2931 WorkspaceServerFactory,
3032 BroadcastServerProtocol
3133 )
32 from faraday.server.api.modules.upload_reports import RawReportProcessor
34
3335
3436 app = create_app() # creates a Flask(__name__) app
3537 logger = logging.getLogger(__name__)
3638
3739
38
39 class CleanHttpHeadersResource(Resource, object):
40 class CleanHttpHeadersResource(Resource):
4041 def render(self, request):
4142 request.responseHeaders.removeHeader('Server')
4243 return super(CleanHttpHeadersResource, self).render(request)
5556 return ret
5657
5758
58 class FaradayWSGIResource(WSGIResource, object):
59 class FaradayWSGIResource(WSGIResource):
5960 def render(self, request):
6061 request.responseHeaders.removeHeader('Server')
6162 return super(FaradayWSGIResource, self).render(request)
6263
6364
64 class FaradayRedirectResource(Redirect, object):
65 class FaradayRedirectResource(Redirect):
6566 def render(self, request):
6667 request.responseHeaders.removeHeader('Server')
6768 return super(FaradayRedirectResource, self).render(request)
6869
6970
70 class WebServer(object):
71 UI_URL_PATH = '_ui'
72 API_URL_PATH = '_api'
71 class WebServer:
72 UI_URL_PATH = b'_ui'
73 API_URL_PATH = b'_api'
7374 WEB_UI_LOCAL_PATH = os.path.join(faraday.server.config.FARADAY_BASE, 'server/www')
7475
7576 def __init__(self, enable_ssl=False):
9394 certs = (faraday.server.config.ssl.keyfile, faraday.server.config.ssl.certificate)
9495 if not all(certs):
9596 logger.critical("HTTPS request but SSL certificates are not configured")
96 exit(1) # Abort web-server startup
97 sys.exit(1) # Abort web-server startup
9798 return ssl.DefaultOpenSSLContextFactory(*certs)
9899
99100 def __build_server_tree(self):
104105 WebServer.API_URL_PATH, self.__build_api_resource())
105106
106107 def __build_web_redirect(self):
107 return FaradayRedirectResource('/')
108 return FaradayRedirectResource(b'/')
108109
109110 def __build_web_resource(self):
110111 return FileWithoutDirectoryListing(WebServer.WEB_UI_LOCAL_PATH)
140141 logger.info('Received SIGTERM, shutting down.')
141142 logger.info("Stopping threads, please wait...")
142143 # teardown()
143 self.raw_report_processor.stop()
144 if self.raw_report_processor.isAlive():
145 self.raw_report_processor.stop()
144146 self.timer.stop()
145147
146148 log_path = os.path.join(CONST_FARADAY_HOME_PATH, 'logs', 'access-logging.log')
159161 try:
160162 self.install_signal()
161163 # start threads and processes
162 self.raw_report_processor = RawReportProcessor()
164 self.raw_report_processor = ReportsManager(REPORTS_QUEUE, name="ReportsManager-Thread", daemon=True)
163165 self.raw_report_processor.start()
164166 self.timer = TimerClass()
165167 self.timer.start()
169171 interface=self.__bind_address)
170172 # websockets
171173 if faraday.server.config.websocket_ssl.enabled:
172 contextFactory = ssl.DefaultOpenSSLContextFactory(
173 faraday.server.config.websocket_ssl.keyfile.strip('\''),
174 faraday.server.config.websocket_ssl.certificate.strip('\'')
175 )
174
176175 try:
176 contextFactory = ssl.DefaultOpenSSLContextFactory(
177 faraday.server.config.websocket_ssl.keyfile.strip('\''),
178 faraday.server.config.websocket_ssl.certificate.strip('\'')
179 )
180
177181 listenWS(self.__build_websockets_resource(), interface=self.__bind_address, contextFactory=contextFactory)
182
183 except SSLError as e:
184 logger.error('Could not start websockets due to a SSL Config error. Some web functionality will not be available')
178185 except error.CannotListenError:
179186 logger.warn('Could not start websockets, address already open. This is ok is you wan to run multiple instances.')
180187 except Exception as ex:
191198 reactor.run()
192199
193200 except error.CannotListenError as e:
194 logger.error(str(e))
201 logger.error(e)
195202 sys.exit(1)
203
204
196205 except Exception as e:
197206 logger.error('Something went wrong when trying to setup the Web UI')
198207 logger.exception(e)
199208 sys.exit(1)
209 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 import json
77 import logging
88 import itsdangerous
99
10 import Cookie
10 import http.cookies
1111 from collections import defaultdict
12 from Queue import Empty
12 from queue import Empty
1313
1414 import txaio
1515
16 from faraday.server.utils.database import get_or_create
1617
1718 txaio.use_twisted()
1819
2425 WebSocketServerProtocol
2526 )
2627
27 from faraday.server.models import Workspace, Agent
28 from faraday.server.models import Workspace, Agent, Executor, db, AgentExecution
2829 from faraday.server.api.modules.websocket_auth import decode_agent_websocket_token
2930 from faraday.server.events import changes_queue
3031
4344 logger.debug('Websocket request {0}'.format(request))
4445 if 'cookie' in request.headers:
4546 try:
46 cookie = Cookie.SimpleCookie()
47 cookie = http.cookies.SimpleCookie()
4748 cookie.load(str(request.headers['cookie']))
48 except Cookie.CookieError:
49 except http.cookies.CookieError:
4950 pass
5051 return (protocol, headers)
5152
53 # {
54 # "action": "RUN_STATUS",
55 # "running": true,
56 # "message": "todo bien guachin"
57 # }
58
5259 def onMessage(self, payload, is_binary):
53 from faraday.server.web import app
60 from faraday.server.web import app # pylint:disable=import-outside-toplevel
5461 """
5562 We only support JOIN and LEAVE workspace messages.
5663 When authentication is implemented we need to verify
9198 if message['action'] == 'LEAVE_WORKSPACE':
9299 self.factory.leave_workspace(self, message['workspace'])
93100 if message['action'] == 'JOIN_AGENT':
94 if 'token' not in message:
101 if 'token' not in message or 'executors' not in message:
95102 logger.warn("Invalid agent join message")
96103 self.state = WebSocketProtocol.STATE_CLOSING
97104 self.sendClose()
99106 with app.app_context():
100107 try:
101108 agent = decode_agent_websocket_token(message['token'])
109 update_executors(agent, message['executors'])
102110 except ValueError:
103111 logger.warn('Invalid agent token!')
104112 self.state = WebSocketProtocol.STATE_CLOSING
105113 self.sendClose()
106114 return False
107 # factory will now send broadcast messages to the agent
108 return self.factory.join_agent(self, agent)
115 # factory will now send broadcast messages to the agent
116 return self.factory.join_agent(self, agent)
109117 if message['action'] == 'LEAVE_AGENT':
110118 with app.app_context():
111119 (agent_id,) = [
119127 self.state = WebSocketProtocol.STATE_CLOSING
120128 self.sendClose()
121129 return False
122
130 if message['action'] == 'RUN_STATUS':
131 with app.app_context():
132 if 'executor_name' not in message:
133 logger.warning('Missing executor_name param in message: ''{}'.format(message))
134 self.sendClose()
135 return
136
137 (agent_id,) = [
138 k
139 for (k, v) in connected_agents.items()
140 if v == self
141 ]
142 agent = Agent.query.get(agent_id)
143 assert agent is not None # TODO the agent could be deleted here
144 executor = Executor.query.filter(Executor.name == message['executor_name'],
145 Executor.agent_id == agent_id).first()
146 if executor:
147 successful = message.get('successful', None)
148 running = message.get('running', None)
149 msg = message['message']
150 agent_execution = AgentExecution(
151 running=running,
152 successful=successful,
153 message=msg,
154 executor=executor,
155 workspace_id=executor.agent.workspace_id
156 )
157 db.session.add(agent_execution)
158 db.session.commit()
123159
124160 def connectionLost(self, reason):
125161 WebSocketServerProtocol.connectionLost(self, reason)
128164
129165 def sendServerStatus(self, redirectUrl=None, redirectAfter=0):
130166 self.sendHtml('This is a websocket port.')
167
168
169 def update_executors(agent, executors):
170 incoming_executor_names = set()
171 for raw_executor in executors:
172 if 'executor_name' not in raw_executor or 'args' not in raw_executor:
173 continue
174 executor, created = get_or_create(
175 db.session,
176 Executor,
177 **{
178 'name': raw_executor['executor_name'],
179 'agent': agent,
180 }
181 )
182
183 executor.parameters_metadata = raw_executor['args']
184 db.session.add(executor)
185 db.session.commit()
186 incoming_executor_names.add(raw_executor['executor_name'])
187
188 current_executors = Executor.query.filter(Executor.agent == agent)
189 for current_executor in current_executors:
190 if current_executor.name not in incoming_executor_names:
191 db.session.delete(current_executor)
192 db.session.commit()
193
194 return True
131195
132196
133197 class WorkspaceServerFactory(WebSocketServerFactory):
196260 return
197261
198262 def unregister_agent(self, protocol):
199 for (key, value) in connected_agents.items():
263 for (key, value) in connected_agents.copy().items():
200264 if value == protocol:
201265 del connected_agents[key]
266 logger.info("Agent {} disconnected!".format(key))
202267
203268 def broadcast(self, msg):
269 if isinstance(msg, str):
270 msg = msg.encode('utf-8')
204271 logger.debug("broadcasting prepared message '{}' ..".format(msg))
205272 prepared_msg = json.loads(self.prepareMessage(msg).payload)
206 if 'agent_id' not in msg:
273 if b'agent_id' not in msg:
207274 for client in self.workspace_clients[prepared_msg['workspace']]:
208275 reactor.callFromThread(client.sendPreparedMessage, self.prepareMessage(msg))
209276 logger.debug("prepared message sent to {}".format(client.peer))
210277
211 if 'agent_id' in msg:
278 if b'agent_id' in msg:
212279 agent_id = prepared_msg['agent_id']
213280 try:
214281 agent_connection = connected_agents[agent_id]
314314 .tab-pane-container {
315315 width: 100%;
316316 overflow-y: auto;
317 max-height: calc(100% - 180px);
317 max-height: calc(100% - 175px);
318 height: 100%;
318319 }
319320
320321 .btn-primary-white {
665666 background-color: #a1ce31;
666667 }
667668
669 .type-choice{
670 border: 1px solid #be2743;
671 background-color: #be2743;
672 }
673
668674 h3.cf-display-name {
669675 margin-top: 0;
670676 }
679685 color: #aaa;
680686 text-align: center;
681687 padding-top: 32px;
688 }
689
690 .btn-cf-choice {
691 margin-bottom: 30px;
692 padding-left: 0;
693 padding-right: 0;
682694 }
683695
684696
55 .clearfix{display:inline-block}html[xmlns] .clearfix{display:block}* html .clearfix{height:1%}
66 /**/
77 html#no-overflow{overflow-x:hidden;}
8 html{
9 zoom: 0.8;
8 .login {
9 background: #3a3a3a !important;
10 position: fixed;
11 }
12 html {
13 height: 100%;
14 width: 100%;
1015 }
1116 body {
1217 font-family: "Open Sans" !important;
187192
188193 .left-main {
189194 position: absolute;
195 width: 100%;
190196 top: 0px;
191197 border-top: 3px solid #464646;
192198 right: 0;
563569 width: 60%;
564570 }
565571
572 #buttonAtRight {
573 right: 0;
574 }
566575 /* Table Status Report */
567576 table.status-report {
568577 /* background-color: #CDCDCD; */
13921401 .change-padding-to-margin {
13931402 padding-bottom: 0px !important;
13941403 margin-bottom: 15px;
1395 }
1404 }
7575 <script type="text/javascript" src="script/ui-bootstrap-tpls-0.14.1.min.js"></script>
7676 <script type="text/javascript" src="script/cryptojs-sha1.js"></script>
7777 <script type="text/javascript" src="script/sanitize.js"></script>
78 <script type="text/javascript" src="script/showdown.js"></script>
79 <script type="text/javascript" src="script/showdown-table.js"></script>
7880 <script type="text/javascript" src="script/angular-ui-notification.min.js"></script>
7981 <script type="text/javascript" src="script/angular-ui.js"></script>
8082 <script type="text/javascript" src="script/angular-clipboard.js"></script>
119121 <script type="text/javascript" src="scripts/commons/filters/getByProperty.js"></script>
120122 <script type="text/javascript" src="scripts/commons/filters/orderObjectBy.js"></script>
121123 <script type="text/javascript" src="scripts/commons/filters/startFrom.js"></script>
124 <script type="text/javascript" src="scripts/commons/filters/markdown.js"></script>
122125 <script type="text/javascript" src="scripts/commons/filters/integer.js"></script>
123126 <script type="text/javascript" src="scripts/commons/filters/removeLinebreaks.js"></script>
124127 <script type="text/javascript" src="scripts/config/services/config.js"></script>
0 /*! showdown-table 17-06-2015 */
1 /*
2 * Basic table support with re-entrant parsing, where cell content
3 * can also specify markdown.
4 *
5 * Tables
6 * ======
7 *
8 * | Col 1 | Col 2 |
9 * |======== |====================================================|
10 * |**bold** | ![Valid XHTML] (http://w3.org/Icons/valid-xhtml10) |
11 * | Plain | Value |
12 *
13 */
14
15 (function () {
16 'use strict';
17
18 var table = function (converter) {
19
20 var tables = {}, style = 'text-align:left;', filter;
21 tables.th = function (header) {
22 if (header.trim() === '') {
23 return '';
24 }
25 var id = header.trim().replace(/ /g, '_').toLowerCase();
26 return '<th id="' + id + '" style="' + style + '">' + header + '</th>';
27 };
28 tables.td = function (cell) {
29 return '<td style="' + style + '">' + converter.makeHtml(cell) + '</td>';
30 };
31 tables.ths = function () {
32 var out = '',
33 i = 0,
34 hs = [].slice.apply(arguments);
35 for (i; i < hs.length; i += 1) {
36 out += tables.th(hs[i]) + '\n';
37 }
38 return out;
39 };
40 tables.tds = function () {
41 var out = '', i = 0, ds = [].slice.apply(arguments);
42 for (i; i < ds.length; i += 1) {
43 out += tables.td(ds[i]) + '\n';
44 }
45 return out;
46 };
47 tables.thead = function () {
48 var out,
49 hs = [].slice.apply(arguments);
50 out = '<thead class="thead-dark">\n';
51 out += '<tr>\n';
52 out += tables.ths.apply(this, hs);
53 out += '</tr>\n';
54 out += '</thead>\n';
55 return out;
56 };
57 tables.tr = function () {
58 var out,
59 cs = [].slice.apply(arguments);
60 out = '<tr>\n';
61 out += tables.tds.apply(this, cs);
62 out += '</tr>\n';
63 return out;
64 };
65 filter = function (text) {
66 var i = 0, lines = text.split('\n'), line, hs, out = [];
67 for (i; i < lines.length; i += 1) {
68 line = lines[i];
69 if (line.trim().match(/^[|].*[|]$/)) {
70 line = line.trim();
71 var tbl = [];
72 tbl.push('<table class="table table-sm">');
73 hs = line.substring(1, line.length - 1).split('|');
74 tbl.push(tables.thead.apply(this, hs));
75 line = lines[++i];
76 if (!line.trim().match(/^[|][-=|: ]+[|]$/)) {
77 line = lines[--i];
78 } else {
79 line = lines[++i];
80 tbl.push('<tbody>');
81 while (line.trim().match(/^[|].*[|]$/)) {
82 line = line.trim();
83 tbl.push(tables.tr.apply(this, line.substring(1, line.length - 1).split('|')));
84 line = lines[++i];
85 }
86 tbl.push('</tbody>');
87 tbl.push('</table>');
88 out.push(tbl.join('\n'));
89 continue;
90 }
91 }
92 out.push(line);
93 }
94 return out.join('\n');
95 };
96 return [
97 {
98 type: 'lang',
99 filter: filter
100 }
101 ];
102 };
103 if (typeof window !== 'undefined' && window.showdown && window.showdown.extensions) {
104 window.showdown.extensions.table = table;
105 }
106 if (typeof module !== 'undefined') {
107 module.exports = table;
108 }
109 }());
110
111 //# sourceMappingURL=showdown-table.js.map
0 ;/*! showdown v 2.0.0-alpha1 - 24-10-2018 */
1 (function(){
2 /**
3 * Created by Tivie on 13-07-2015.
4 */
5
6 function getDefaultOpts (simple) {
7 'use strict';
8
9 var defaultOptions = {
10 omitExtraWLInCodeBlocks: {
11 defaultValue: false,
12 describe: 'Omit the default extra whiteline added to code blocks',
13 type: 'boolean'
14 },
15 noHeaderId: {
16 defaultValue: false,
17 describe: 'Turn on/off generated header id',
18 type: 'boolean'
19 },
20 prefixHeaderId: {
21 defaultValue: false,
22 describe: 'Add a prefix to the generated header ids. Passing a string will prefix that string to the header id. Setting to true will add a generic \'section-\' prefix',
23 type: 'string'
24 },
25 rawPrefixHeaderId: {
26 defaultValue: false,
27 describe: 'Setting this option to true will prevent showdown from modifying the prefix. This might result in malformed IDs (if, for instance, the " char is used in the prefix)',
28 type: 'boolean'
29 },
30 ghCompatibleHeaderId: {
31 defaultValue: false,
32 describe: 'Generate header ids compatible with github style (spaces are replaced with dashes, a bunch of non alphanumeric chars are removed)',
33 type: 'boolean'
34 },
35 rawHeaderId: {
36 defaultValue: false,
37 describe: 'Remove only spaces, \' and " from generated header ids (including prefixes), replacing them with dashes (-). WARNING: This might result in malformed ids',
38 type: 'boolean'
39 },
40 headerLevelStart: {
41 defaultValue: false,
42 describe: 'The header blocks level start',
43 type: 'integer'
44 },
45 parseImgDimensions: {
46 defaultValue: false,
47 describe: 'Turn on/off image dimension parsing',
48 type: 'boolean'
49 },
50 simplifiedAutoLink: {
51 defaultValue: false,
52 describe: 'Turn on/off GFM autolink style',
53 type: 'boolean'
54 },
55 literalMidWordUnderscores: {
56 defaultValue: false,
57 describe: 'Parse midword underscores as literal underscores',
58 type: 'boolean'
59 },
60 literalMidWordAsterisks: {
61 defaultValue: false,
62 describe: 'Parse midword asterisks as literal asterisks',
63 type: 'boolean'
64 },
65 strikethrough: {
66 defaultValue: false,
67 describe: 'Turn on/off strikethrough support',
68 type: 'boolean'
69 },
70 tables: {
71 defaultValue: false,
72 describe: 'Turn on/off tables support',
73 type: 'boolean'
74 },
75 tablesHeaderId: {
76 defaultValue: false,
77 describe: 'Add an id to table headers',
78 type: 'boolean'
79 },
80 ghCodeBlocks: {
81 defaultValue: true,
82 describe: 'Turn on/off GFM fenced code blocks support',
83 type: 'boolean'
84 },
85 tasklists: {
86 defaultValue: false,
87 describe: 'Turn on/off GFM tasklist support',
88 type: 'boolean'
89 },
90 smoothLivePreview: {
91 defaultValue: false,
92 describe: 'Prevents weird effects in live previews due to incomplete input',
93 type: 'boolean'
94 },
95 smartIndentationFix: {
96 defaultValue: false,
97 description: 'Tries to smartly fix indentation in es6 strings',
98 type: 'boolean'
99 },
100 disableForced4SpacesIndentedSublists: {
101 defaultValue: false,
102 description: 'Disables the requirement of indenting nested sublists by 4 spaces',
103 type: 'boolean'
104 },
105 simpleLineBreaks: {
106 defaultValue: false,
107 description: 'Parses simple line breaks as <br> (GFM Style)',
108 type: 'boolean'
109 },
110 requireSpaceBeforeHeadingText: {
111 defaultValue: false,
112 description: 'Makes adding a space between `#` and the header text mandatory (GFM Style)',
113 type: 'boolean'
114 },
115 ghMentions: {
116 defaultValue: false,
117 description: 'Enables github @mentions',
118 type: 'boolean'
119 },
120 ghMentionsLink: {
121 defaultValue: 'https://github.com/{u}',
122 description: 'Changes the link generated by @mentions. Only applies if ghMentions option is enabled.',
123 type: 'string'
124 },
125 encodeEmails: {
126 defaultValue: true,
127 description: 'Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities',
128 type: 'boolean'
129 },
130 openLinksInNewWindow: {
131 defaultValue: false,
132 description: 'Open all links in new windows',
133 type: 'boolean'
134 },
135 backslashEscapesHTMLTags: {
136 defaultValue: false,
137 description: 'Support for HTML Tag escaping. ex: \<div>foo\</div>',
138 type: 'boolean'
139 },
140 emoji: {
141 defaultValue: false,
142 description: 'Enable emoji support. Ex: `this is a :smile: emoji`',
143 type: 'boolean'
144 },
145 underline: {
146 defaultValue: false,
147 description: 'Enable support for underline. Syntax is double or triple underscores: `__underline word__`. With this option enabled, underscores no longer parses into `<em>` and `<strong>`',
148 type: 'boolean'
149 },
150 completeHTMLDocument: {
151 defaultValue: false,
152 description: 'Outputs a complete html document, including `<html>`, `<head>` and `<body>` tags',
153 type: 'boolean'
154 },
155 metadata: {
156 defaultValue: false,
157 description: 'Enable support for document metadata (defined at the top of the document between `«««` and `»»»` or between `---` and `---`).',
158 type: 'boolean'
159 },
160 splitAdjacentBlockquotes: {
161 defaultValue: false,
162 description: 'Split adjacent blockquote blocks',
163 type: 'boolean'
164 }
165 };
166 if (simple === false) {
167 return JSON.parse(JSON.stringify(defaultOptions));
168 }
169 var ret = {};
170 for (var opt in defaultOptions) {
171 if (defaultOptions.hasOwnProperty(opt)) {
172 ret[opt] = defaultOptions[opt].defaultValue;
173 }
174 }
175 return ret;
176 }
177
178 function allOptionsOn () {
179 'use strict';
180 var options = getDefaultOpts(true),
181 ret = {};
182 for (var opt in options) {
183 if (options.hasOwnProperty(opt)) {
184 ret[opt] = true;
185 }
186 }
187 return ret;
188 }
189
190 /**
191 * Created by Tivie on 06-01-2015.
192 */
193 // Private properties
194 var showdown = {},
195 parsers = {},
196 extensions = {},
197 globalOptions = getDefaultOpts(true),
198 setFlavor = 'vanilla',
199 flavor = {
200 github: {
201 omitExtraWLInCodeBlocks: true,
202 simplifiedAutoLink: true,
203 literalMidWordUnderscores: true,
204 strikethrough: true,
205 tables: true,
206 tablesHeaderId: true,
207 ghCodeBlocks: true,
208 tasklists: true,
209 disableForced4SpacesIndentedSublists: true,
210 simpleLineBreaks: true,
211 requireSpaceBeforeHeadingText: true,
212 ghCompatibleHeaderId: true,
213 ghMentions: true,
214 backslashEscapesHTMLTags: true,
215 emoji: true,
216 splitAdjacentBlockquotes: true
217 },
218 original: {
219 noHeaderId: true,
220 ghCodeBlocks: false
221 },
222 ghost: {
223 omitExtraWLInCodeBlocks: true,
224 parseImgDimensions: true,
225 simplifiedAutoLink: true,
226 literalMidWordUnderscores: true,
227 strikethrough: true,
228 tables: true,
229 tablesHeaderId: true,
230 ghCodeBlocks: true,
231 tasklists: true,
232 smoothLivePreview: true,
233 simpleLineBreaks: true,
234 requireSpaceBeforeHeadingText: true,
235 ghMentions: false,
236 encodeEmails: true
237 },
238 vanilla: getDefaultOpts(true),
239 allOn: allOptionsOn()
240 };
241
242 /**
243 * helper namespace
244 * @type {{}}
245 */
246 showdown.helper = {};
247
248 /**
249 * TODO LEGACY SUPPORT CODE
250 * @type {{}}
251 */
252 showdown.extensions = {};
253
254 /**
255 * Set a global option
256 * @static
257 * @param {string} key
258 * @param {*} value
259 * @returns {showdown}
260 */
261 showdown.setOption = function (key, value) {
262 'use strict';
263 globalOptions[key] = value;
264 return this;
265 };
266
267 /**
268 * Get a global option
269 * @static
270 * @param {string} key
271 * @returns {*}
272 */
273 showdown.getOption = function (key) {
274 'use strict';
275 return globalOptions[key];
276 };
277
278 /**
279 * Get the global options
280 * @static
281 * @returns {{}}
282 */
283 showdown.getOptions = function () {
284 'use strict';
285 return globalOptions;
286 };
287
288 /**
289 * Reset global options to the default values
290 * @static
291 */
292 showdown.resetOptions = function () {
293 'use strict';
294 globalOptions = getDefaultOpts(true);
295 };
296
297 /**
298 * Set the flavor showdown should use as default
299 * @param {string} name
300 */
301 showdown.setFlavor = function (name) {
302 'use strict';
303 if (!flavor.hasOwnProperty(name)) {
304 throw Error(name + ' flavor was not found');
305 }
306 showdown.resetOptions();
307 var preset = flavor[name];
308 setFlavor = name;
309 for (var option in preset) {
310 if (preset.hasOwnProperty(option)) {
311 globalOptions[option] = preset[option];
312 }
313 }
314 };
315
316 /**
317 * Get the currently set flavor
318 * @returns {string}
319 */
320 showdown.getFlavor = function () {
321 'use strict';
322 return setFlavor;
323 };
324
325 /**
326 * Get the options of a specified flavor. Returns undefined if the flavor was not found
327 * @param {string} name Name of the flavor
328 * @returns {{}|undefined}
329 */
330 showdown.getFlavorOptions = function (name) {
331 'use strict';
332 if (flavor.hasOwnProperty(name)) {
333 return flavor[name];
334 }
335 };
336
337 /**
338 * Get the default options
339 * @static
340 * @param {boolean} [simple=true]
341 * @returns {{}}
342 */
343 showdown.getDefaultOptions = function (simple) {
344 'use strict';
345 return getDefaultOpts(simple);
346 };
347
348 /**
349 * Get or set a subParser
350 *
351 * subParser(name) - Get a registered subParser
352 * subParser(name, func) - Register a subParser
353 * @static
354 * @param {string} name
355 * @param {function} [func]
356 * @returns {*}
357 */
358 showdown.subParser = function (name, func) {
359 'use strict';
360 if (showdown.helper.isString(name)) {
361 if (typeof func !== 'undefined') {
362 parsers[name] = func;
363 } else {
364 if (parsers.hasOwnProperty(name)) {
365 return parsers[name];
366 } else {
367 throw Error('SubParser named ' + name + ' not registered!');
368 }
369 }
370 } else {
371 throw Error('showdown.subParser function first argument must be a string (the name of the subparser)');
372 }
373 };
374
375 /**
376 * Gets or registers an extension
377 * @static
378 * @param {string} name
379 * @param {object|function=} ext
380 * @returns {*}
381 */
382 showdown.extension = function (name, ext) {
383 'use strict';
384
385 if (!showdown.helper.isString(name)) {
386 throw Error('Extension \'name\' must be a string');
387 }
388
389 name = showdown.helper.stdExtName(name);
390
391 // Getter
392 if (showdown.helper.isUndefined(ext)) {
393 if (!extensions.hasOwnProperty(name)) {
394 throw Error('Extension named ' + name + ' is not registered!');
395 }
396 return extensions[name];
397
398 // Setter
399 } else {
400 // Expand extension if it's wrapped in a function
401 if (typeof ext === 'function') {
402 ext = ext();
403 }
404
405 // Ensure extension is an array
406 if (!showdown.helper.isArray(ext)) {
407 ext = [ext];
408 }
409
410 var validExtension = validate(ext, name);
411
412 if (validExtension.valid) {
413 extensions[name] = ext;
414 } else {
415 throw Error(validExtension.error);
416 }
417 }
418 };
419
420 /**
421 * Gets all extensions registered
422 * @returns {{}}
423 */
424 showdown.getAllExtensions = function () {
425 'use strict';
426 return extensions;
427 };
428
429 /**
430 * Remove an extension
431 * @param {string} name
432 */
433 showdown.removeExtension = function (name) {
434 'use strict';
435 delete extensions[name];
436 };
437
438 /**
439 * Removes all extensions
440 */
441 showdown.resetExtensions = function () {
442 'use strict';
443 extensions = {};
444 };
445
446 /**
447 * Validate extension
448 * @param {array} extension
449 * @param {string} name
450 * @returns {{valid: boolean, error: string}}
451 */
452 function validate (extension, name) {
453 'use strict';
454
455 var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension',
456 ret = {
457 valid: true,
458 error: ''
459 };
460
461 if (!showdown.helper.isArray(extension)) {
462 extension = [extension];
463 }
464
465 for (var i = 0; i < extension.length; ++i) {
466 var baseMsg = errMsg + ' sub-extension ' + i + ': ',
467 ext = extension[i];
468 if (typeof ext !== 'object') {
469 ret.valid = false;
470 ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given';
471 return ret;
472 }
473
474 if (!showdown.helper.isString(ext.type)) {
475 ret.valid = false;
476 ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given';
477 return ret;
478 }
479
480 var type = ext.type = ext.type.toLowerCase();
481
482 // normalize extension type
483 if (type === 'language') {
484 type = ext.type = 'lang';
485 }
486
487 if (type === 'html') {
488 type = ext.type = 'output';
489 }
490
491 if (type !== 'lang' && type !== 'output' && type !== 'listener') {
492 ret.valid = false;
493 ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang/language", "output/html" or "listener"';
494 return ret;
495 }
496
497 if (type === 'listener') {
498 if (showdown.helper.isUndefined(ext.listeners)) {
499 ret.valid = false;
500 ret.error = baseMsg + '. Extensions of type "listener" must have a property called "listeners"';
501 return ret;
502 }
503 } else {
504 if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) {
505 ret.valid = false;
506 ret.error = baseMsg + type + ' extensions must define either a "regex" property or a "filter" method';
507 return ret;
508 }
509 }
510
511 if (ext.listeners) {
512 if (typeof ext.listeners !== 'object') {
513 ret.valid = false;
514 ret.error = baseMsg + '"listeners" property must be an object but ' + typeof ext.listeners + ' given';
515 return ret;
516 }
517 for (var ln in ext.listeners) {
518 if (ext.listeners.hasOwnProperty(ln)) {
519 if (typeof ext.listeners[ln] !== 'function') {
520 ret.valid = false;
521 ret.error = baseMsg + '"listeners" property must be an hash of [event name]: [callback]. listeners.' + ln +
522 ' must be a function but ' + typeof ext.listeners[ln] + ' given';
523 return ret;
524 }
525 }
526 }
527 }
528
529 if (ext.filter) {
530 if (typeof ext.filter !== 'function') {
531 ret.valid = false;
532 ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given';
533 return ret;
534 }
535 } else if (ext.regex) {
536 if (showdown.helper.isString(ext.regex)) {
537 ext.regex = new RegExp(ext.regex, 'g');
538 }
539 if (!(ext.regex instanceof RegExp)) {
540 ret.valid = false;
541 ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' + typeof ext.regex + ' given';
542 return ret;
543 }
544 if (showdown.helper.isUndefined(ext.replace)) {
545 ret.valid = false;
546 ret.error = baseMsg + '"regex" extensions must implement a replace string or function';
547 return ret;
548 }
549 }
550 }
551 return ret;
552 }
553
554 /**
555 * Validate extension
556 * @param {object} ext
557 * @returns {boolean}
558 */
559 showdown.validateExtension = function (ext) {
560 'use strict';
561
562 var validateExtension = validate(ext, null);
563 if (!validateExtension.valid) {
564 console.warn(validateExtension.error);
565 return false;
566 }
567 return true;
568 };
569
570 /**
571 * showdownjs helper functions
572 */
573
574 if (!showdown.hasOwnProperty('helper')) {
575 showdown.helper = {};
576 }
577
578 if (typeof this.document === 'undefined' && typeof this.window === 'undefined') {
579 var jsdom = require('jsdom');
580 this.window = new jsdom.JSDOM('', {}).window; // jshint ignore:line
581 }
582 showdown.helper.document = this.window.document;
583
584 /**
585 * Check if var is string
586 * @static
587 * @param {string} a
588 * @returns {boolean}
589 */
590 showdown.helper.isString = function (a) {
591 'use strict';
592 return (typeof a === 'string' || a instanceof String);
593 };
594
595 /**
596 * Check if var is a function
597 * @static
598 * @param {*} a
599 * @returns {boolean}
600 */
601 showdown.helper.isFunction = function (a) {
602 'use strict';
603 var getType = {};
604 return a && getType.toString.call(a) === '[object Function]';
605 };
606
607 /**
608 * isArray helper function
609 * @static
610 * @param {*} a
611 * @returns {boolean}
612 */
613 showdown.helper.isArray = function (a) {
614 'use strict';
615 return Array.isArray(a);
616 };
617
618 /**
619 * Check if value is undefined
620 * @static
621 * @param {*} value The value to check.
622 * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
623 */
624 showdown.helper.isUndefined = function (value) {
625 'use strict';
626 return typeof value === 'undefined';
627 };
628
629 /**
630 * ForEach helper function
631 * Iterates over Arrays and Objects (own properties only)
632 * @static
633 * @param {*} obj
634 * @param {function} callback Accepts 3 params: 1. value, 2. key, 3. the original array/object
635 */
636 showdown.helper.forEach = function (obj, callback) {
637 'use strict';
638 // check if obj is defined
639 if (showdown.helper.isUndefined(obj)) {
640 throw new Error('obj param is required');
641 }
642
643 if (showdown.helper.isUndefined(callback)) {
644 throw new Error('callback param is required');
645 }
646
647 if (!showdown.helper.isFunction(callback)) {
648 throw new Error('callback param must be a function/closure');
649 }
650
651 if (typeof obj.forEach === 'function') {
652 obj.forEach(callback);
653 } else if (showdown.helper.isArray(obj)) {
654 for (var i = 0; i < obj.length; i++) {
655 callback(obj[i], i, obj);
656 }
657 } else if (typeof (obj) === 'object') {
658 for (var prop in obj) {
659 if (obj.hasOwnProperty(prop)) {
660 callback(obj[prop], prop, obj);
661 }
662 }
663 } else {
664 throw new Error('obj does not seem to be an array or an iterable object');
665 }
666 };
667
668 /**
669 * Standardidize extension name
670 * @static
671 * @param {string} s extension name
672 * @returns {string}
673 */
674 showdown.helper.stdExtName = function (s) {
675 'use strict';
676 return s.replace(/[_?*+\/\\.^-]/g, '').replace(/\s/g, '').toLowerCase();
677 };
678
679 function escapeCharactersCallback (wholeMatch, m1) {
680 'use strict';
681 var charCodeToEscape = m1.charCodeAt(0);
682 return '¨E' + charCodeToEscape + 'E';
683 }
684
685 /**
686 * Callback used to escape characters when passing through String.replace
687 * @static
688 * @param {string} wholeMatch
689 * @param {string} m1
690 * @returns {string}
691 */
692 showdown.helper.escapeCharactersCallback = escapeCharactersCallback;
693
694 /**
695 * Escape characters in a string
696 * @static
697 * @param {string} text
698 * @param {string} charsToEscape
699 * @param {boolean} afterBackslash
700 * @returns {XML|string|void|*}
701 */
702 showdown.helper.escapeCharacters = function (text, charsToEscape, afterBackslash) {
703 'use strict';
704 // First we have to escape the escape characters so that
705 // we can build a character class out of them
706 var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])';
707
708 if (afterBackslash) {
709 regexString = '\\\\' + regexString;
710 }
711
712 var regex = new RegExp(regexString, 'g');
713 text = text.replace(regex, escapeCharactersCallback);
714
715 return text;
716 };
717
718 var rgxFindMatchPos = function (str, left, right, flags) {
719 'use strict';
720 var f = flags || '',
721 g = f.indexOf('g') > -1,
722 x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, '')),
723 l = new RegExp(left, f.replace(/g/g, '')),
724 pos = [],
725 t, s, m, start, end;
726
727 do {
728 t = 0;
729 while ((m = x.exec(str))) {
730 if (l.test(m[0])) {
731 if (!(t++)) {
732 s = x.lastIndex;
733 start = s - m[0].length;
734 }
735 } else if (t) {
736 if (!--t) {
737 end = m.index + m[0].length;
738 var obj = {
739 left: {start: start, end: s},
740 match: {start: s, end: m.index},
741 right: {start: m.index, end: end},
742 wholeMatch: {start: start, end: end}
743 };
744 pos.push(obj);
745 if (!g) {
746 return pos;
747 }
748 }
749 }
750 }
751 } while (t && (x.lastIndex = s));
752
753 return pos;
754 };
755
756 /**
757 * matchRecursiveRegExp
758 *
759 * (c) 2007 Steven Levithan <stevenlevithan.com>
760 * MIT License
761 *
762 * Accepts a string to search, a left and right format delimiter
763 * as regex patterns, and optional regex flags. Returns an array
764 * of matches, allowing nested instances of left/right delimiters.
765 * Use the "g" flag to return all matches, otherwise only the
766 * first is returned. Be careful to ensure that the left and
767 * right format delimiters produce mutually exclusive matches.
768 * Backreferences are not supported within the right delimiter
769 * due to how it is internally combined with the left delimiter.
770 * When matching strings whose format delimiters are unbalanced
771 * to the left or right, the output is intentionally as a
772 * conventional regex library with recursion support would
773 * produce, e.g. "<<x>" and "<x>>" both produce ["x"] when using
774 * "<" and ">" as the delimiters (both strings contain a single,
775 * balanced instance of "<x>").
776 *
777 * examples:
778 * matchRecursiveRegExp("test", "\\(", "\\)")
779 * returns: []
780 * matchRecursiveRegExp("<t<<e>><s>>t<>", "<", ">", "g")
781 * returns: ["t<<e>><s>", ""]
782 * matchRecursiveRegExp("<div id=\"x\">test</div>", "<div\\b[^>]*>", "</div>", "gi")
783 * returns: ["test"]
784 */
785 showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) {
786 'use strict';
787
788 var matchPos = rgxFindMatchPos (str, left, right, flags),
789 results = [];
790
791 for (var i = 0; i < matchPos.length; ++i) {
792 results.push([
793 str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
794 str.slice(matchPos[i].match.start, matchPos[i].match.end),
795 str.slice(matchPos[i].left.start, matchPos[i].left.end),
796 str.slice(matchPos[i].right.start, matchPos[i].right.end)
797 ]);
798 }
799 return results;
800 };
801
802 /**
803 *
804 * @param {string} str
805 * @param {string|function} replacement
806 * @param {string} left
807 * @param {string} right
808 * @param {string} flags
809 * @returns {string}
810 */
811 showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) {
812 'use strict';
813
814 if (!showdown.helper.isFunction(replacement)) {
815 var repStr = replacement;
816 replacement = function () {
817 return repStr;
818 };
819 }
820
821 var matchPos = rgxFindMatchPos(str, left, right, flags),
822 finalStr = str,
823 lng = matchPos.length;
824
825 if (lng > 0) {
826 var bits = [];
827 if (matchPos[0].wholeMatch.start !== 0) {
828 bits.push(str.slice(0, matchPos[0].wholeMatch.start));
829 }
830 for (var i = 0; i < lng; ++i) {
831 bits.push(
832 replacement(
833 str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end),
834 str.slice(matchPos[i].match.start, matchPos[i].match.end),
835 str.slice(matchPos[i].left.start, matchPos[i].left.end),
836 str.slice(matchPos[i].right.start, matchPos[i].right.end)
837 )
838 );
839 if (i < lng - 1) {
840 bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start));
841 }
842 }
843 if (matchPos[lng - 1].wholeMatch.end < str.length) {
844 bits.push(str.slice(matchPos[lng - 1].wholeMatch.end));
845 }
846 finalStr = bits.join('');
847 }
848 return finalStr;
849 };
850
851 /**
852 * Returns the index within the passed String object of the first occurrence of the specified regex,
853 * starting the search at fromIndex. Returns -1 if the value is not found.
854 *
855 * @param {string} str string to search
856 * @param {RegExp} regex Regular expression to search
857 * @param {int} [fromIndex = 0] Index to start the search
858 * @returns {Number}
859 * @throws InvalidArgumentError
860 */
861 showdown.helper.regexIndexOf = function (str, regex, fromIndex) {
862 'use strict';
863 if (!showdown.helper.isString(str)) {
864 throw 'InvalidArgumentError: first parameter of showdown.helper.regexIndexOf function must be a string';
865 }
866 if (regex instanceof RegExp === false) {
867 throw 'InvalidArgumentError: second parameter of showdown.helper.regexIndexOf function must be an instance of RegExp';
868 }
869 var indexOf = str.substring(fromIndex || 0).search(regex);
870 return (indexOf >= 0) ? (indexOf + (fromIndex || 0)) : indexOf;
871 };
872
873 /**
874 * Splits the passed string object at the defined index, and returns an array composed of the two substrings
875 * @param {string} str string to split
876 * @param {int} index index to split string at
877 * @returns {[string,string]}
878 * @throws InvalidArgumentError
879 */
880 showdown.helper.splitAtIndex = function (str, index) {
881 'use strict';
882 if (!showdown.helper.isString(str)) {
883 throw 'InvalidArgumentError: first parameter of showdown.helper.regexIndexOf function must be a string';
884 }
885 return [str.substring(0, index), str.substring(index)];
886 };
887
888 /**
889 * Obfuscate an e-mail address through the use of Character Entities,
890 * transforming ASCII characters into their equivalent decimal or hex entities.
891 *
892 * Since it has a random component, subsequent calls to this function produce different results
893 *
894 * @param {string} mail
895 * @returns {string}
896 */
897 showdown.helper.encodeEmailAddress = function (mail) {
898 'use strict';
899 var encode = [
900 function (ch) {
901 return '&#' + ch.charCodeAt(0) + ';';
902 },
903 function (ch) {
904 return '&#x' + ch.charCodeAt(0).toString(16) + ';';
905 },
906 function (ch) {
907 return ch;
908 }
909 ];
910
911 mail = mail.replace(/./g, function (ch) {
912 if (ch === '@') {
913 // this *must* be encoded. I insist.
914 ch = encode[Math.floor(Math.random() * 2)](ch);
915 } else {
916 var r = Math.random();
917 // roughly 10% raw, 45% hex, 45% dec
918 ch = (
919 r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch)
920 );
921 }
922 return ch;
923 });
924
925 return mail;
926 };
927
928 /**
929 *
930 * @param str
931 * @param targetLength
932 * @param padString
933 * @returns {string}
934 */
935 showdown.helper.padEnd = function padEnd (str, targetLength, padString) {
936 'use strict';
937 /*jshint bitwise: false*/
938 // eslint-disable-next-line space-infix-ops
939 targetLength = targetLength>>0; //floor if number or convert non-number to 0;
940 /*jshint bitwise: true*/
941 padString = String(padString || ' ');
942 if (str.length > targetLength) {
943 return String(str);
944 } else {
945 targetLength = targetLength - str.length;
946 if (targetLength > padString.length) {
947 padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
948 }
949 return String(str) + padString.slice(0,targetLength);
950 }
951 };
952
953 /**
954 * Unescape HTML entities
955 * @param txt
956 * @returns {string}
957 */
958 showdown.helper.unescapeHTMLEntities = function (txt) {
959 'use strict';
960
961 return txt
962 .replace(/&quot;/g, '"')
963 .replace(/&lt;/g, '<')
964 .replace(/&gt;/g, '>')
965 .replace(/&amp;/g, '&');
966 };
967
968 showdown.helper._hashHTMLSpan = function (html, globals) {
969 return '¨C' + (globals.gHtmlSpans.push(html) - 1) + 'C';
970 };
971
972 /**
973 * Showdown's Event Object
974 * @param {string} name Name of the event
975 * @param {string} text Text
976 * @param {{}} params optional. params of the event
977 * @constructor
978 */
979 showdown.helper.Event = function (name, text, params) {
980 'use strict';
981
982 var regexp = params.regexp || null;
983 var matches = params.matches || {};
984 var options = params.options || {};
985 var converter = params.converter || null;
986 var globals = params.globals || {};
987
988 /**
989 * Get the name of the event
990 * @returns {string}
991 */
992 this.getName = function () {
993 return name;
994 };
995
996 this.getEventName = function () {
997 return name;
998 };
999
1000 this._stopExecution = false;
1001
1002 this.parsedText = params.parsedText || null;
1003
1004 this.getRegexp = function () {
1005 return regexp;
1006 };
1007
1008 this.getOptions = function () {
1009 return options;
1010 };
1011
1012 this.getConverter = function () {
1013 return converter;
1014 };
1015
1016 this.getGlobals = function () {
1017 return globals;
1018 };
1019
1020 this.getCapturedText = function () {
1021 return text;
1022 };
1023
1024 this.getText = function () {
1025 return text;
1026 };
1027
1028 this.setText = function (newText) {
1029 text = newText;
1030 };
1031
1032 this.getMatches = function () {
1033 return matches;
1034 };
1035
1036 this.setMatches = function (newMatches) {
1037 matches = newMatches;
1038 };
1039
1040 this.preventDefault = function (bool) {
1041 this._stopExecution = !bool;
1042 };
1043 };
1044
1045 /**
1046 * POLYFILLS
1047 */
1048 // use this instead of builtin is undefined for IE8 compatibility
1049 if (typeof(console) === 'undefined') {
1050 console = {
1051 warn: function (msg) {
1052 'use strict';
1053 alert(msg);
1054 },
1055 log: function (msg) {
1056 'use strict';
1057 alert(msg);
1058 },
1059 error: function (msg) {
1060 'use strict';
1061 throw msg;
1062 }
1063 };
1064 }
1065
1066 /**
1067 * Common regexes.
1068 * We declare some common regexes to improve performance
1069 */
1070 showdown.helper.regexes = {
1071 asteriskDashTildeAndColon: /([*_:~])/g,
1072 asteriskDashAndTilde: /([*_~])/g
1073 };
1074
1075 /**
1076 * EMOJIS LIST
1077 */
1078 showdown.helper.emojis = {
1079 '+1':'\ud83d\udc4d',
1080 '-1':'\ud83d\udc4e',
1081 '100':'\ud83d\udcaf',
1082 '1234':'\ud83d\udd22',
1083 '1st_place_medal':'\ud83e\udd47',
1084 '2nd_place_medal':'\ud83e\udd48',
1085 '3rd_place_medal':'\ud83e\udd49',
1086 '8ball':'\ud83c\udfb1',
1087 'a':'\ud83c\udd70\ufe0f',
1088 'ab':'\ud83c\udd8e',
1089 'abc':'\ud83d\udd24',
1090 'abcd':'\ud83d\udd21',
1091 'accept':'\ud83c\ude51',
1092 'aerial_tramway':'\ud83d\udea1',
1093 'airplane':'\u2708\ufe0f',
1094 'alarm_clock':'\u23f0',
1095 'alembic':'\u2697\ufe0f',
1096 'alien':'\ud83d\udc7d',
1097 'ambulance':'\ud83d\ude91',
1098 'amphora':'\ud83c\udffa',
1099 'anchor':'\u2693\ufe0f',
1100 'angel':'\ud83d\udc7c',
1101 'anger':'\ud83d\udca2',
1102 'angry':'\ud83d\ude20',
1103 'anguished':'\ud83d\ude27',
1104 'ant':'\ud83d\udc1c',
1105 'apple':'\ud83c\udf4e',
1106 'aquarius':'\u2652\ufe0f',
1107 'aries':'\u2648\ufe0f',
1108 'arrow_backward':'\u25c0\ufe0f',
1109 'arrow_double_down':'\u23ec',
1110 'arrow_double_up':'\u23eb',
1111 'arrow_down':'\u2b07\ufe0f',
1112 'arrow_down_small':'\ud83d\udd3d',
1113 'arrow_forward':'\u25b6\ufe0f',
1114 'arrow_heading_down':'\u2935\ufe0f',
1115 'arrow_heading_up':'\u2934\ufe0f',
1116 'arrow_left':'\u2b05\ufe0f',
1117 'arrow_lower_left':'\u2199\ufe0f',
1118 'arrow_lower_right':'\u2198\ufe0f',
1119 'arrow_right':'\u27a1\ufe0f',
1120 'arrow_right_hook':'\u21aa\ufe0f',
1121 'arrow_up':'\u2b06\ufe0f',
1122 'arrow_up_down':'\u2195\ufe0f',
1123 'arrow_up_small':'\ud83d\udd3c',
1124 'arrow_upper_left':'\u2196\ufe0f',
1125 'arrow_upper_right':'\u2197\ufe0f',
1126 'arrows_clockwise':'\ud83d\udd03',
1127 'arrows_counterclockwise':'\ud83d\udd04',
1128 'art':'\ud83c\udfa8',
1129 'articulated_lorry':'\ud83d\ude9b',
1130 'artificial_satellite':'\ud83d\udef0',
1131 'astonished':'\ud83d\ude32',
1132 'athletic_shoe':'\ud83d\udc5f',
1133 'atm':'\ud83c\udfe7',
1134 'atom_symbol':'\u269b\ufe0f',
1135 'avocado':'\ud83e\udd51',
1136 'b':'\ud83c\udd71\ufe0f',
1137 'baby':'\ud83d\udc76',
1138 'baby_bottle':'\ud83c\udf7c',
1139 'baby_chick':'\ud83d\udc24',
1140 'baby_symbol':'\ud83d\udebc',
1141 'back':'\ud83d\udd19',
1142 'bacon':'\ud83e\udd53',
1143 'badminton':'\ud83c\udff8',
1144 'baggage_claim':'\ud83d\udec4',
1145 'baguette_bread':'\ud83e\udd56',
1146 'balance_scale':'\u2696\ufe0f',
1147 'balloon':'\ud83c\udf88',
1148 'ballot_box':'\ud83d\uddf3',
1149 'ballot_box_with_check':'\u2611\ufe0f',
1150 'bamboo':'\ud83c\udf8d',
1151 'banana':'\ud83c\udf4c',
1152 'bangbang':'\u203c\ufe0f',
1153 'bank':'\ud83c\udfe6',
1154 'bar_chart':'\ud83d\udcca',
1155 'barber':'\ud83d\udc88',
1156 'baseball':'\u26be\ufe0f',
1157 'basketball':'\ud83c\udfc0',
1158 'basketball_man':'\u26f9\ufe0f',
1159 'basketball_woman':'\u26f9\ufe0f&zwj;\u2640\ufe0f',
1160 'bat':'\ud83e\udd87',
1161 'bath':'\ud83d\udec0',
1162 'bathtub':'\ud83d\udec1',
1163 'battery':'\ud83d\udd0b',
1164 'beach_umbrella':'\ud83c\udfd6',
1165 'bear':'\ud83d\udc3b',
1166 'bed':'\ud83d\udecf',
1167 'bee':'\ud83d\udc1d',
1168 'beer':'\ud83c\udf7a',
1169 'beers':'\ud83c\udf7b',
1170 'beetle':'\ud83d\udc1e',
1171 'beginner':'\ud83d\udd30',
1172 'bell':'\ud83d\udd14',
1173 'bellhop_bell':'\ud83d\udece',
1174 'bento':'\ud83c\udf71',
1175 'biking_man':'\ud83d\udeb4',
1176 'bike':'\ud83d\udeb2',
1177 'biking_woman':'\ud83d\udeb4&zwj;\u2640\ufe0f',
1178 'bikini':'\ud83d\udc59',
1179 'biohazard':'\u2623\ufe0f',
1180 'bird':'\ud83d\udc26',
1181 'birthday':'\ud83c\udf82',
1182 'black_circle':'\u26ab\ufe0f',
1183 'black_flag':'\ud83c\udff4',
1184 'black_heart':'\ud83d\udda4',
1185 'black_joker':'\ud83c\udccf',
1186 'black_large_square':'\u2b1b\ufe0f',
1187 'black_medium_small_square':'\u25fe\ufe0f',
1188 'black_medium_square':'\u25fc\ufe0f',
1189 'black_nib':'\u2712\ufe0f',
1190 'black_small_square':'\u25aa\ufe0f',
1191 'black_square_button':'\ud83d\udd32',
1192 'blonde_man':'\ud83d\udc71',
1193 'blonde_woman':'\ud83d\udc71&zwj;\u2640\ufe0f',
1194 'blossom':'\ud83c\udf3c',
1195 'blowfish':'\ud83d\udc21',
1196 'blue_book':'\ud83d\udcd8',
1197 'blue_car':'\ud83d\ude99',
1198 'blue_heart':'\ud83d\udc99',
1199 'blush':'\ud83d\ude0a',
1200 'boar':'\ud83d\udc17',
1201 'boat':'\u26f5\ufe0f',
1202 'bomb':'\ud83d\udca3',
1203 'book':'\ud83d\udcd6',
1204 'bookmark':'\ud83d\udd16',
1205 'bookmark_tabs':'\ud83d\udcd1',
1206 'books':'\ud83d\udcda',
1207 'boom':'\ud83d\udca5',
1208 'boot':'\ud83d\udc62',
1209 'bouquet':'\ud83d\udc90',
1210 'bowing_man':'\ud83d\ude47',
1211 'bow_and_arrow':'\ud83c\udff9',
1212 'bowing_woman':'\ud83d\ude47&zwj;\u2640\ufe0f',
1213 'bowling':'\ud83c\udfb3',
1214 'boxing_glove':'\ud83e\udd4a',
1215 'boy':'\ud83d\udc66',
1216 'bread':'\ud83c\udf5e',
1217 'bride_with_veil':'\ud83d\udc70',
1218 'bridge_at_night':'\ud83c\udf09',
1219 'briefcase':'\ud83d\udcbc',
1220 'broken_heart':'\ud83d\udc94',
1221 'bug':'\ud83d\udc1b',
1222 'building_construction':'\ud83c\udfd7',
1223 'bulb':'\ud83d\udca1',
1224 'bullettrain_front':'\ud83d\ude85',
1225 'bullettrain_side':'\ud83d\ude84',
1226 'burrito':'\ud83c\udf2f',
1227 'bus':'\ud83d\ude8c',
1228 'business_suit_levitating':'\ud83d\udd74',
1229 'busstop':'\ud83d\ude8f',
1230 'bust_in_silhouette':'\ud83d\udc64',
1231 'busts_in_silhouette':'\ud83d\udc65',
1232 'butterfly':'\ud83e\udd8b',
1233 'cactus':'\ud83c\udf35',
1234 'cake':'\ud83c\udf70',
1235 'calendar':'\ud83d\udcc6',
1236 'call_me_hand':'\ud83e\udd19',
1237 'calling':'\ud83d\udcf2',
1238 'camel':'\ud83d\udc2b',
1239 'camera':'\ud83d\udcf7',
1240 'camera_flash':'\ud83d\udcf8',
1241 'camping':'\ud83c\udfd5',
1242 'cancer':'\u264b\ufe0f',
1243 'candle':'\ud83d\udd6f',
1244 'candy':'\ud83c\udf6c',
1245 'canoe':'\ud83d\udef6',
1246 'capital_abcd':'\ud83d\udd20',
1247 'capricorn':'\u2651\ufe0f',
1248 'car':'\ud83d\ude97',
1249 'card_file_box':'\ud83d\uddc3',
1250 'card_index':'\ud83d\udcc7',
1251 'card_index_dividers':'\ud83d\uddc2',
1252 'carousel_horse':'\ud83c\udfa0',
1253 'carrot':'\ud83e\udd55',
1254 'cat':'\ud83d\udc31',
1255 'cat2':'\ud83d\udc08',
1256 'cd':'\ud83d\udcbf',
1257 'chains':'\u26d3',
1258 'champagne':'\ud83c\udf7e',
1259 'chart':'\ud83d\udcb9',
1260 'chart_with_downwards_trend':'\ud83d\udcc9',
1261 'chart_with_upwards_trend':'\ud83d\udcc8',
1262 'checkered_flag':'\ud83c\udfc1',
1263 'cheese':'\ud83e\uddc0',
1264 'cherries':'\ud83c\udf52',
1265 'cherry_blossom':'\ud83c\udf38',
1266 'chestnut':'\ud83c\udf30',
1267 'chicken':'\ud83d\udc14',
1268 'children_crossing':'\ud83d\udeb8',
1269 'chipmunk':'\ud83d\udc3f',
1270 'chocolate_bar':'\ud83c\udf6b',
1271 'christmas_tree':'\ud83c\udf84',
1272 'church':'\u26ea\ufe0f',
1273 'cinema':'\ud83c\udfa6',
1274 'circus_tent':'\ud83c\udfaa',
1275 'city_sunrise':'\ud83c\udf07',
1276 'city_sunset':'\ud83c\udf06',
1277 'cityscape':'\ud83c\udfd9',
1278 'cl':'\ud83c\udd91',
1279 'clamp':'\ud83d\udddc',
1280 'clap':'\ud83d\udc4f',
1281 'clapper':'\ud83c\udfac',
1282 'classical_building':'\ud83c\udfdb',
1283 'clinking_glasses':'\ud83e\udd42',
1284 'clipboard':'\ud83d\udccb',
1285 'clock1':'\ud83d\udd50',
1286 'clock10':'\ud83d\udd59',
1287 'clock1030':'\ud83d\udd65',
1288 'clock11':'\ud83d\udd5a',
1289 'clock1130':'\ud83d\udd66',
1290 'clock12':'\ud83d\udd5b',
1291 'clock1230':'\ud83d\udd67',
1292 'clock130':'\ud83d\udd5c',
1293 'clock2':'\ud83d\udd51',
1294 'clock230':'\ud83d\udd5d',
1295 'clock3':'\ud83d\udd52',
1296 'clock330':'\ud83d\udd5e',
1297 'clock4':'\ud83d\udd53',
1298 'clock430':'\ud83d\udd5f',
1299 'clock5':'\ud83d\udd54',
1300 'clock530':'\ud83d\udd60',
1301 'clock6':'\ud83d\udd55',
1302 'clock630':'\ud83d\udd61',
1303 'clock7':'\ud83d\udd56',
1304 'clock730':'\ud83d\udd62',
1305 'clock8':'\ud83d\udd57',
1306 'clock830':'\ud83d\udd63',
1307 'clock9':'\ud83d\udd58',
1308 'clock930':'\ud83d\udd64',
1309 'closed_book':'\ud83d\udcd5',
1310 'closed_lock_with_key':'\ud83d\udd10',
1311 'closed_umbrella':'\ud83c\udf02',
1312 'cloud':'\u2601\ufe0f',
1313 'cloud_with_lightning':'\ud83c\udf29',
1314 'cloud_with_lightning_and_rain':'\u26c8',
1315 'cloud_with_rain':'\ud83c\udf27',
1316 'cloud_with_snow':'\ud83c\udf28',
1317 'clown_face':'\ud83e\udd21',
1318 'clubs':'\u2663\ufe0f',
1319 'cocktail':'\ud83c\udf78',
1320 'coffee':'\u2615\ufe0f',
1321 'coffin':'\u26b0\ufe0f',
1322 'cold_sweat':'\ud83d\ude30',
1323 'comet':'\u2604\ufe0f',
1324 'computer':'\ud83d\udcbb',
1325 'computer_mouse':'\ud83d\uddb1',
1326 'confetti_ball':'\ud83c\udf8a',
1327 'confounded':'\ud83d\ude16',
1328 'confused':'\ud83d\ude15',
1329 'congratulations':'\u3297\ufe0f',
1330 'construction':'\ud83d\udea7',
1331 'construction_worker_man':'\ud83d\udc77',
1332 'construction_worker_woman':'\ud83d\udc77&zwj;\u2640\ufe0f',
1333 'control_knobs':'\ud83c\udf9b',
1334 'convenience_store':'\ud83c\udfea',
1335 'cookie':'\ud83c\udf6a',
1336 'cool':'\ud83c\udd92',
1337 'policeman':'\ud83d\udc6e',
1338 'copyright':'\u00a9\ufe0f',
1339 'corn':'\ud83c\udf3d',
1340 'couch_and_lamp':'\ud83d\udecb',
1341 'couple':'\ud83d\udc6b',
1342 'couple_with_heart_woman_man':'\ud83d\udc91',
1343 'couple_with_heart_man_man':'\ud83d\udc68&zwj;\u2764\ufe0f&zwj;\ud83d\udc68',
1344 'couple_with_heart_woman_woman':'\ud83d\udc69&zwj;\u2764\ufe0f&zwj;\ud83d\udc69',
1345 'couplekiss_man_man':'\ud83d\udc68&zwj;\u2764\ufe0f&zwj;\ud83d\udc8b&zwj;\ud83d\udc68',
1346 'couplekiss_man_woman':'\ud83d\udc8f',
1347 'couplekiss_woman_woman':'\ud83d\udc69&zwj;\u2764\ufe0f&zwj;\ud83d\udc8b&zwj;\ud83d\udc69',
1348 'cow':'\ud83d\udc2e',
1349 'cow2':'\ud83d\udc04',
1350 'cowboy_hat_face':'\ud83e\udd20',
1351 'crab':'\ud83e\udd80',
1352 'crayon':'\ud83d\udd8d',
1353 'credit_card':'\ud83d\udcb3',
1354 'crescent_moon':'\ud83c\udf19',
1355 'cricket':'\ud83c\udfcf',
1356 'crocodile':'\ud83d\udc0a',
1357 'croissant':'\ud83e\udd50',
1358 'crossed_fingers':'\ud83e\udd1e',
1359 'crossed_flags':'\ud83c\udf8c',
1360 'crossed_swords':'\u2694\ufe0f',
1361 'crown':'\ud83d\udc51',
1362 'cry':'\ud83d\ude22',
1363 'crying_cat_face':'\ud83d\ude3f',
1364 'crystal_ball':'\ud83d\udd2e',
1365 'cucumber':'\ud83e\udd52',
1366 'cupid':'\ud83d\udc98',
1367 'curly_loop':'\u27b0',
1368 'currency_exchange':'\ud83d\udcb1',
1369 'curry':'\ud83c\udf5b',
1370 'custard':'\ud83c\udf6e',
1371 'customs':'\ud83d\udec3',
1372 'cyclone':'\ud83c\udf00',
1373 'dagger':'\ud83d\udde1',
1374 'dancer':'\ud83d\udc83',
1375 'dancing_women':'\ud83d\udc6f',
1376 'dancing_men':'\ud83d\udc6f&zwj;\u2642\ufe0f',
1377 'dango':'\ud83c\udf61',
1378 'dark_sunglasses':'\ud83d\udd76',
1379 'dart':'\ud83c\udfaf',
1380 'dash':'\ud83d\udca8',
1381 'date':'\ud83d\udcc5',
1382 'deciduous_tree':'\ud83c\udf33',
1383 'deer':'\ud83e\udd8c',
1384 'department_store':'\ud83c\udfec',
1385 'derelict_house':'\ud83c\udfda',
1386 'desert':'\ud83c\udfdc',
1387 'desert_island':'\ud83c\udfdd',
1388 'desktop_computer':'\ud83d\udda5',
1389 'male_detective':'\ud83d\udd75\ufe0f',
1390 'diamond_shape_with_a_dot_inside':'\ud83d\udca0',
1391 'diamonds':'\u2666\ufe0f',
1392 'disappointed':'\ud83d\ude1e',
1393 'disappointed_relieved':'\ud83d\ude25',
1394 'dizzy':'\ud83d\udcab',
1395 'dizzy_face':'\ud83d\ude35',
1396 'do_not_litter':'\ud83d\udeaf',
1397 'dog':'\ud83d\udc36',
1398 'dog2':'\ud83d\udc15',
1399 'dollar':'\ud83d\udcb5',
1400 'dolls':'\ud83c\udf8e',
1401 'dolphin':'\ud83d\udc2c',
1402 'door':'\ud83d\udeaa',
1403 'doughnut':'\ud83c\udf69',
1404 'dove':'\ud83d\udd4a',
1405 'dragon':'\ud83d\udc09',
1406 'dragon_face':'\ud83d\udc32',
1407 'dress':'\ud83d\udc57',
1408 'dromedary_camel':'\ud83d\udc2a',
1409 'drooling_face':'\ud83e\udd24',
1410 'droplet':'\ud83d\udca7',
1411 'drum':'\ud83e\udd41',
1412 'duck':'\ud83e\udd86',
1413 'dvd':'\ud83d\udcc0',
1414 'e-mail':'\ud83d\udce7',
1415 'eagle':'\ud83e\udd85',
1416 'ear':'\ud83d\udc42',
1417 'ear_of_rice':'\ud83c\udf3e',
1418 'earth_africa':'\ud83c\udf0d',
1419 'earth_americas':'\ud83c\udf0e',
1420 'earth_asia':'\ud83c\udf0f',
1421 'egg':'\ud83e\udd5a',
1422 'eggplant':'\ud83c\udf46',
1423 'eight_pointed_black_star':'\u2734\ufe0f',
1424 'eight_spoked_asterisk':'\u2733\ufe0f',
1425 'electric_plug':'\ud83d\udd0c',
1426 'elephant':'\ud83d\udc18',
1427 'email':'\u2709\ufe0f',
1428 'end':'\ud83d\udd1a',
1429 'envelope_with_arrow':'\ud83d\udce9',
1430 'euro':'\ud83d\udcb6',
1431 'european_castle':'\ud83c\udff0',
1432 'european_post_office':'\ud83c\udfe4',
1433 'evergreen_tree':'\ud83c\udf32',
1434 'exclamation':'\u2757\ufe0f',
1435 'expressionless':'\ud83d\ude11',
1436 'eye':'\ud83d\udc41',
1437 'eye_speech_bubble':'\ud83d\udc41&zwj;\ud83d\udde8',
1438 'eyeglasses':'\ud83d\udc53',
1439 'eyes':'\ud83d\udc40',
1440 'face_with_head_bandage':'\ud83e\udd15',
1441 'face_with_thermometer':'\ud83e\udd12',
1442 'fist_oncoming':'\ud83d\udc4a',
1443 'factory':'\ud83c\udfed',
1444 'fallen_leaf':'\ud83c\udf42',
1445 'family_man_woman_boy':'\ud83d\udc6a',
1446 'family_man_boy':'\ud83d\udc68&zwj;\ud83d\udc66',
1447 'family_man_boy_boy':'\ud83d\udc68&zwj;\ud83d\udc66&zwj;\ud83d\udc66',
1448 'family_man_girl':'\ud83d\udc68&zwj;\ud83d\udc67',
1449 'family_man_girl_boy':'\ud83d\udc68&zwj;\ud83d\udc67&zwj;\ud83d\udc66',
1450 'family_man_girl_girl':'\ud83d\udc68&zwj;\ud83d\udc67&zwj;\ud83d\udc67',
1451 'family_man_man_boy':'\ud83d\udc68&zwj;\ud83d\udc68&zwj;\ud83d\udc66',
1452 'family_man_man_boy_boy':'\ud83d\udc68&zwj;\ud83d\udc68&zwj;\ud83d\udc66&zwj;\ud83d\udc66',
1453 'family_man_man_girl':'\ud83d\udc68&zwj;\ud83d\udc68&zwj;\ud83d\udc67',
1454 'family_man_man_girl_boy':'\ud83d\udc68&zwj;\ud83d\udc68&zwj;\ud83d\udc67&zwj;\ud83d\udc66',
1455 'family_man_man_girl_girl':'\ud83d\udc68&zwj;\ud83d\udc68&zwj;\ud83d\udc67&zwj;\ud83d\udc67',
1456 'family_man_woman_boy_boy':'\ud83d\udc68&zwj;\ud83d\udc69&zwj;\ud83d\udc66&zwj;\ud83d\udc66',
1457 'family_man_woman_girl':'\ud83d\udc68&zwj;\ud83d\udc69&zwj;\ud83d\udc67',
1458 'family_man_woman_girl_boy':'\ud83d\udc68&zwj;\ud83d\udc69&zwj;\ud83d\udc67&zwj;\ud83d\udc66',
1459 'family_man_woman_girl_girl':'\ud83d\udc68&zwj;\ud83d\udc69&zwj;\ud83d\udc67&zwj;\ud83d\udc67',
1460 'family_woman_boy':'\ud83d\udc69&zwj;\ud83d\udc66',
1461 'family_woman_boy_boy':'\ud83d\udc69&zwj;\ud83d\udc66&zwj;\ud83d\udc66',
1462 'family_woman_girl':'\ud83d\udc69&zwj;\ud83d\udc67',
1463 'family_woman_girl_boy':'\ud83d\udc69&zwj;\ud83d\udc67&zwj;\ud83d\udc66',
1464 'family_woman_girl_girl':'\ud83d\udc69&zwj;\ud83d\udc67&zwj;\ud83d\udc67',
1465 'family_woman_woman_boy':'\ud83d\udc69&zwj;\ud83d\udc69&zwj;\ud83d\udc66',
1466 'family_woman_woman_boy_boy':'\ud83d\udc69&zwj;\ud83d\udc69&zwj;\ud83d\udc66&zwj;\ud83d\udc66',
1467 'family_woman_woman_girl':'\ud83d\udc69&zwj;\ud83d\udc69&zwj;\ud83d\udc67',
1468 'family_woman_woman_girl_boy':'\ud83d\udc69&zwj;\ud83d\udc69&zwj;\ud83d\udc67&zwj;\ud83d\udc66',
1469 'family_woman_woman_girl_girl':'\ud83d\udc69&zwj;\ud83d\udc69&zwj;\ud83d\udc67&zwj;\ud83d\udc67',
1470 'fast_forward':'\u23e9',
1471 'fax':'\ud83d\udce0',
1472 'fearful':'\ud83d\ude28',
1473 'feet':'\ud83d\udc3e',
1474 'female_detective':'\ud83d\udd75\ufe0f&zwj;\u2640\ufe0f',
1475 'ferris_wheel':'\ud83c\udfa1',
1476 'ferry':'\u26f4',
1477 'field_hockey':'\ud83c\udfd1',
1478 'file_cabinet':'\ud83d\uddc4',
1479 'file_folder':'\ud83d\udcc1',
1480 'film_projector':'\ud83d\udcfd',
1481 'film_strip':'\ud83c\udf9e',
1482 'fire':'\ud83d\udd25',
1483 'fire_engine':'\ud83d\ude92',
1484 'fireworks':'\ud83c\udf86',
1485 'first_quarter_moon':'\ud83c\udf13',
1486 'first_quarter_moon_with_face':'\ud83c\udf1b',
1487 'fish':'\ud83d\udc1f',
1488 'fish_cake':'\ud83c\udf65',
1489 'fishing_pole_and_fish':'\ud83c\udfa3',
1490 'fist_raised':'\u270a',
1491 'fist_left':'\ud83e\udd1b',
1492 'fist_right':'\ud83e\udd1c',
1493 'flags':'\ud83c\udf8f',
1494 'flashlight':'\ud83d\udd26',
1495 'fleur_de_lis':'\u269c\ufe0f',
1496 'flight_arrival':'\ud83d\udeec',
1497 'flight_departure':'\ud83d\udeeb',
1498 'floppy_disk':'\ud83d\udcbe',
1499 'flower_playing_cards':'\ud83c\udfb4',
1500 'flushed':'\ud83d\ude33',
1501 'fog':'\ud83c\udf2b',
1502 'foggy':'\ud83c\udf01',
1503 'football':'\ud83c\udfc8',
1504 'footprints':'\ud83d\udc63',
1505 'fork_and_knife':'\ud83c\udf74',
1506 'fountain':'\u26f2\ufe0f',
1507 'fountain_pen':'\ud83d\udd8b',
1508 'four_leaf_clover':'\ud83c\udf40',
1509 'fox_face':'\ud83e\udd8a',
1510 'framed_picture':'\ud83d\uddbc',
1511 'free':'\ud83c\udd93',
1512 'fried_egg':'\ud83c\udf73',
1513 'fried_shrimp':'\ud83c\udf64',
1514 'fries':'\ud83c\udf5f',
1515 'frog':'\ud83d\udc38',
1516 'frowning':'\ud83d\ude26',
1517 'frowning_face':'\u2639\ufe0f',
1518 'frowning_man':'\ud83d\ude4d&zwj;\u2642\ufe0f',
1519 'frowning_woman':'\ud83d\ude4d',
1520 'middle_finger':'\ud83d\udd95',
1521 'fuelpump':'\u26fd\ufe0f',
1522 'full_moon':'\ud83c\udf15',
1523 'full_moon_with_face':'\ud83c\udf1d',
1524 'funeral_urn':'\u26b1\ufe0f',
1525 'game_die':'\ud83c\udfb2',
1526 'gear':'\u2699\ufe0f',
1527 'gem':'\ud83d\udc8e',
1528 'gemini':'\u264a\ufe0f',
1529 'ghost':'\ud83d\udc7b',
1530 'gift':'\ud83c\udf81',
1531 'gift_heart':'\ud83d\udc9d',
1532 'girl':'\ud83d\udc67',
1533 'globe_with_meridians':'\ud83c\udf10',
1534 'goal_net':'\ud83e\udd45',
1535 'goat':'\ud83d\udc10',
1536 'golf':'\u26f3\ufe0f',
1537 'golfing_man':'\ud83c\udfcc\ufe0f',
1538 'golfing_woman':'\ud83c\udfcc\ufe0f&zwj;\u2640\ufe0f',
1539 'gorilla':'\ud83e\udd8d',
1540 'grapes':'\ud83c\udf47',
1541 'green_apple':'\ud83c\udf4f',
1542 'green_book':'\ud83d\udcd7',
1543 'green_heart':'\ud83d\udc9a',
1544 'green_salad':'\ud83e\udd57',
1545 'grey_exclamation':'\u2755',
1546 'grey_question':'\u2754',
1547 'grimacing':'\ud83d\ude2c',
1548 'grin':'\ud83d\ude01',
1549 'grinning':'\ud83d\ude00',
1550 'guardsman':'\ud83d\udc82',
1551 'guardswoman':'\ud83d\udc82&zwj;\u2640\ufe0f',
1552 'guitar':'\ud83c\udfb8',
1553 'gun':'\ud83d\udd2b',
1554 'haircut_woman':'\ud83d\udc87',
1555 'haircut_man':'\ud83d\udc87&zwj;\u2642\ufe0f',
1556 'hamburger':'\ud83c\udf54',
1557 'hammer':'\ud83d\udd28',
1558 'hammer_and_pick':'\u2692',
1559 'hammer_and_wrench':'\ud83d\udee0',
1560 'hamster':'\ud83d\udc39',
1561 'hand':'\u270b',
1562 'handbag':'\ud83d\udc5c',
1563 'handshake':'\ud83e\udd1d',
1564 'hankey':'\ud83d\udca9',
1565 'hatched_chick':'\ud83d\udc25',
1566 'hatching_chick':'\ud83d\udc23',
1567 'headphones':'\ud83c\udfa7',
1568 'hear_no_evil':'\ud83d\ude49',
1569 'heart':'\u2764\ufe0f',
1570 'heart_decoration':'\ud83d\udc9f',
1571 'heart_eyes':'\ud83d\ude0d',
1572 'heart_eyes_cat':'\ud83d\ude3b',
1573 'heartbeat':'\ud83d\udc93',
1574 'heartpulse':'\ud83d\udc97',
1575 'hearts':'\u2665\ufe0f',
1576 'heavy_check_mark':'\u2714\ufe0f',
1577 'heavy_division_sign':'\u2797',
1578 'heavy_dollar_sign':'\ud83d\udcb2',
1579 'heavy_heart_exclamation':'\u2763\ufe0f',
1580 'heavy_minus_sign':'\u2796',
1581 'heavy_multiplication_x':'\u2716\ufe0f',
1582 'heavy_plus_sign':'\u2795',
1583 'helicopter':'\ud83d\ude81',
1584 'herb':'\ud83c\udf3f',
1585 'hibiscus':'\ud83c\udf3a',
1586 'high_brightness':'\ud83d\udd06',
1587 'high_heel':'\ud83d\udc60',
1588 'hocho':'\ud83d\udd2a',
1589 'hole':'\ud83d\udd73',
1590 'honey_pot':'\ud83c\udf6f',
1591 'horse':'\ud83d\udc34',
1592 'horse_racing':'\ud83c\udfc7',
1593 'hospital':'\ud83c\udfe5',
1594 'hot_pepper':'\ud83c\udf36',
1595 'hotdog':'\ud83c\udf2d',
1596 'hotel':'\ud83c\udfe8',
1597 'hotsprings':'\u2668\ufe0f',
1598 'hourglass':'\u231b\ufe0f',
1599 'hourglass_flowing_sand':'\u23f3',
1600 'house':'\ud83c\udfe0',
1601 'house_with_garden':'\ud83c\udfe1',
1602 'houses':'\ud83c\udfd8',
1603 'hugs':'\ud83e\udd17',
1604 'hushed':'\ud83d\ude2f',
1605 'ice_cream':'\ud83c\udf68',
1606 'ice_hockey':'\ud83c\udfd2',
1607 'ice_skate':'\u26f8',
1608 'icecream':'\ud83c\udf66',
1609 'id':'\ud83c\udd94',
1610 'ideograph_advantage':'\ud83c\ude50',
1611 'imp':'\ud83d\udc7f',
1612 'inbox_tray':'\ud83d\udce5',
1613 'incoming_envelope':'\ud83d\udce8',
1614 'tipping_hand_woman':'\ud83d\udc81',
1615 'information_source':'\u2139\ufe0f',
1616 'innocent':'\ud83d\ude07',
1617 'interrobang':'\u2049\ufe0f',
1618 'iphone':'\ud83d\udcf1',
1619 'izakaya_lantern':'\ud83c\udfee',
1620 'jack_o_lantern':'\ud83c\udf83',
1621 'japan':'\ud83d\uddfe',
1622 'japanese_castle':'\ud83c\udfef',
1623 'japanese_goblin':'\ud83d\udc7a',
1624 'japanese_ogre':'\ud83d\udc79',
1625 'jeans':'\ud83d\udc56',
1626 'joy':'\ud83d\ude02',
1627 'joy_cat':'\ud83d\ude39',
1628 'joystick':'\ud83d\udd79',
1629 'kaaba':'\ud83d\udd4b',
1630 'key':'\ud83d\udd11',
1631 'keyboard':'\u2328\ufe0f',
1632 'keycap_ten':'\ud83d\udd1f',
1633 'kick_scooter':'\ud83d\udef4',
1634 'kimono':'\ud83d\udc58',
1635 'kiss':'\ud83d\udc8b',
1636 'kissing':'\ud83d\ude17',
1637 'kissing_cat':'\ud83d\ude3d',
1638 'kissing_closed_eyes':'\ud83d\ude1a',
1639 'kissing_heart':'\ud83d\ude18',
1640 'kissing_smiling_eyes':'\ud83d\ude19',
1641 'kiwi_fruit':'\ud83e\udd5d',
1642 'koala':'\ud83d\udc28',
1643 'koko':'\ud83c\ude01',
1644 'label':'\ud83c\udff7',
1645 'large_blue_circle':'\ud83d\udd35',
1646 'large_blue_diamond':'\ud83d\udd37',
1647 'large_orange_diamond':'\ud83d\udd36',
1648 'last_quarter_moon':'\ud83c\udf17',
1649 'last_quarter_moon_with_face':'\ud83c\udf1c',
1650 'latin_cross':'\u271d\ufe0f',
1651 'laughing':'\ud83d\ude06',
1652 'leaves':'\ud83c\udf43',
1653 'ledger':'\ud83d\udcd2',
1654 'left_luggage':'\ud83d\udec5',
1655 'left_right_arrow':'\u2194\ufe0f',
1656 'leftwards_arrow_with_hook':'\u21a9\ufe0f',
1657 'lemon':'\ud83c\udf4b',
1658 'leo':'\u264c\ufe0f',
1659 'leopard':'\ud83d\udc06',
1660 'level_slider':'\ud83c\udf9a',
1661 'libra':'\u264e\ufe0f',
1662 'light_rail':'\ud83d\ude88',
1663 'link':'\ud83d\udd17',
1664 'lion':'\ud83e\udd81',
1665 'lips':'\ud83d\udc44',
1666 'lipstick':'\ud83d\udc84',
1667 'lizard':'\ud83e\udd8e',
1668 'lock':'\ud83d\udd12',
1669 'lock_with_ink_pen':'\ud83d\udd0f',
1670 'lollipop':'\ud83c\udf6d',
1671 'loop':'\u27bf',
1672 'loud_sound':'\ud83d\udd0a',
1673 'loudspeaker':'\ud83d\udce2',
1674 'love_hotel':'\ud83c\udfe9',
1675 'love_letter':'\ud83d\udc8c',
1676 'low_brightness':'\ud83d\udd05',
1677 'lying_face':'\ud83e\udd25',
1678 'm':'\u24c2\ufe0f',
1679 'mag':'\ud83d\udd0d',
1680 'mag_right':'\ud83d\udd0e',
1681 'mahjong':'\ud83c\udc04\ufe0f',
1682 'mailbox':'\ud83d\udceb',
1683 'mailbox_closed':'\ud83d\udcea',
1684 'mailbox_with_mail':'\ud83d\udcec',
1685 'mailbox_with_no_mail':'\ud83d\udced',
1686 'man':'\ud83d\udc68',
1687 'man_artist':'\ud83d\udc68&zwj;\ud83c\udfa8',
1688 'man_astronaut':'\ud83d\udc68&zwj;\ud83d\ude80',
1689 'man_cartwheeling':'\ud83e\udd38&zwj;\u2642\ufe0f',
1690 'man_cook':'\ud83d\udc68&zwj;\ud83c\udf73',
1691 'man_dancing':'\ud83d\udd7a',
1692 'man_facepalming':'\ud83e\udd26&zwj;\u2642\ufe0f',
1693 'man_factory_worker':'\ud83d\udc68&zwj;\ud83c\udfed',
1694 'man_farmer':'\ud83d\udc68&zwj;\ud83c\udf3e',
1695 'man_firefighter':'\ud83d\udc68&zwj;\ud83d\ude92',
1696 'man_health_worker':'\ud83d\udc68&zwj;\u2695\ufe0f',
1697 'man_in_tuxedo':'\ud83e\udd35',
1698 'man_judge':'\ud83d\udc68&zwj;\u2696\ufe0f',
1699 'man_juggling':'\ud83e\udd39&zwj;\u2642\ufe0f',
1700 'man_mechanic':'\ud83d\udc68&zwj;\ud83d\udd27',
1701 'man_office_worker':'\ud83d\udc68&zwj;\ud83d\udcbc',
1702 'man_pilot':'\ud83d\udc68&zwj;\u2708\ufe0f',
1703 'man_playing_handball':'\ud83e\udd3e&zwj;\u2642\ufe0f',
1704 'man_playing_water_polo':'\ud83e\udd3d&zwj;\u2642\ufe0f',
1705 'man_scientist':'\ud83d\udc68&zwj;\ud83d\udd2c',
1706 'man_shrugging':'\ud83e\udd37&zwj;\u2642\ufe0f',
1707 'man_singer':'\ud83d\udc68&zwj;\ud83c\udfa4',
1708 'man_student':'\ud83d\udc68&zwj;\ud83c\udf93',
1709 'man_teacher':'\ud83d\udc68&zwj;\ud83c\udfeb',
1710 'man_technologist':'\ud83d\udc68&zwj;\ud83d\udcbb',
1711 'man_with_gua_pi_mao':'\ud83d\udc72',
1712 'man_with_turban':'\ud83d\udc73',
1713 'tangerine':'\ud83c\udf4a',
1714 'mans_shoe':'\ud83d\udc5e',
1715 'mantelpiece_clock':'\ud83d\udd70',
1716 'maple_leaf':'\ud83c\udf41',
1717 'martial_arts_uniform':'\ud83e\udd4b',
1718 'mask':'\ud83d\ude37',
1719 'massage_woman':'\ud83d\udc86',
1720 'massage_man':'\ud83d\udc86&zwj;\u2642\ufe0f',
1721 'meat_on_bone':'\ud83c\udf56',
1722 'medal_military':'\ud83c\udf96',
1723 'medal_sports':'\ud83c\udfc5',
1724 'mega':'\ud83d\udce3',
1725 'melon':'\ud83c\udf48',
1726 'memo':'\ud83d\udcdd',
1727 'men_wrestling':'\ud83e\udd3c&zwj;\u2642\ufe0f',
1728 'menorah':'\ud83d\udd4e',
1729 'mens':'\ud83d\udeb9',
1730 'metal':'\ud83e\udd18',
1731 'metro':'\ud83d\ude87',
1732 'microphone':'\ud83c\udfa4',
1733 'microscope':'\ud83d\udd2c',
1734 'milk_glass':'\ud83e\udd5b',
1735 'milky_way':'\ud83c\udf0c',
1736 'minibus':'\ud83d\ude90',
1737 'minidisc':'\ud83d\udcbd',
1738 'mobile_phone_off':'\ud83d\udcf4',
1739 'money_mouth_face':'\ud83e\udd11',
1740 'money_with_wings':'\ud83d\udcb8',
1741 'moneybag':'\ud83d\udcb0',
1742 'monkey':'\ud83d\udc12',
1743 'monkey_face':'\ud83d\udc35',
1744 'monorail':'\ud83d\ude9d',
1745 'moon':'\ud83c\udf14',
1746 'mortar_board':'\ud83c\udf93',
1747 'mosque':'\ud83d\udd4c',
1748 'motor_boat':'\ud83d\udee5',
1749 'motor_scooter':'\ud83d\udef5',
1750 'motorcycle':'\ud83c\udfcd',
1751 'motorway':'\ud83d\udee3',
1752 'mount_fuji':'\ud83d\uddfb',
1753 'mountain':'\u26f0',
1754 'mountain_biking_man':'\ud83d\udeb5',
1755 'mountain_biking_woman':'\ud83d\udeb5&zwj;\u2640\ufe0f',
1756 'mountain_cableway':'\ud83d\udea0',
1757 'mountain_railway':'\ud83d\ude9e',
1758 'mountain_snow':'\ud83c\udfd4',
1759 'mouse':'\ud83d\udc2d',
1760 'mouse2':'\ud83d\udc01',
1761 'movie_camera':'\ud83c\udfa5',
1762 'moyai':'\ud83d\uddff',
1763 'mrs_claus':'\ud83e\udd36',
1764 'muscle':'\ud83d\udcaa',
1765 'mushroom':'\ud83c\udf44',
1766 'musical_keyboard':'\ud83c\udfb9',
1767 'musical_note':'\ud83c\udfb5',
1768 'musical_score':'\ud83c\udfbc',
1769 'mute':'\ud83d\udd07',
1770 'nail_care':'\ud83d\udc85',
1771 'name_badge':'\ud83d\udcdb',
1772 'national_park':'\ud83c\udfde',
1773 'nauseated_face':'\ud83e\udd22',
1774 'necktie':'\ud83d\udc54',
1775 'negative_squared_cross_mark':'\u274e',
1776 'nerd_face':'\ud83e\udd13',
1777 'neutral_face':'\ud83d\ude10',
1778 'new':'\ud83c\udd95',
1779 'new_moon':'\ud83c\udf11',
1780 'new_moon_with_face':'\ud83c\udf1a',
1781 'newspaper':'\ud83d\udcf0',
1782 'newspaper_roll':'\ud83d\uddde',
1783 'next_track_button':'\u23ed',
1784 'ng':'\ud83c\udd96',
1785 'no_good_man':'\ud83d\ude45&zwj;\u2642\ufe0f',
1786 'no_good_woman':'\ud83d\ude45',
1787 'night_with_stars':'\ud83c\udf03',
1788 'no_bell':'\ud83d\udd15',
1789 'no_bicycles':'\ud83d\udeb3',
1790 'no_entry':'\u26d4\ufe0f',
1791 'no_entry_sign':'\ud83d\udeab',
1792 'no_mobile_phones':'\ud83d\udcf5',
1793 'no_mouth':'\ud83d\ude36',
1794 'no_pedestrians':'\ud83d\udeb7',
1795 'no_smoking':'\ud83d\udead',
1796 'non-potable_water':'\ud83d\udeb1',
1797 'nose':'\ud83d\udc43',
1798 'notebook':'\ud83d\udcd3',
1799 'notebook_with_decorative_cover':'\ud83d\udcd4',
1800 'notes':'\ud83c\udfb6',
1801 'nut_and_bolt':'\ud83d\udd29',
1802 'o':'\u2b55\ufe0f',
1803 'o2':'\ud83c\udd7e\ufe0f',
1804 'ocean':'\ud83c\udf0a',
1805 'octopus':'\ud83d\udc19',
1806 'oden':'\ud83c\udf62',
1807 'office':'\ud83c\udfe2',
1808 'oil_drum':'\ud83d\udee2',
1809 'ok':'\ud83c\udd97',
1810 'ok_hand':'\ud83d\udc4c',
1811 'ok_man':'\ud83d\ude46&zwj;\u2642\ufe0f',
1812 'ok_woman':'\ud83d\ude46',
1813 'old_key':'\ud83d\udddd',
1814 'older_man':'\ud83d\udc74',
1815 'older_woman':'\ud83d\udc75',
1816 'om':'\ud83d\udd49',
1817 'on':'\ud83d\udd1b',
1818 'oncoming_automobile':'\ud83d\ude98',
1819 'oncoming_bus':'\ud83d\ude8d',
1820 'oncoming_police_car':'\ud83d\ude94',
1821 'oncoming_taxi':'\ud83d\ude96',
1822 'open_file_folder':'\ud83d\udcc2',
1823 'open_hands':'\ud83d\udc50',
1824 'open_mouth':'\ud83d\ude2e',
1825 'open_umbrella':'\u2602\ufe0f',
1826 'ophiuchus':'\u26ce',
1827 'orange_book':'\ud83d\udcd9',
1828 'orthodox_cross':'\u2626\ufe0f',
1829 'outbox_tray':'\ud83d\udce4',
1830 'owl':'\ud83e\udd89',
1831 'ox':'\ud83d\udc02',
1832 'package':'\ud83d\udce6',
1833 'page_facing_up':'\ud83d\udcc4',
1834 'page_with_curl':'\ud83d\udcc3',
1835 'pager':'\ud83d\udcdf',
1836 'paintbrush':'\ud83d\udd8c',
1837 'palm_tree':'\ud83c\udf34',
1838 'pancakes':'\ud83e\udd5e',
1839 'panda_face':'\ud83d\udc3c',
1840 'paperclip':'\ud83d\udcce',
1841 'paperclips':'\ud83d\udd87',
1842 'parasol_on_ground':'\u26f1',
1843 'parking':'\ud83c\udd7f\ufe0f',
1844 'part_alternation_mark':'\u303d\ufe0f',
1845 'partly_sunny':'\u26c5\ufe0f',
1846 'passenger_ship':'\ud83d\udef3',
1847 'passport_control':'\ud83d\udec2',
1848 'pause_button':'\u23f8',
1849 'peace_symbol':'\u262e\ufe0f',
1850 'peach':'\ud83c\udf51',
1851 'peanuts':'\ud83e\udd5c',
1852 'pear':'\ud83c\udf50',
1853 'pen':'\ud83d\udd8a',
1854 'pencil2':'\u270f\ufe0f',
1855 'penguin':'\ud83d\udc27',
1856 'pensive':'\ud83d\ude14',
1857 'performing_arts':'\ud83c\udfad',
1858 'persevere':'\ud83d\ude23',
1859 'person_fencing':'\ud83e\udd3a',
1860 'pouting_woman':'\ud83d\ude4e',
1861 'phone':'\u260e\ufe0f',
1862 'pick':'\u26cf',
1863 'pig':'\ud83d\udc37',
1864 'pig2':'\ud83d\udc16',
1865 'pig_nose':'\ud83d\udc3d',
1866 'pill':'\ud83d\udc8a',
1867 'pineapple':'\ud83c\udf4d',
1868 'ping_pong':'\ud83c\udfd3',
1869 'pisces':'\u2653\ufe0f',
1870 'pizza':'\ud83c\udf55',
1871 'place_of_worship':'\ud83d\uded0',
1872 'plate_with_cutlery':'\ud83c\udf7d',
1873 'play_or_pause_button':'\u23ef',
1874 'point_down':'\ud83d\udc47',
1875 'point_left':'\ud83d\udc48',
1876 'point_right':'\ud83d\udc49',
1877 'point_up':'\u261d\ufe0f',
1878 'point_up_2':'\ud83d\udc46',
1879 'police_car':'\ud83d\ude93',
1880 'policewoman':'\ud83d\udc6e&zwj;\u2640\ufe0f',
1881 'poodle':'\ud83d\udc29',
1882 'popcorn':'\ud83c\udf7f',
1883 'post_office':'\ud83c\udfe3',
1884 'postal_horn':'\ud83d\udcef',
1885 'postbox':'\ud83d\udcee',
1886 'potable_water':'\ud83d\udeb0',
1887 'potato':'\ud83e\udd54',
1888 'pouch':'\ud83d\udc5d',
1889 'poultry_leg':'\ud83c\udf57',
1890 'pound':'\ud83d\udcb7',
1891 'rage':'\ud83d\ude21',
1892 'pouting_cat':'\ud83d\ude3e',
1893 'pouting_man':'\ud83d\ude4e&zwj;\u2642\ufe0f',
1894 'pray':'\ud83d\ude4f',
1895 'prayer_beads':'\ud83d\udcff',
1896 'pregnant_woman':'\ud83e\udd30',
1897 'previous_track_button':'\u23ee',
1898 'prince':'\ud83e\udd34',
1899 'princess':'\ud83d\udc78',
1900 'printer':'\ud83d\udda8',
1901 'purple_heart':'\ud83d\udc9c',
1902 'purse':'\ud83d\udc5b',
1903 'pushpin':'\ud83d\udccc',
1904 'put_litter_in_its_place':'\ud83d\udeae',
1905 'question':'\u2753',
1906 'rabbit':'\ud83d\udc30',
1907 'rabbit2':'\ud83d\udc07',
1908 'racehorse':'\ud83d\udc0e',
1909 'racing_car':'\ud83c\udfce',
1910 'radio':'\ud83d\udcfb',
1911 'radio_button':'\ud83d\udd18',
1912 'radioactive':'\u2622\ufe0f',
1913 'railway_car':'\ud83d\ude83',
1914 'railway_track':'\ud83d\udee4',
1915 'rainbow':'\ud83c\udf08',
1916 'rainbow_flag':'\ud83c\udff3\ufe0f&zwj;\ud83c\udf08',
1917 'raised_back_of_hand':'\ud83e\udd1a',
1918 'raised_hand_with_fingers_splayed':'\ud83d\udd90',
1919 'raised_hands':'\ud83d\ude4c',
1920 'raising_hand_woman':'\ud83d\ude4b',
1921 'raising_hand_man':'\ud83d\ude4b&zwj;\u2642\ufe0f',
1922 'ram':'\ud83d\udc0f',
1923 'ramen':'\ud83c\udf5c',
1924 'rat':'\ud83d\udc00',
1925 'record_button':'\u23fa',
1926 'recycle':'\u267b\ufe0f',
1927 'red_circle':'\ud83d\udd34',
1928 'registered':'\u00ae\ufe0f',
1929 'relaxed':'\u263a\ufe0f',
1930 'relieved':'\ud83d\ude0c',
1931 'reminder_ribbon':'\ud83c\udf97',
1932 'repeat':'\ud83d\udd01',
1933 'repeat_one':'\ud83d\udd02',
1934 'rescue_worker_helmet':'\u26d1',
1935 'restroom':'\ud83d\udebb',
1936 'revolving_hearts':'\ud83d\udc9e',
1937 'rewind':'\u23ea',
1938 'rhinoceros':'\ud83e\udd8f',
1939 'ribbon':'\ud83c\udf80',
1940 'rice':'\ud83c\udf5a',
1941 'rice_ball':'\ud83c\udf59',
1942 'rice_cracker':'\ud83c\udf58',
1943 'rice_scene':'\ud83c\udf91',
1944 'right_anger_bubble':'\ud83d\uddef',
1945 'ring':'\ud83d\udc8d',
1946 'robot':'\ud83e\udd16',
1947 'rocket':'\ud83d\ude80',
1948 'rofl':'\ud83e\udd23',
1949 'roll_eyes':'\ud83d\ude44',
1950 'roller_coaster':'\ud83c\udfa2',
1951 'rooster':'\ud83d\udc13',
1952 'rose':'\ud83c\udf39',
1953 'rosette':'\ud83c\udff5',
1954 'rotating_light':'\ud83d\udea8',
1955 'round_pushpin':'\ud83d\udccd',
1956 'rowing_man':'\ud83d\udea3',
1957 'rowing_woman':'\ud83d\udea3&zwj;\u2640\ufe0f',
1958 'rugby_football':'\ud83c\udfc9',
1959 'running_man':'\ud83c\udfc3',
1960 'running_shirt_with_sash':'\ud83c\udfbd',
1961 'running_woman':'\ud83c\udfc3&zwj;\u2640\ufe0f',
1962 'sa':'\ud83c\ude02\ufe0f',
1963 'sagittarius':'\u2650\ufe0f',
1964 'sake':'\ud83c\udf76',
1965 'sandal':'\ud83d\udc61',
1966 'santa':'\ud83c\udf85',
1967 'satellite':'\ud83d\udce1',
1968 'saxophone':'\ud83c\udfb7',
1969 'school':'\ud83c\udfeb',
1970 'school_satchel':'\ud83c\udf92',
1971 'scissors':'\u2702\ufe0f',
1972 'scorpion':'\ud83e\udd82',
1973 'scorpius':'\u264f\ufe0f',
1974 'scream':'\ud83d\ude31',
1975 'scream_cat':'\ud83d\ude40',
1976 'scroll':'\ud83d\udcdc',
1977 'seat':'\ud83d\udcba',
1978 'secret':'\u3299\ufe0f',
1979 'see_no_evil':'\ud83d\ude48',
1980 'seedling':'\ud83c\udf31',
1981 'selfie':'\ud83e\udd33',
1982 'shallow_pan_of_food':'\ud83e\udd58',
1983 'shamrock':'\u2618\ufe0f',
1984 'shark':'\ud83e\udd88',
1985 'shaved_ice':'\ud83c\udf67',
1986 'sheep':'\ud83d\udc11',
1987 'shell':'\ud83d\udc1a',
1988 'shield':'\ud83d\udee1',
1989 'shinto_shrine':'\u26e9',
1990 'ship':'\ud83d\udea2',
1991 'shirt':'\ud83d\udc55',
1992 'shopping':'\ud83d\udecd',
1993 'shopping_cart':'\ud83d\uded2',
1994 'shower':'\ud83d\udebf',
1995 'shrimp':'\ud83e\udd90',
1996 'signal_strength':'\ud83d\udcf6',
1997 'six_pointed_star':'\ud83d\udd2f',
1998 'ski':'\ud83c\udfbf',
1999 'skier':'\u26f7',
2000 'skull':'\ud83d\udc80',
2001 'skull_and_crossbones':'\u2620\ufe0f',
2002 'sleeping':'\ud83d\ude34',
2003 'sleeping_bed':'\ud83d\udecc',
2004 'sleepy':'\ud83d\ude2a',
2005 'slightly_frowning_face':'\ud83d\ude41',
2006 'slightly_smiling_face':'\ud83d\ude42',
2007 'slot_machine':'\ud83c\udfb0',
2008 'small_airplane':'\ud83d\udee9',
2009 'small_blue_diamond':'\ud83d\udd39',
2010 'small_orange_diamond':'\ud83d\udd38',
2011 'small_red_triangle':'\ud83d\udd3a',
2012 'small_red_triangle_down':'\ud83d\udd3b',
2013 'smile':'\ud83d\ude04',
2014 'smile_cat':'\ud83d\ude38',
2015 'smiley':'\ud83d\ude03',
2016 'smiley_cat':'\ud83d\ude3a',
2017 'smiling_imp':'\ud83d\ude08',
2018 'smirk':'\ud83d\ude0f',
2019 'smirk_cat':'\ud83d\ude3c',
2020 'smoking':'\ud83d\udeac',
2021 'snail':'\ud83d\udc0c',
2022 'snake':'\ud83d\udc0d',
2023 'sneezing_face':'\ud83e\udd27',
2024 'snowboarder':'\ud83c\udfc2',
2025 'snowflake':'\u2744\ufe0f',
2026 'snowman':'\u26c4\ufe0f',
2027 'snowman_with_snow':'\u2603\ufe0f',
2028 'sob':'\ud83d\ude2d',
2029 'soccer':'\u26bd\ufe0f',
2030 'soon':'\ud83d\udd1c',
2031 'sos':'\ud83c\udd98',
2032 'sound':'\ud83d\udd09',
2033 'space_invader':'\ud83d\udc7e',
2034 'spades':'\u2660\ufe0f',
2035 'spaghetti':'\ud83c\udf5d',
2036 'sparkle':'\u2747\ufe0f',
2037 'sparkler':'\ud83c\udf87',
2038 'sparkles':'\u2728',
2039 'sparkling_heart':'\ud83d\udc96',
2040 'speak_no_evil':'\ud83d\ude4a',
2041 'speaker':'\ud83d\udd08',
2042 'speaking_head':'\ud83d\udde3',
2043 'speech_balloon':'\ud83d\udcac',
2044 'speedboat':'\ud83d\udea4',
2045 'spider':'\ud83d\udd77',
2046 'spider_web':'\ud83d\udd78',
2047 'spiral_calendar':'\ud83d\uddd3',
2048 'spiral_notepad':'\ud83d\uddd2',
2049 'spoon':'\ud83e\udd44',
2050 'squid':'\ud83e\udd91',
2051 'stadium':'\ud83c\udfdf',
2052 'star':'\u2b50\ufe0f',
2053 'star2':'\ud83c\udf1f',
2054 'star_and_crescent':'\u262a\ufe0f',
2055 'star_of_david':'\u2721\ufe0f',
2056 'stars':'\ud83c\udf20',
2057 'station':'\ud83d\ude89',
2058 'statue_of_liberty':'\ud83d\uddfd',
2059 'steam_locomotive':'\ud83d\ude82',
2060 'stew':'\ud83c\udf72',
2061 'stop_button':'\u23f9',
2062 'stop_sign':'\ud83d\uded1',
2063 'stopwatch':'\u23f1',
2064 'straight_ruler':'\ud83d\udccf',
2065 'strawberry':'\ud83c\udf53',
2066 'stuck_out_tongue':'\ud83d\ude1b',
2067 'stuck_out_tongue_closed_eyes':'\ud83d\ude1d',
2068 'stuck_out_tongue_winking_eye':'\ud83d\ude1c',
2069 'studio_microphone':'\ud83c\udf99',
2070 'stuffed_flatbread':'\ud83e\udd59',
2071 'sun_behind_large_cloud':'\ud83c\udf25',
2072 'sun_behind_rain_cloud':'\ud83c\udf26',
2073 'sun_behind_small_cloud':'\ud83c\udf24',
2074 'sun_with_face':'\ud83c\udf1e',
2075 'sunflower':'\ud83c\udf3b',
2076 'sunglasses':'\ud83d\ude0e',
2077 'sunny':'\u2600\ufe0f',
2078 'sunrise':'\ud83c\udf05',
2079 'sunrise_over_mountains':'\ud83c\udf04',
2080 'surfing_man':'\ud83c\udfc4',
2081 'surfing_woman':'\ud83c\udfc4&zwj;\u2640\ufe0f',
2082 'sushi':'\ud83c\udf63',
2083 'suspension_railway':'\ud83d\ude9f',
2084 'sweat':'\ud83d\ude13',
2085 'sweat_drops':'\ud83d\udca6',
2086 'sweat_smile':'\ud83d\ude05',
2087 'sweet_potato':'\ud83c\udf60',
2088 'swimming_man':'\ud83c\udfca',
2089 'swimming_woman':'\ud83c\udfca&zwj;\u2640\ufe0f',
2090 'symbols':'\ud83d\udd23',
2091 'synagogue':'\ud83d\udd4d',
2092 'syringe':'\ud83d\udc89',
2093 'taco':'\ud83c\udf2e',
2094 'tada':'\ud83c\udf89',
2095 'tanabata_tree':'\ud83c\udf8b',
2096 'taurus':'\u2649\ufe0f',
2097 'taxi':'\ud83d\ude95',
2098 'tea':'\ud83c\udf75',
2099 'telephone_receiver':'\ud83d\udcde',
2100 'telescope':'\ud83d\udd2d',
2101 'tennis':'\ud83c\udfbe',
2102 'tent':'\u26fa\ufe0f',
2103 'thermometer':'\ud83c\udf21',
2104 'thinking':'\ud83e\udd14',
2105 'thought_balloon':'\ud83d\udcad',
2106 'ticket':'\ud83c\udfab',
2107 'tickets':'\ud83c\udf9f',
2108 'tiger':'\ud83d\udc2f',
2109 'tiger2':'\ud83d\udc05',
2110 'timer_clock':'\u23f2',
2111 'tipping_hand_man':'\ud83d\udc81&zwj;\u2642\ufe0f',
2112 'tired_face':'\ud83d\ude2b',
2113 'tm':'\u2122\ufe0f',
2114 'toilet':'\ud83d\udebd',
2115 'tokyo_tower':'\ud83d\uddfc',
2116 'tomato':'\ud83c\udf45',
2117 'tongue':'\ud83d\udc45',
2118 'top':'\ud83d\udd1d',
2119 'tophat':'\ud83c\udfa9',
2120 'tornado':'\ud83c\udf2a',
2121 'trackball':'\ud83d\uddb2',
2122 'tractor':'\ud83d\ude9c',
2123 'traffic_light':'\ud83d\udea5',
2124 'train':'\ud83d\ude8b',
2125 'train2':'\ud83d\ude86',
2126 'tram':'\ud83d\ude8a',
2127 'triangular_flag_on_post':'\ud83d\udea9',
2128 'triangular_ruler':'\ud83d\udcd0',
2129 'trident':'\ud83d\udd31',
2130 'triumph':'\ud83d\ude24',
2131 'trolleybus':'\ud83d\ude8e',
2132 'trophy':'\ud83c\udfc6',
2133 'tropical_drink':'\ud83c\udf79',
2134 'tropical_fish':'\ud83d\udc20',
2135 'truck':'\ud83d\ude9a',
2136 'trumpet':'\ud83c\udfba',
2137 'tulip':'\ud83c\udf37',
2138 'tumbler_glass':'\ud83e\udd43',
2139 'turkey':'\ud83e\udd83',
2140 'turtle':'\ud83d\udc22',
2141 'tv':'\ud83d\udcfa',
2142 'twisted_rightwards_arrows':'\ud83d\udd00',
2143 'two_hearts':'\ud83d\udc95',
2144 'two_men_holding_hands':'\ud83d\udc6c',
2145 'two_women_holding_hands':'\ud83d\udc6d',
2146 'u5272':'\ud83c\ude39',
2147 'u5408':'\ud83c\ude34',
2148 'u55b6':'\ud83c\ude3a',
2149 'u6307':'\ud83c\ude2f\ufe0f',
2150 'u6708':'\ud83c\ude37\ufe0f',
2151 'u6709':'\ud83c\ude36',
2152 'u6e80':'\ud83c\ude35',
2153 'u7121':'\ud83c\ude1a\ufe0f',
2154 'u7533':'\ud83c\ude38',
2155 'u7981':'\ud83c\ude32',
2156 'u7a7a':'\ud83c\ude33',
2157 'umbrella':'\u2614\ufe0f',
2158 'unamused':'\ud83d\ude12',
2159 'underage':'\ud83d\udd1e',
2160 'unicorn':'\ud83e\udd84',
2161 'unlock':'\ud83d\udd13',
2162 'up':'\ud83c\udd99',
2163 'upside_down_face':'\ud83d\ude43',
2164 'v':'\u270c\ufe0f',
2165 'vertical_traffic_light':'\ud83d\udea6',
2166 'vhs':'\ud83d\udcfc',
2167 'vibration_mode':'\ud83d\udcf3',
2168 'video_camera':'\ud83d\udcf9',
2169 'video_game':'\ud83c\udfae',
2170 'violin':'\ud83c\udfbb',
2171 'virgo':'\u264d\ufe0f',
2172 'volcano':'\ud83c\udf0b',
2173 'volleyball':'\ud83c\udfd0',
2174 'vs':'\ud83c\udd9a',
2175 'vulcan_salute':'\ud83d\udd96',
2176 'walking_man':'\ud83d\udeb6',
2177 'walking_woman':'\ud83d\udeb6&zwj;\u2640\ufe0f',
2178 'waning_crescent_moon':'\ud83c\udf18',
2179 'waning_gibbous_moon':'\ud83c\udf16',
2180 'warning':'\u26a0\ufe0f',
2181 'wastebasket':'\ud83d\uddd1',
2182 'watch':'\u231a\ufe0f',
2183 'water_buffalo':'\ud83d\udc03',
2184 'watermelon':'\ud83c\udf49',
2185 'wave':'\ud83d\udc4b',
2186 'wavy_dash':'\u3030\ufe0f',
2187 'waxing_crescent_moon':'\ud83c\udf12',
2188 'wc':'\ud83d\udebe',
2189 'weary':'\ud83d\ude29',
2190 'wedding':'\ud83d\udc92',
2191 'weight_lifting_man':'\ud83c\udfcb\ufe0f',
2192 'weight_lifting_woman':'\ud83c\udfcb\ufe0f&zwj;\u2640\ufe0f',
2193 'whale':'\ud83d\udc33',
2194 'whale2':'\ud83d\udc0b',
2195 'wheel_of_dharma':'\u2638\ufe0f',
2196 'wheelchair':'\u267f\ufe0f',
2197 'white_check_mark':'\u2705',
2198 'white_circle':'\u26aa\ufe0f',
2199 'white_flag':'\ud83c\udff3\ufe0f',
2200 'white_flower':'\ud83d\udcae',
2201 'white_large_square':'\u2b1c\ufe0f',
2202 'white_medium_small_square':'\u25fd\ufe0f',
2203 'white_medium_square':'\u25fb\ufe0f',
2204 'white_small_square':'\u25ab\ufe0f',
2205 'white_square_button':'\ud83d\udd33',
2206 'wilted_flower':'\ud83e\udd40',
2207 'wind_chime':'\ud83c\udf90',
2208 'wind_face':'\ud83c\udf2c',
2209 'wine_glass':'\ud83c\udf77',
2210 'wink':'\ud83d\ude09',
2211 'wolf':'\ud83d\udc3a',
2212 'woman':'\ud83d\udc69',
2213 'woman_artist':'\ud83d\udc69&zwj;\ud83c\udfa8',
2214 'woman_astronaut':'\ud83d\udc69&zwj;\ud83d\ude80',
2215 'woman_cartwheeling':'\ud83e\udd38&zwj;\u2640\ufe0f',
2216 'woman_cook':'\ud83d\udc69&zwj;\ud83c\udf73',
2217 'woman_facepalming':'\ud83e\udd26&zwj;\u2640\ufe0f',
2218 'woman_factory_worker':'\ud83d\udc69&zwj;\ud83c\udfed',
2219 'woman_farmer':'\ud83d\udc69&zwj;\ud83c\udf3e',
2220 'woman_firefighter':'\ud83d\udc69&zwj;\ud83d\ude92',
2221 'woman_health_worker':'\ud83d\udc69&zwj;\u2695\ufe0f',
2222 'woman_judge':'\ud83d\udc69&zwj;\u2696\ufe0f',
2223 'woman_juggling':'\ud83e\udd39&zwj;\u2640\ufe0f',
2224 'woman_mechanic':'\ud83d\udc69&zwj;\ud83d\udd27',
2225 'woman_office_worker':'\ud83d\udc69&zwj;\ud83d\udcbc',
2226 'woman_pilot':'\ud83d\udc69&zwj;\u2708\ufe0f',
2227 'woman_playing_handball':'\ud83e\udd3e&zwj;\u2640\ufe0f',
2228 'woman_playing_water_polo':'\ud83e\udd3d&zwj;\u2640\ufe0f',
2229 'woman_scientist':'\ud83d\udc69&zwj;\ud83d\udd2c',
2230 'woman_shrugging':'\ud83e\udd37&zwj;\u2640\ufe0f',
2231 'woman_singer':'\ud83d\udc69&zwj;\ud83c\udfa4',
2232 'woman_student':'\ud83d\udc69&zwj;\ud83c\udf93',
2233 'woman_teacher':'\ud83d\udc69&zwj;\ud83c\udfeb',
2234 'woman_technologist':'\ud83d\udc69&zwj;\ud83d\udcbb',
2235 'woman_with_turban':'\ud83d\udc73&zwj;\u2640\ufe0f',
2236 'womans_clothes':'\ud83d\udc5a',
2237 'womans_hat':'\ud83d\udc52',
2238 'women_wrestling':'\ud83e\udd3c&zwj;\u2640\ufe0f',
2239 'womens':'\ud83d\udeba',
2240 'world_map':'\ud83d\uddfa',
2241 'worried':'\ud83d\ude1f',
2242 'wrench':'\ud83d\udd27',
2243 'writing_hand':'\u270d\ufe0f',
2244 'x':'\u274c',
2245 'yellow_heart':'\ud83d\udc9b',
2246 'yen':'\ud83d\udcb4',
2247 'yin_yang':'\u262f\ufe0f',
2248 'yum':'\ud83d\ude0b',
2249 'zap':'\u26a1\ufe0f',
2250 'zipper_mouth_face':'\ud83e\udd10',
2251 'zzz':'\ud83d\udca4',
2252
2253 /* special emojis :P */
2254 'octocat': '<img width="20" height="20" align="absmiddle" src="https://assets-cdn.github.com/images/icons/emoji/octocat.png">',
2255 'showdown': '<img width="20" height="20" align="absmiddle" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAS1BMVEX///8jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS0jJS3b1q3b1q3b1q3b1q3b1q3b1q3b1q3b1q0565CIAAAAGXRSTlMAQHCAYCCw/+DQwPCQUBAwoHCAEP+wwFBgS2fvBgAAAUZJREFUeAHs1cGy7BAUheFFsEDw/k97VTq3T6ge2EmdM+pvrP6Iwd74XV9Kb52xuMU4/uc1YNgZLFOeV8FGdhGrNk5SEgUyPxAEdj4LlMRDyhVAMVEa2M7TBSeVZAFPdqHgzSZJwPKgcLFLAooHDJo4EDCw4gAtBoJA5UFj4Ng5LOGLwVXZuoIlji/jeQHFk7+baHxrCjeUwB9+s88KndvlhcyBN5BSkYNQIVVb4pV+Npm7hhuKDs/uMP5KxT3WzSNNLIuuoDpMmuAVMruMSeDyQBi24DTr43LAY7ILA1QYaWkgfHzFthYYzg67SQsCbB8GhJUEGCtO9n0rSaCLxgJQjS/JSgMTg2eBDEHAJ+H350AsjYNYscrErgI2e/l+mdR967TCX/v6N0EhPECYCP0i+IAoYQOE8BogNhQMEMdrgAQWHaMAAGi5I5euoY9NAAAAAElFTkSuQmCC">'
2256 };
2257
2258 /**
2259 * These are all the transformations that form block-level
2260 * tags like paragraphs, headers, and list items.
2261 */
2262 showdown.subParser('makehtml.blockGamut', function (text, options, globals) {
2263 'use strict';
2264
2265 text = globals.converter._dispatch('makehtml.blockGamut.before', text, options, globals).getText();
2266
2267 // we parse blockquotes first so that we can have headings and hrs
2268 // inside blockquotes
2269 text = showdown.subParser('makehtml.blockQuotes')(text, options, globals);
2270 text = showdown.subParser('makehtml.headers')(text, options, globals);
2271
2272 // Do Horizontal Rules:
2273 text = showdown.subParser('makehtml.horizontalRule')(text, options, globals);
2274
2275 text = showdown.subParser('makehtml.lists')(text, options, globals);
2276 text = showdown.subParser('makehtml.codeBlocks')(text, options, globals);
2277 text = showdown.subParser('makehtml.tables')(text, options, globals);
2278
2279 // We already ran _HashHTMLBlocks() before, in Markdown(), but that
2280 // was to escape raw HTML in the original Markdown source. This time,
2281 // we're escaping the markup we've just created, so that we don't wrap
2282 // <p> tags around block-level tags.
2283 text = showdown.subParser('makehtml.hashHTMLBlocks')(text, options, globals);
2284 text = showdown.subParser('makehtml.paragraphs')(text, options, globals);
2285
2286 text = globals.converter._dispatch('makehtml.blockGamut.after', text, options, globals).getText();
2287
2288 return text;
2289 });
2290
2291 showdown.subParser('makehtml.blockQuotes', function (text, options, globals) {
2292 'use strict';
2293
2294 text = globals.converter._dispatch('makehtml.blockQuotes.before', text, options, globals).getText();
2295
2296 // add a couple extra lines after the text and endtext mark
2297 text = text + '\n\n';
2298
2299 var rgx = /(^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+/gm;
2300
2301 if (options.splitAdjacentBlockquotes) {
2302 rgx = /^ {0,3}>[\s\S]*?(?:\n\n)/gm;
2303 }
2304
2305 text = text.replace(rgx, function (bq) {
2306 // attacklab: hack around Konqueror 3.5.4 bug:
2307 // "----------bug".replace(/^-/g,"") == "bug"
2308 bq = bq.replace(/^[ \t]*>[ \t]?/gm, ''); // trim one level of quoting
2309
2310 // attacklab: clean up hack
2311 bq = bq.replace(/¨0/g, '');
2312
2313 bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines
2314 bq = showdown.subParser('makehtml.githubCodeBlocks')(bq, options, globals);
2315 bq = showdown.subParser('makehtml.blockGamut')(bq, options, globals); // recurse
2316
2317 bq = bq.replace(/(^|\n)/g, '$1 ');
2318 // These leading spaces screw with <pre> content, so we need to fix that:
2319 bq = bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
2320 var pre = m1;
2321 // attacklab: hack around Konqueror 3.5.4 bug:
2322 pre = pre.replace(/^ /mg, '¨0');
2323 pre = pre.replace(/¨0/g, '');
2324 return pre;
2325 });
2326
2327 return showdown.subParser('makehtml.hashBlock')('<blockquote>\n' + bq + '\n</blockquote>', options, globals);
2328 });
2329
2330 text = globals.converter._dispatch('makehtml.blockQuotes.after', text, options, globals).getText();
2331 return text;
2332 });
2333
2334 /**
2335 * Process Markdown `<pre><code>` blocks.
2336 */
2337 showdown.subParser('makehtml.codeBlocks', function (text, options, globals) {
2338 'use strict';
2339
2340 text = globals.converter._dispatch('makehtml.codeBlocks.before', text, options, globals).getText();
2341
2342 // sentinel workarounds for lack of \A and \Z, safari\khtml bug
2343 text += '¨0';
2344
2345 var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=¨0))/g;
2346 text = text.replace(pattern, function (wholeMatch, m1, m2) {
2347 var codeblock = m1,
2348 nextChar = m2,
2349 end = '\n';
2350
2351 codeblock = showdown.subParser('makehtml.outdent')(codeblock, options, globals);
2352 codeblock = showdown.subParser('makehtml.encodeCode')(codeblock, options, globals);
2353 codeblock = showdown.subParser('makehtml.detab')(codeblock, options, globals);
2354 codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
2355 codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
2356
2357 if (options.omitExtraWLInCodeBlocks) {
2358 end = '';
2359 }
2360
2361 codeblock = '<pre><code>' + codeblock + end + '</code></pre>';
2362
2363 return showdown.subParser('makehtml.hashBlock')(codeblock, options, globals) + nextChar;
2364 });
2365
2366 // strip sentinel
2367 text = text.replace(/¨0/, '');
2368
2369 text = globals.converter._dispatch('makehtml.codeBlocks.after', text, options, globals).getText();
2370 return text;
2371 });
2372
2373 /**
2374 *
2375 * * Backtick quotes are used for <code></code> spans.
2376 *
2377 * * You can use multiple backticks as the delimiters if you want to
2378 * include literal backticks in the code span. So, this input:
2379 *
2380 * Just type ``foo `bar` baz`` at the prompt.
2381 *
2382 * Will translate to:
2383 *
2384 * <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
2385 *
2386 * There's no arbitrary limit to the number of backticks you
2387 * can use as delimters. If you need three consecutive backticks
2388 * in your code, use four for delimiters, etc.
2389 *
2390 * * You can use spaces to get literal backticks at the edges:
2391 *
2392 * ... type `` `bar` `` ...
2393 *
2394 * Turns to:
2395 *
2396 * ... type <code>`bar`</code> ...
2397 */
2398 showdown.subParser('makehtml.codeSpans', function (text, options, globals) {
2399 'use strict';
2400
2401 text = globals.converter._dispatch('makehtml.codeSpans.before', text, options, globals).getText();
2402
2403 if (typeof(text) === 'undefined') {
2404 text = '';
2405 }
2406 text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
2407 function (wholeMatch, m1, m2, m3) {
2408 var c = m3;
2409 c = c.replace(/^([ \t]*)/g, ''); // leading whitespace
2410 c = c.replace(/[ \t]*$/g, ''); // trailing whitespace
2411 c = showdown.subParser('makehtml.encodeCode')(c, options, globals);
2412 c = m1 + '<code>' + c + '</code>';
2413 c = showdown.subParser('makehtml.hashHTMLSpans')(c, options, globals);
2414 return c;
2415 }
2416 );
2417
2418 text = globals.converter._dispatch('makehtml.codeSpans.after', text, options, globals).getText();
2419 return text;
2420 });
2421
2422 /**
2423 * Create a full HTML document from the processed markdown
2424 */
2425 showdown.subParser('makehtml.completeHTMLDocument', function (text, options, globals) {
2426 'use strict';
2427
2428 if (!options.completeHTMLDocument) {
2429 return text;
2430 }
2431
2432 text = globals.converter._dispatch('makehtml.completeHTMLDocument.before', text, options, globals).getText();
2433
2434 var doctype = 'html',
2435 doctypeParsed = '<!DOCTYPE HTML>\n',
2436 title = '',
2437 charset = '<meta charset="utf-8">\n',
2438 lang = '',
2439 metadata = '';
2440
2441 if (typeof globals.metadata.parsed.doctype !== 'undefined') {
2442 doctypeParsed = '<!DOCTYPE ' + globals.metadata.parsed.doctype + '>\n';
2443 doctype = globals.metadata.parsed.doctype.toString().toLowerCase();
2444 if (doctype === 'html' || doctype === 'html5') {
2445 charset = '<meta charset="utf-8">';
2446 }
2447 }
2448
2449 for (var meta in globals.metadata.parsed) {
2450 if (globals.metadata.parsed.hasOwnProperty(meta)) {
2451 switch (meta.toLowerCase()) {
2452 case 'doctype':
2453 break;
2454
2455 case 'title':
2456 title = '<title>' + globals.metadata.parsed.title + '</title>\n';
2457 break;
2458
2459 case 'charset':
2460 if (doctype === 'html' || doctype === 'html5') {
2461 charset = '<meta charset="' + globals.metadata.parsed.charset + '">\n';
2462 } else {
2463 charset = '<meta name="charset" content="' + globals.metadata.parsed.charset + '">\n';
2464 }
2465 break;
2466
2467 case 'language':
2468 case 'lang':
2469 lang = ' lang="' + globals.metadata.parsed[meta] + '"';
2470 metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
2471 break;
2472
2473 default:
2474 metadata += '<meta name="' + meta + '" content="' + globals.metadata.parsed[meta] + '">\n';
2475 }
2476 }
2477 }
2478
2479 text = doctypeParsed + '<html' + lang + '>\n<head>\n' + title + charset + metadata + '</head>\n<body>\n' + text.trim() + '\n</body>\n</html>';
2480
2481 text = globals.converter._dispatch('makehtml.completeHTMLDocument.after', text, options, globals).getText();
2482 return text;
2483 });
2484
2485 /**
2486 * Convert all tabs to spaces
2487 */
2488 showdown.subParser('makehtml.detab', function (text, options, globals) {
2489 'use strict';
2490 text = globals.converter._dispatch('makehtml.detab.before', text, options, globals).getText();
2491
2492 // expand first n-1 tabs
2493 text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width
2494
2495 // replace the nth with two sentinels
2496 text = text.replace(/\t/g, '¨A¨B');
2497
2498 // use the sentinel to anchor our regex so it doesn't explode
2499 text = text.replace(/¨B(.+?)¨A/g, function (wholeMatch, m1) {
2500 var leadingText = m1,
2501 numSpaces = 4 - leadingText.length % 4; // g_tab_width
2502
2503 // there *must* be a better way to do this:
2504 for (var i = 0; i < numSpaces; i++) {
2505 leadingText += ' ';
2506 }
2507
2508 return leadingText;
2509 });
2510
2511 // clean up sentinels
2512 text = text.replace(/¨A/g, ' '); // g_tab_width
2513 text = text.replace(/¨B/g, '');
2514
2515 text = globals.converter._dispatch('makehtml.detab.after', text, options, globals).getText();
2516 return text;
2517 });
2518
2519 showdown.subParser('makehtml.ellipsis', function (text, options, globals) {
2520 'use strict';
2521
2522 text = globals.converter._dispatch('makehtml.ellipsis.before', text, options, globals).getText();
2523
2524 text = text.replace(/\.\.\./g, '…');
2525
2526 text = globals.converter._dispatch('makehtml.ellipsis.after', text, options, globals).getText();
2527
2528 return text;
2529 });
2530
2531 /**
2532 * These are all the transformations that occur *within* block-level
2533 * tags like paragraphs, headers, and list items.
2534 */
2535 showdown.subParser('makehtml.emoji', function (text, options, globals) {
2536 'use strict';
2537
2538 if (!options.emoji) {
2539 return text;
2540 }
2541
2542 text = globals.converter._dispatch('makehtml.emoji.before', text, options, globals).getText();
2543
2544 var emojiRgx = /:([\S]+?):/g;
2545
2546 text = text.replace(emojiRgx, function (wm, emojiCode) {
2547 if (showdown.helper.emojis.hasOwnProperty(emojiCode)) {
2548 return showdown.helper.emojis[emojiCode];
2549 }
2550 return wm;
2551 });
2552
2553 text = globals.converter._dispatch('makehtml.emoji.after', text, options, globals).getText();
2554
2555 return text;
2556 });
2557
2558 /**
2559 * Smart processing for ampersands and angle brackets that need to be encoded.
2560 */
2561 showdown.subParser('makehtml.encodeAmpsAndAngles', function (text, options, globals) {
2562 'use strict';
2563 text = globals.converter._dispatch('makehtml.encodeAmpsAndAngles.before', text, options, globals).getText();
2564
2565 // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
2566 // http://bumppo.net/projects/amputator/
2567 text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&amp;');
2568
2569 // Encode naked <'s
2570 text = text.replace(/<(?![a-z\/?$!])/gi, '&lt;');
2571
2572 // Encode <
2573 text = text.replace(/</g, '&lt;');
2574
2575 // Encode >
2576 text = text.replace(/>/g, '&gt;');
2577
2578 text = globals.converter._dispatch('makehtml.encodeAmpsAndAngles.after', text, options, globals).getText();
2579 return text;
2580 });
2581
2582 /**
2583 * Returns the string, with after processing the following backslash escape sequences.
2584 *
2585 * attacklab: The polite way to do this is with the new escapeCharacters() function:
2586 *
2587 * text = escapeCharacters(text,"\\",true);
2588 * text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
2589 *
2590 * ...but we're sidestepping its use of the (slow) RegExp constructor
2591 * as an optimization for Firefox. This function gets called a LOT.
2592 */
2593 showdown.subParser('makehtml.encodeBackslashEscapes', function (text, options, globals) {
2594 'use strict';
2595 text = globals.converter._dispatch('makehtml.encodeBackslashEscapes.before', text, options, globals).getText();
2596
2597 text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback);
2598 text = text.replace(/\\([`*_{}\[\]()>#+.!~=|:-])/g, showdown.helper.escapeCharactersCallback);
2599
2600 text = globals.converter._dispatch('makehtml.encodeBackslashEscapes.after', text, options, globals).getText();
2601 return text;
2602 });
2603
2604 /**
2605 * Encode/escape certain characters inside Markdown code runs.
2606 * The point is that in code, these characters are literals,
2607 * and lose their special Markdown meanings.
2608 */
2609 showdown.subParser('makehtml.encodeCode', function (text, options, globals) {
2610 'use strict';
2611
2612 text = globals.converter._dispatch('makehtml.encodeCode.before', text, options, globals).getText();
2613
2614 // Encode all ampersands; HTML entities are not
2615 // entities within a Markdown code span.
2616 text = text
2617 .replace(/&/g, '&amp;')
2618 // Do the angle bracket song and dance:
2619 .replace(/</g, '&lt;')
2620 .replace(/>/g, '&gt;')
2621 // Now, escape characters that are magic in Markdown:
2622 .replace(/([*_{}\[\]\\=~-])/g, showdown.helper.escapeCharactersCallback);
2623
2624 text = globals.converter._dispatch('makehtml.encodeCode.after', text, options, globals).getText();
2625 return text;
2626 });
2627
2628 /**
2629 * Within tags -- meaning between < and > -- encode [\ ` * _ ~ =] so they
2630 * don't conflict with their use in Markdown for code, italics and strong.
2631 */
2632 showdown.subParser('makehtml.escapeSpecialCharsWithinTagAttributes', function (text, options, globals) {
2633 'use strict';
2634 text = globals.converter._dispatch('makehtml.escapeSpecialCharsWithinTagAttributes.before', text, options, globals).getText();
2635
2636 // Build a regex to find HTML tags.
2637 var tags = /<\/?[a-z\d_:-]+(?:[\s]+[\s\S]+?)?>/gi,
2638 comments = /<!(--(?:(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--)>/gi;
2639
2640 text = text.replace(tags, function (wholeMatch) {
2641 return wholeMatch
2642 .replace(/(.)<\/?code>(?=.)/g, '$1`')
2643 .replace(/([\\`*_~=|])/g, showdown.helper.escapeCharactersCallback);
2644 });
2645
2646 text = text.replace(comments, function (wholeMatch) {
2647 return wholeMatch
2648 .replace(/([\\`*_~=|])/g, showdown.helper.escapeCharactersCallback);
2649 });
2650
2651 text = globals.converter._dispatch('makehtml.escapeSpecialCharsWithinTagAttributes.after', text, options, globals).getText();
2652 return text;
2653 });
2654
2655 /**
2656 * Handle github codeblocks prior to running HashHTML so that
2657 * HTML contained within the codeblock gets escaped properly
2658 * Example:
2659 * ```ruby
2660 * def hello_world(x)
2661 * puts "Hello, #{x}"
2662 * end
2663 * ```
2664 */
2665 showdown.subParser('makehtml.githubCodeBlocks', function (text, options, globals) {
2666 'use strict';
2667
2668 // early exit if option is not enabled
2669 if (!options.ghCodeBlocks) {
2670 return text;
2671 }
2672
2673 text = globals.converter._dispatch('makehtml.githubCodeBlocks.before', text, options, globals).getText();
2674
2675 text += '¨0';
2676
2677 text = text.replace(/(?:^|\n)(?: {0,3})(```+|~~~+)(?: *)([^\s`~]*)\n([\s\S]*?)\n(?: {0,3})\1/g, function (wholeMatch, delim, language, codeblock) {
2678 var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n';
2679
2680 // First parse the github code block
2681 codeblock = showdown.subParser('makehtml.encodeCode')(codeblock, options, globals);
2682 codeblock = showdown.subParser('makehtml.detab')(codeblock, options, globals);
2683 codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
2684 codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace
2685
2686 codeblock = '<pre><code' + (language ? ' class="' + language + ' language-' + language + '"' : '') + '>' + codeblock + end + '</code></pre>';
2687
2688 codeblock = showdown.subParser('makehtml.hashBlock')(codeblock, options, globals);
2689
2690 // Since GHCodeblocks can be false positives, we need to
2691 // store the primitive text and the parsed text in a global var,
2692 // and then return a token
2693 return '\n\n¨G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
2694 });
2695
2696 // attacklab: strip sentinel
2697 text = text.replace(/¨0/, '');
2698
2699 return globals.converter._dispatch('makehtml.githubCodeBlocks.after', text, options, globals).getText();
2700 });
2701
2702 showdown.subParser('makehtml.hashBlock', function (text, options, globals) {
2703 'use strict';
2704 text = globals.converter._dispatch('makehtml.hashBlock.before', text, options, globals).getText();
2705 text = text.replace(/(^\n+|\n+$)/g, '');
2706 text = '\n\n¨K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n';
2707 text = globals.converter._dispatch('makehtml.hashBlock.after', text, options, globals).getText();
2708 return text;
2709 });
2710
2711 /**
2712 * Hash and escape <code> elements that should not be parsed as markdown
2713 */
2714 showdown.subParser('makehtml.hashCodeTags', function (text, options, globals) {
2715 'use strict';
2716 text = globals.converter._dispatch('makehtml.hashCodeTags.before', text, options, globals).getText();
2717
2718 var repFunc = function (wholeMatch, match, left, right) {
2719 var codeblock = left + showdown.subParser('makehtml.encodeCode')(match, options, globals) + right;
2720 return '¨C' + (globals.gHtmlSpans.push(codeblock) - 1) + 'C';
2721 };
2722
2723 // Hash naked <code>
2724 text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '<code\\b[^>]*>', '</code>', 'gim');
2725
2726 text = globals.converter._dispatch('makehtml.hashCodeTags.after', text, options, globals).getText();
2727 return text;
2728 });
2729
2730 showdown.subParser('makehtml.hashElement', function (text, options, globals) {
2731 'use strict';
2732
2733 return function (wholeMatch, m1) {
2734 var blockText = m1;
2735
2736 // Undo double lines
2737 blockText = blockText.replace(/\n\n/g, '\n');
2738 blockText = blockText.replace(/^\n/, '');
2739
2740 // strip trailing blank lines
2741 blockText = blockText.replace(/\n+$/g, '');
2742
2743 // Replace the element text with a marker ("¨KxK" where x is its key)
2744 blockText = '\n\n¨K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n';
2745
2746 return blockText;
2747 };
2748 });
2749
2750 showdown.subParser('makehtml.hashHTMLBlocks', function (text, options, globals) {
2751 'use strict';
2752 text = globals.converter._dispatch('makehtml.hashHTMLBlocks.before', text, options, globals).getText();
2753
2754 var blockTags = [
2755 'pre',
2756 'div',
2757 'h1',
2758 'h2',
2759 'h3',
2760 'h4',
2761 'h5',
2762 'h6',
2763 'blockquote',
2764 'table',
2765 'dl',
2766 'ol',
2767 'ul',
2768 'script',
2769 'noscript',
2770 'form',
2771 'fieldset',
2772 'iframe',
2773 'math',
2774 'style',
2775 'section',
2776 'header',
2777 'footer',
2778 'nav',
2779 'article',
2780 'aside',
2781 'address',
2782 'audio',
2783 'canvas',
2784 'figure',
2785 'hgroup',
2786 'output',
2787 'video',
2788 'p'
2789 ],
2790 repFunc = function (wholeMatch, match, left, right) {
2791 var txt = wholeMatch;
2792 // check if this html element is marked as markdown
2793 // if so, it's contents should be parsed as markdown
2794 if (left.search(/\bmarkdown\b/) !== -1) {
2795 txt = left + globals.converter.makeHtml(match) + right;
2796 }
2797 return '\n\n¨K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
2798 };
2799
2800 if (options.backslashEscapesHTMLTags) {
2801 // encode backslash escaped HTML tags
2802 text = text.replace(/\\<(\/?[^>]+?)>/g, function (wm, inside) {
2803 return '&lt;' + inside + '&gt;';
2804 });
2805 }
2806
2807 // hash HTML Blocks
2808 for (var i = 0; i < blockTags.length; ++i) {
2809
2810 var opTagPos,
2811 rgx1 = new RegExp('^ {0,3}(<' + blockTags[i] + '\\b[^>]*>)', 'im'),
2812 patLeft = '<' + blockTags[i] + '\\b[^>]*>',
2813 patRight = '</' + blockTags[i] + '>';
2814 // 1. Look for the first position of the first opening HTML tag in the text
2815 while ((opTagPos = showdown.helper.regexIndexOf(text, rgx1)) !== -1) {
2816
2817 // if the HTML tag is \ escaped, we need to escape it and break
2818
2819
2820 //2. Split the text in that position
2821 var subTexts = showdown.helper.splitAtIndex(text, opTagPos),
2822 //3. Match recursively
2823 newSubText1 = showdown.helper.replaceRecursiveRegExp(subTexts[1], repFunc, patLeft, patRight, 'im');
2824
2825 // prevent an infinite loop
2826 if (newSubText1 === subTexts[1]) {
2827 break;
2828 }
2829 text = subTexts[0].concat(newSubText1);
2830 }
2831 }
2832 // HR SPECIAL CASE
2833 text = text.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,
2834 showdown.subParser('makehtml.hashElement')(text, options, globals));
2835
2836 // Special case for standalone HTML comments
2837 text = showdown.helper.replaceRecursiveRegExp(text, function (txt) {
2838 return '\n\n¨K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n';
2839 }, '^ {0,3}<!--', '-->', 'gm');
2840
2841 // PHP and ASP-style processor instructions (<?...?> and <%...%>)
2842 text = text.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,
2843 showdown.subParser('makehtml.hashElement')(text, options, globals));
2844
2845 text = globals.converter._dispatch('makehtml.hashHTMLBlocks.after', text, options, globals).getText();
2846 return text;
2847 });
2848
2849 /**
2850 * Hash span elements that should not be parsed as markdown
2851 */
2852 showdown.subParser('makehtml.hashHTMLSpans', function (text, options, globals) {
2853 'use strict';
2854 text = globals.converter._dispatch('makehtml.hashHTMLSpans.before', text, options, globals).getText();
2855
2856 // Hash Self Closing tags
2857 text = text.replace(/<[^>]+?\/>/gi, function (wm) {
2858 return showdown.helper._hashHTMLSpan(wm, globals);
2859 });
2860
2861 // Hash tags without properties
2862 text = text.replace(/<([^>]+?)>[\s\S]*?<\/\1>/g, function (wm) {
2863 return showdown.helper._hashHTMLSpan(wm, globals);
2864 });
2865
2866 // Hash tags with properties
2867 text = text.replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g, function (wm) {
2868 return showdown.helper._hashHTMLSpan(wm, globals);
2869 });
2870
2871 // Hash self closing tags without />
2872 text = text.replace(/<[^>]+?>/gi, function (wm) {
2873 return showdown.helper._hashHTMLSpan(wm, globals);
2874 });
2875
2876 text = globals.converter._dispatch('makehtml.hashHTMLSpans.after', text, options, globals).getText();
2877 return text;
2878 });
2879
2880 /**
2881 * Unhash HTML spans
2882 */
2883 showdown.subParser('makehtml.unhashHTMLSpans', function (text, options, globals) {
2884 'use strict';
2885 text = globals.converter._dispatch('makehtml.unhashHTMLSpans.before', text, options, globals).getText();
2886
2887 for (var i = 0; i < globals.gHtmlSpans.length; ++i) {
2888 var repText = globals.gHtmlSpans[i],
2889 // limiter to prevent infinite loop (assume 10 as limit for recurse)
2890 limit = 0;
2891
2892 while (/¨C(\d+)C/.test(repText)) {
2893 var num = RegExp.$1;
2894 repText = repText.replace('¨C' + num + 'C', globals.gHtmlSpans[num]);
2895 if (limit === 10) {
2896 console.error('maximum nesting of 10 spans reached!!!');
2897 break;
2898 }
2899 ++limit;
2900 }
2901 text = text.replace('¨C' + i + 'C', repText);
2902 }
2903
2904 text = globals.converter._dispatch('makehtml.unhashHTMLSpans.after', text, options, globals).getText();
2905 return text;
2906 });
2907
2908 /**
2909 * Hash and escape <pre><code> elements that should not be parsed as markdown
2910 */
2911 showdown.subParser('makehtml.hashPreCodeTags', function (text, options, globals) {
2912 'use strict';
2913 text = globals.converter._dispatch('makehtml.hashPreCodeTags.before', text, options, globals).getText();
2914
2915 var repFunc = function (wholeMatch, match, left, right) {
2916 // encode html entities
2917 var codeblock = left + showdown.subParser('makehtml.encodeCode')(match, options, globals) + right;
2918 return '\n\n¨G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n';
2919 };
2920
2921 // Hash <pre><code>
2922 text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^ {0,3}<pre\\b[^>]*>\\s*<code\\b[^>]*>', '^ {0,3}</code>\\s*</pre>', 'gim');
2923
2924 text = globals.converter._dispatch('makehtml.hashPreCodeTags.after', text, options, globals).getText();
2925 return text;
2926 });
2927
2928 showdown.subParser('makehtml.headers', function (text, options, globals) {
2929 'use strict';
2930
2931 text = globals.converter._dispatch('makehtml.headers.before', text, options, globals).getText();
2932
2933 var headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart),
2934
2935 // Set text-style headers:
2936 // Header 1
2937 // ========
2938 //
2939 // Header 2
2940 // --------
2941 //
2942 setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm,
2943 setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm;
2944
2945 text = text.replace(setextRegexH1, function (wholeMatch, m1) {
2946
2947 var spanGamut = showdown.subParser('makehtml.spanGamut')(m1, options, globals),
2948 hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
2949 hLevel = headerLevelStart,
2950 hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
2951 return showdown.subParser('makehtml.hashBlock')(hashBlock, options, globals);
2952 });
2953
2954 text = text.replace(setextRegexH2, function (matchFound, m1) {
2955 var spanGamut = showdown.subParser('makehtml.spanGamut')(m1, options, globals),
2956 hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"',
2957 hLevel = headerLevelStart + 1,
2958 hashBlock = '<h' + hLevel + hID + '>' + spanGamut + '</h' + hLevel + '>';
2959 return showdown.subParser('makehtml.hashBlock')(hashBlock, options, globals);
2960 });
2961
2962 // atx-style headers:
2963 // # Header 1
2964 // ## Header 2
2965 // ## Header 2 with closing hashes ##
2966 // ...
2967 // ###### Header 6
2968 //
2969 var atxStyle = (options.requireSpaceBeforeHeadingText) ? /^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+/gm : /^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm;
2970
2971 text = text.replace(atxStyle, function (wholeMatch, m1, m2) {
2972 var hText = m2;
2973 if (options.customizedHeaderId) {
2974 hText = m2.replace(/\s?\{([^{]+?)}\s*$/, '');
2975 }
2976
2977 var span = showdown.subParser('makehtml.spanGamut')(hText, options, globals),
2978 hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"',
2979 hLevel = headerLevelStart - 1 + m1.length,
2980 header = '<h' + hLevel + hID + '>' + span + '</h' + hLevel + '>';
2981
2982 return showdown.subParser('makehtml.hashBlock')(header, options, globals);
2983 });
2984
2985 function headerId (m) {
2986 var title,
2987 prefix;
2988
2989 // It is separate from other options to allow combining prefix and customized
2990 if (options.customizedHeaderId) {
2991 var match = m.match(/\{([^{]+?)}\s*$/);
2992 if (match && match[1]) {
2993 m = match[1];
2994 }
2995 }
2996
2997 title = m;
2998
2999 // Prefix id to prevent causing inadvertent pre-existing style matches.
3000 if (showdown.helper.isString(options.prefixHeaderId)) {
3001 prefix = options.prefixHeaderId;
3002 } else if (options.prefixHeaderId === true) {
3003 prefix = 'section-';
3004 } else {
3005 prefix = '';
3006 }
3007
3008 if (!options.rawPrefixHeaderId) {
3009 title = prefix + title;
3010 }
3011
3012 if (options.ghCompatibleHeaderId) {
3013 title = title
3014 .replace(/ /g, '-')
3015 // replace previously escaped chars (&, ¨ and $)
3016 .replace(/&amp;/g, '')
3017 .replace(/¨T/g, '')
3018 .replace(/¨D/g, '')
3019 // replace rest of the chars (&~$ are repeated as they might have been escaped)
3020 // borrowed from github's redcarpet (some they should produce similar results)
3021 .replace(/[&+$,\/:;=?@"#{}|^¨~\[\]`\\*)(%.!'<>]/g, '')
3022 .toLowerCase();
3023 } else if (options.rawHeaderId) {
3024 title = title
3025 .replace(/ /g, '-')
3026 // replace previously escaped chars (&, ¨ and $)
3027 .replace(/&amp;/g, '&')
3028 .replace(/¨T/g, '¨')
3029 .replace(/¨D/g, '$')
3030 // replace " and '
3031 .replace(/["']/g, '-')
3032 .toLowerCase();
3033 } else {
3034 title = title
3035 .replace(/[^\w]/g, '')
3036 .toLowerCase();
3037 }
3038
3039 if (options.rawPrefixHeaderId) {
3040 title = prefix + title;
3041 }
3042
3043 if (globals.hashLinkCounts[title]) {
3044 title = title + '-' + (globals.hashLinkCounts[title]++);
3045 } else {
3046 globals.hashLinkCounts[title] = 1;
3047 }
3048 return title;
3049 }
3050
3051 text = globals.converter._dispatch('makehtml.headers.after', text, options, globals).getText();
3052 return text;
3053 });
3054
3055 /**
3056 * Turn Markdown horizontal rule shortcuts into <hr /> tags.
3057 *
3058 * Any 3 or more unindented consecutive hyphens, asterisks or underscores with or without a space beetween them
3059 * in a single line is considered a horizontal rule
3060 */
3061 showdown.subParser('makehtml.horizontalRule', function (text, options, globals) {
3062 'use strict';
3063 text = globals.converter._dispatch('makehtml.horizontalRule.before', text, options, globals).getText();
3064
3065 var key = showdown.subParser('makehtml.hashBlock')('<hr />', options, globals);
3066 text = text.replace(/^ {0,2}( ?-){3,}[ \t]*$/gm, key);
3067 text = text.replace(/^ {0,2}( ?\*){3,}[ \t]*$/gm, key);
3068 text = text.replace(/^ {0,2}( ?_){3,}[ \t]*$/gm, key);
3069
3070 text = globals.converter._dispatch('makehtml.horizontalRule.after', text, options, globals).getText();
3071 return text;
3072 });
3073
3074 /**
3075 * Turn Markdown image shortcuts into <img> tags.
3076 */
3077 showdown.subParser('makehtml.images', function (text, options, globals) {
3078 'use strict';
3079
3080 text = globals.converter._dispatch('makehtml.images.before', text, options, globals).getText();
3081
3082 var inlineRegExp = /!\[([^\]]*?)][ \t]*()\([ \t]?<?([\S]+?(?:\([\S]*?\)[\S]*?)?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
3083 crazyRegExp = /!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,
3084 base64RegExp = /!\[([^\]]*?)][ \t]*()\([ \t]?<?(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,
3085 referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[([\s\S]*?)]()()()()()/g,
3086 refShortcutRegExp = /!\[([^\[\]]+)]()()()()()/g;
3087
3088 function writeImageTagBase64 (wholeMatch, altText, linkId, url, width, height, m5, title) {
3089 url = url.replace(/\s/g, '');
3090 return writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title);
3091 }
3092
3093 function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) {
3094
3095 var gUrls = globals.gUrls,
3096 gTitles = globals.gTitles,
3097 gDims = globals.gDimensions;
3098
3099 linkId = linkId.toLowerCase();
3100
3101 if (!title) {
3102 title = '';
3103 }
3104 // Special case for explicit empty url
3105 if (wholeMatch.search(/\(<?\s*>? ?(['"].*['"])?\)$/m) > -1) {
3106 url = '';
3107
3108 } else if (url === '' || url === null) {
3109 if (linkId === '' || linkId === null) {
3110 // lower-case and turn embedded newlines into spaces
3111 linkId = altText.toLowerCase().replace(/ ?\n/g, ' ');
3112 }
3113 url = '#' + linkId;
3114
3115 if (!showdown.helper.isUndefined(gUrls[linkId])) {
3116 url = gUrls[linkId];
3117 if (!showdown.helper.isUndefined(gTitles[linkId])) {
3118 title = gTitles[linkId];
3119 }
3120 if (!showdown.helper.isUndefined(gDims[linkId])) {
3121 width = gDims[linkId].width;
3122 height = gDims[linkId].height;
3123 }
3124 } else {
3125 return wholeMatch;
3126 }
3127 }
3128
3129 altText = altText
3130 .replace(/"/g, '&quot;')
3131 //altText = showdown.helper.escapeCharacters(altText, '*_', false);
3132 .replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
3133 //url = showdown.helper.escapeCharacters(url, '*_', false);
3134 url = url.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
3135 var result = '<img src="' + url + '" alt="' + altText + '"';
3136
3137 if (title && showdown.helper.isString(title)) {
3138 title = title
3139 .replace(/"/g, '&quot;')
3140 //title = showdown.helper.escapeCharacters(title, '*_', false);
3141 .replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
3142 result += ' title="' + title + '"';
3143 }
3144
3145 if (width && height) {
3146 width = (width === '*') ? 'auto' : width;
3147 height = (height === '*') ? 'auto' : height;
3148
3149 result += ' width="' + width + '"';
3150 result += ' height="' + height + '"';
3151 }
3152
3153 result += ' />';
3154
3155 return result;
3156 }
3157
3158 // First, handle reference-style labeled images: ![alt text][id]
3159 text = text.replace(referenceRegExp, writeImageTag);
3160
3161 // Next, handle inline images: ![alt text](url =<width>x<height> "optional title")
3162
3163 // base64 encoded images
3164 text = text.replace(base64RegExp, writeImageTagBase64);
3165
3166 // cases with crazy urls like ./image/cat1).png
3167 text = text.replace(crazyRegExp, writeImageTag);
3168
3169 // normal cases
3170 text = text.replace(inlineRegExp, writeImageTag);
3171
3172 // handle reference-style shortcuts: ![img text]
3173 text = text.replace(refShortcutRegExp, writeImageTag);
3174
3175 text = globals.converter._dispatch('makehtml.images.after', text, options, globals).getText();
3176 return text;
3177 });
3178
3179 showdown.subParser('makehtml.italicsAndBold', function (text, options, globals) {
3180 'use strict';
3181
3182 text = globals.converter._dispatch('makehtml.italicsAndBold.before', text, options, globals).getText();
3183
3184 // it's faster to have 3 separate regexes for each case than have just one
3185 // because of backtracing, in some cases, it could lead to an exponential effect
3186 // called "catastrophic backtrace". Ominous!
3187
3188 function parseInside (txt, left, right) {
3189 return left + txt + right;
3190 }
3191
3192 // Parse underscores
3193 if (options.literalMidWordUnderscores) {
3194 text = text.replace(/\b___(\S[\s\S]*?)___\b/g, function (wm, txt) {
3195 return parseInside (txt, '<strong><em>', '</em></strong>');
3196 });
3197 text = text.replace(/\b__(\S[\s\S]*?)__\b/g, function (wm, txt) {
3198 return parseInside (txt, '<strong>', '</strong>');
3199 });
3200 text = text.replace(/\b_(\S[\s\S]*?)_\b/g, function (wm, txt) {
3201 return parseInside (txt, '<em>', '</em>');
3202 });
3203 } else {
3204 text = text.replace(/___(\S[\s\S]*?)___/g, function (wm, m) {
3205 return (/\S$/.test(m)) ? parseInside (m, '<strong><em>', '</em></strong>') : wm;
3206 });
3207 text = text.replace(/__(\S[\s\S]*?)__/g, function (wm, m) {
3208 return (/\S$/.test(m)) ? parseInside (m, '<strong>', '</strong>') : wm;
3209 });
3210 text = text.replace(/_([^\s_][\s\S]*?)_/g, function (wm, m) {
3211 // !/^_[^_]/.test(m) - test if it doesn't start with __ (since it seems redundant, we removed it)
3212 return (/\S$/.test(m)) ? parseInside (m, '<em>', '</em>') : wm;
3213 });
3214 }
3215
3216 // Now parse asterisks
3217 /*
3218 if (options.literalMidWordAsterisks) {
3219 text = text.replace(/([^*]|^)\B\*\*\*(\S[\s\S]+?)\*\*\*\B(?!\*)/g, function (wm, lead, txt) {
3220 return parseInside (txt, lead + '<strong><em>', '</em></strong>');
3221 });
3222 text = text.replace(/([^*]|^)\B\*\*(\S[\s\S]+?)\*\*\B(?!\*)/g, function (wm, lead, txt) {
3223 return parseInside (txt, lead + '<strong>', '</strong>');
3224 });
3225 text = text.replace(/([^*]|^)\B\*(\S[\s\S]+?)\*\B(?!\*)/g, function (wm, lead, txt) {
3226 return parseInside (txt, lead + '<em>', '</em>');
3227 });
3228 } else {
3229 */
3230 text = text.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g, function (wm, m) {
3231 return (/\S$/.test(m)) ? parseInside (m, '<strong><em>', '</em></strong>') : wm;
3232 });
3233 text = text.replace(/\*\*(\S[\s\S]*?)\*\*/g, function (wm, m) {
3234 return (/\S$/.test(m)) ? parseInside (m, '<strong>', '</strong>') : wm;
3235 });
3236 text = text.replace(/\*([^\s*][\s\S]*?)\*/g, function (wm, m) {
3237 // !/^\*[^*]/.test(m) - test if it doesn't start with ** (since it seems redundant, we removed it)
3238 return (/\S$/.test(m)) ? parseInside (m, '<em>', '</em>') : wm;
3239 });
3240 //}
3241
3242 text = globals.converter._dispatch('makehtml.italicsAndBold.after', text, options, globals).getText();
3243 return text;
3244 });
3245
3246 ////
3247 // makehtml/links.js
3248 // Copyright (c) 2018 ShowdownJS
3249 //
3250 // Transforms MD links into `<a>` html anchors
3251 //
3252 // A link contains link text (the visible text), a link destination (the URI that is the link destination), and
3253 // optionally a link title. There are two basic kinds of links in Markdown.
3254 // In inline links the destination and title are given immediately after the link text.
3255 // In reference links the destination and title are defined elsewhere in the document.
3256 //
3257 // ***Author:***
3258 // - Estevão Soares dos Santos (Tivie) <https://github.com/tivie>
3259 ////
3260
3261 (function () {
3262 /**
3263 * Helper function: Wrapper function to pass as second replace parameter
3264 *
3265 * @param {RegExp} rgx
3266 * @param {string} evtRootName
3267 * @param {{}} options
3268 * @param {{}} globals
3269 * @returns {Function}
3270 */
3271 function replaceAnchorTag (rgx, evtRootName, options, globals, emptyCase) {
3272 emptyCase = !!emptyCase;
3273 return function (wholeMatch, text, id, url, m5, m6, title) {
3274 // bail we we find 2 newlines somewhere
3275 if (/\n\n/.test(wholeMatch)) {
3276 return wholeMatch;
3277 }
3278
3279 var evt = createEvent(rgx, evtRootName + '.captureStart', wholeMatch, text, id, url, title, options, globals);
3280 return writeAnchorTag(evt, options, globals, emptyCase);
3281 };
3282 }
3283
3284 /**
3285 * TODO Normalize this
3286 * Helper function: Create a capture event
3287 * @param {RegExp} rgx
3288 * @param {String} evtName Event name
3289 * @param {String} wholeMatch
3290 * @param {String} text
3291 * @param {String} id
3292 * @param {String} url
3293 * @param {String} title
3294 * @param {{}} options
3295 * @param {{}} globals
3296 * @returns {showdown.helper.Event|*}
3297 */
3298 function createEvent (rgx, evtName, wholeMatch, text, id, url, title, options, globals) {
3299 return globals.converter._dispatch(evtName, wholeMatch, options, globals, {
3300 regexp: rgx,
3301 matches: {
3302 wholeMatch: wholeMatch,
3303 text: text,
3304 id: id,
3305 url: url,
3306 title: title
3307 }
3308 });
3309 }
3310
3311 /**
3312 * Helper Function: Normalize and write an anchor tag based on passed parameters
3313 * @param evt
3314 * @param options
3315 * @param globals
3316 * @param {boolean} emptyCase
3317 * @returns {string}
3318 */
3319 function writeAnchorTag (evt, options, globals, emptyCase) {
3320
3321 var wholeMatch = evt.getMatches().wholeMatch;
3322 var text = evt.getMatches().text;
3323 var id = evt.getMatches().id;
3324 var url = evt.getMatches().url;
3325 var title = evt.getMatches().title;
3326 var target = '';
3327
3328 if (!title) {
3329 title = '';
3330 }
3331 id = (id) ? id.toLowerCase() : '';
3332
3333 if (emptyCase) {
3334 url = '';
3335 } else if (!url) {
3336 if (!id) {
3337 // lower-case and turn embedded newlines into spaces
3338 id = text.toLowerCase().replace(/ ?\n/g, ' ');
3339 }
3340 url = '#' + id;
3341
3342 if (!showdown.helper.isUndefined(globals.gUrls[id])) {
3343 url = globals.gUrls[id];
3344 if (!showdown.helper.isUndefined(globals.gTitles[id])) {
3345 title = globals.gTitles[id];
3346 }
3347 } else {
3348 return wholeMatch;
3349 }
3350 }
3351 //url = showdown.helper.escapeCharacters(url, '*_:~', false); // replaced line to improve performance
3352 url = url.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
3353
3354 if (title !== '' && title !== null) {
3355 title = title.replace(/"/g, '&quot;');
3356 //title = showdown.helper.escapeCharacters(title, '*_', false); // replaced line to improve performance
3357 title = title.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
3358 title = ' title="' + title + '"';
3359 }
3360
3361 // optionLinksInNewWindow only applies
3362 // to external links. Hash links (#) open in same page
3363 if (options.openLinksInNewWindow && !/^#/.test(url)) {
3364 // escaped _
3365 target = ' target="¨E95Eblank"';
3366 }
3367
3368 // Text can be a markdown element, so we run through the appropriate parsers
3369 text = showdown.subParser('makehtml.codeSpans')(text, options, globals);
3370 text = showdown.subParser('makehtml.emoji')(text, options, globals);
3371 text = showdown.subParser('makehtml.underline')(text, options, globals);
3372 text = showdown.subParser('makehtml.italicsAndBold')(text, options, globals);
3373 text = showdown.subParser('makehtml.strikethrough')(text, options, globals);
3374 text = showdown.subParser('makehtml.ellipsis')(text, options, globals);
3375 text = showdown.subParser('makehtml.hashHTMLSpans')(text, options, globals);
3376
3377 //evt = createEvent(rgx, evtRootName + '.captureEnd', wholeMatch, text, id, url, title, options, globals);
3378
3379 var result = '<a href="' + url + '"' + title + target + '>' + text + '</a>';
3380
3381 //evt = createEvent(rgx, evtRootName + '.beforeHash', wholeMatch, text, id, url, title, options, globals);
3382
3383 result = showdown.subParser('makehtml.hashHTMLSpans')(result, options, globals);
3384
3385 return result;
3386 }
3387
3388 var evtRootName = 'makehtml.links';
3389
3390 /**
3391 * Turn Markdown link shortcuts into XHTML <a> tags.
3392 */
3393 showdown.subParser('makehtml.links', function (text, options, globals) {
3394
3395 text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
3396
3397 // 1. Handle reference-style links: [link text] [id]
3398 text = showdown.subParser('makehtml.links.reference')(text, options, globals);
3399
3400 // 2. Handle inline-style links: [link text](url "optional title")
3401 text = showdown.subParser('makehtml.links.inline')(text, options, globals);
3402
3403 // 3. Handle reference-style shortcuts: [link text]
3404 // These must come last in case there's a [link text][1] or [link text](/foo)
3405 text = showdown.subParser('makehtml.links.referenceShortcut')(text, options, globals);
3406
3407 // 4. Handle angle brackets links -> `<http://example.com/>`
3408 // Must come after links, because you can use < and > delimiters in inline links like [this](<url>).
3409 text = showdown.subParser('makehtml.links.angleBrackets')(text, options, globals);
3410
3411 // 5. Handle GithubMentions (if option is enabled)
3412 text = showdown.subParser('makehtml.links.ghMentions')(text, options, globals);
3413
3414 // 6. Handle <a> tags and img tags
3415 text = text.replace(/<a\s[^>]*>[\s\S]*<\/a>/g, function (wholeMatch) {
3416 return showdown.helper._hashHTMLSpan(wholeMatch, globals);
3417 });
3418
3419 text = text.replace(/<img\s[^>]*\/?>/g, function (wholeMatch) {
3420 return showdown.helper._hashHTMLSpan(wholeMatch, globals);
3421 });
3422
3423 // 7. Handle naked links (if option is enabled)
3424 text = showdown.subParser('makehtml.links.naked')(text, options, globals);
3425
3426 text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
3427 return text;
3428 });
3429
3430 /**
3431 * TODO WRITE THIS DOCUMENTATION
3432 */
3433 showdown.subParser('makehtml.links.inline', function (text, options, globals) {
3434 var evtRootName = evtRootName + '.inline';
3435
3436 text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
3437
3438 // 1. Look for empty cases: []() and [empty]() and []("title")
3439 var rgxEmpty = /\[(.*?)]()()()()\(<? ?>? ?(?:["'](.*)["'])?\)/g;
3440 text = text.replace(rgxEmpty, replaceAnchorTag(rgxEmpty, evtRootName, options, globals, true));
3441
3442 // 2. Look for cases with crazy urls like ./image/cat1).png
3443 var rgxCrazy = /\[((?:\[[^\]]*]|[^\[\]])*)]()\s?\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g;
3444 text = text.replace(rgxCrazy, replaceAnchorTag(rgxCrazy, evtRootName, options, globals));
3445
3446 // 3. inline links with no title or titles wrapped in ' or ":
3447 // [text](url.com) || [text](<url.com>) || [text](url.com "title") || [text](<url.com> "title")
3448 //var rgx2 = /\[[ ]*[\s]?[ ]*([^\n\[\]]*?)[ ]*[\s]?[ ]*] ?()\(<?[ ]*[\s]?[ ]*([^\s'"]*)>?(?:[ ]*[\n]?[ ]*()(['"])(.*?)\5)?[ ]*[\s]?[ ]*\)/; // this regex is too slow!!!
3449 var rgx2 = /\[([\S ]*?)]\s?()\( *<?([^\s'"]*?(?:\([\S]*?\)[\S]*?)?)>?\s*(?:()(['"])(.*?)\5)? *\)/g;
3450 text = text.replace(rgx2, replaceAnchorTag(rgx2, evtRootName, options, globals));
3451
3452 // 4. inline links with titles wrapped in (): [foo](bar.com (title))
3453 var rgx3 = /\[([\S ]*?)]\s?()\( *<?([^\s'"]*?(?:\([\S]*?\)[\S]*?)?)>?\s+()()\((.*?)\) *\)/g;
3454 text = text.replace(rgx3, replaceAnchorTag(rgx3, evtRootName, options, globals));
3455
3456 text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
3457
3458 return text;
3459 });
3460
3461 /**
3462 * TODO WRITE THIS DOCUMENTATION
3463 */
3464 showdown.subParser('makehtml.links.reference', function (text, options, globals) {
3465 var evtRootName = evtRootName + '.reference';
3466
3467 text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
3468
3469 var rgx = /\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g;
3470 text = text.replace(rgx, replaceAnchorTag(rgx, evtRootName, options, globals));
3471
3472 text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
3473
3474 return text;
3475 });
3476
3477 /**
3478 * TODO WRITE THIS DOCUMENTATION
3479 */
3480 showdown.subParser('makehtml.links.referenceShortcut', function (text, options, globals) {
3481 var evtRootName = evtRootName + '.referenceShortcut';
3482
3483 text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
3484
3485 var rgx = /\[([^\[\]]+)]()()()()()/g;
3486 text = text.replace(rgx, replaceAnchorTag(rgx, evtRootName, options, globals));
3487
3488 text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
3489
3490 return text;
3491 });
3492
3493 /**
3494 * TODO WRITE THIS DOCUMENTATION
3495 */
3496 showdown.subParser('makehtml.links.ghMentions', function (text, options, globals) {
3497 var evtRootName = evtRootName + 'ghMentions';
3498
3499 if (!options.ghMentions) {
3500 return text;
3501 }
3502
3503 text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
3504
3505 var rgx = /(^|\s)(\\)?(@([a-z\d]+(?:[a-z\d._-]+?[a-z\d]+)*))/gi;
3506
3507 text = text.replace(rgx, function (wholeMatch, st, escape, mentions, username) {
3508 // bail if the mentions was escaped
3509 if (escape === '\\') {
3510 return st + mentions;
3511 }
3512
3513 // check if options.ghMentionsLink is a string
3514 // TODO Validation should be done at initialization not at runtime
3515 if (!showdown.helper.isString(options.ghMentionsLink)) {
3516 throw new Error('ghMentionsLink option must be a string');
3517 }
3518 var url = options.ghMentionsLink.replace(/{u}/g, username);
3519 var evt = createEvent(rgx, evtRootName + '.captureStart', wholeMatch, mentions, null, url, null, options, globals);
3520 // captureEnd Event is triggered inside writeAnchorTag function
3521 return st + writeAnchorTag(evt, options, globals);
3522 });
3523
3524 text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
3525
3526 return text;
3527 });
3528
3529 /**
3530 * TODO WRITE THIS DOCUMENTATION
3531 */
3532 showdown.subParser('makehtml.links.angleBrackets', function (text, options, globals) {
3533 var evtRootName = 'makehtml.links.angleBrackets';
3534
3535 text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
3536
3537 // 1. Parse links first
3538 var urlRgx = /<(((?:https?|ftp):\/\/|www\.)[^'">\s]+)>/gi;
3539 text = text.replace(urlRgx, function (wholeMatch, url, urlStart) {
3540 var text = url;
3541 url = (urlStart === 'www.') ? 'http://' + url : url;
3542 var evt = createEvent(urlRgx, evtRootName + '.captureStart', wholeMatch, text, null, url, null, options, globals);
3543 return writeAnchorTag(evt, options, globals);
3544 });
3545
3546 // 2. Then Mail Addresses
3547 var mailRgx = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi;
3548 text = text.replace(mailRgx, function (wholeMatch, mail) {
3549 var url = 'mailto:';
3550 mail = showdown.subParser('makehtml.unescapeSpecialChars')(mail, options, globals);
3551 if (options.encodeEmails) {
3552 url = showdown.helper.encodeEmailAddress(url + mail);
3553 mail = showdown.helper.encodeEmailAddress(mail);
3554 } else {
3555 url = url + mail;
3556 }
3557 var evt = createEvent(mailRgx, evtRootName + '.captureStart', wholeMatch, mail, null, url, null, options, globals);
3558 return writeAnchorTag(evt, options, globals);
3559 });
3560
3561 text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
3562 return text;
3563 });
3564
3565 /**
3566 * TODO MAKE THIS WORK (IT'S NOT ACTIVATED)
3567 * TODO WRITE THIS DOCUMENTATION
3568 */
3569 showdown.subParser('makehtml.links.naked', function (text, options, globals) {
3570 if (!options.simplifiedAutoLink) {
3571 return text;
3572 }
3573
3574 var evtRootName = 'makehtml.links.naked';
3575
3576 text = globals.converter._dispatch(evtRootName + '.start', text, options, globals).getText();
3577
3578 // 2. Now we check for
3579 // we also include leading markdown magic chars [_*~] for cases like __https://www.google.com/foobar__
3580 var urlRgx = /([_*~]*?)(((?:https?|ftp):\/\/|www\.)[^\s<>"'`´.-][^\s<>"'`´]*?\.[a-z\d.]+[^\s<>"']*)\1/gi;
3581 text = text.replace(urlRgx, function (wholeMatch, leadingMDChars, url, urlPrefix) {
3582
3583 // we now will start traversing the url from the front to back, looking for punctuation chars [_*~,;:.!?\)\]]
3584 var len = url.length;
3585 var suffix = '';
3586 for (var i = len - 1; i >= 0; --i) {
3587 var char = url.charAt(i);
3588
3589 if (/[_*~,;:.!?]/.test(char)) {
3590 // it's a punctuation char
3591 // we remove it from the url
3592 url = url.slice(0, -1);
3593 // and prepend it to the suffix
3594 suffix = char + suffix;
3595 } else if (/\)/.test(char)) {
3596 var opPar = url.match(/\(/g) || [];
3597 var clPar = url.match(/\)/g);
3598
3599 // it's a curved parenthesis so we need to check for "balance" (kinda)
3600 if (opPar.length < clPar.length) {
3601 // there are more closing Parenthesis than opening so chop it!!!!!
3602 url = url.slice(0, -1);
3603 // and prepend it to the suffix
3604 suffix = char + suffix;
3605 } else {
3606 // it's (kinda) balanced so our work is done
3607 break;
3608 }
3609 } else if (/]/.test(char)) {
3610 var opPar2 = url.match(/\[/g) || [];
3611 var clPar2 = url.match(/\]/g);
3612 // it's a squared parenthesis so we need to check for "balance" (kinda)
3613 if (opPar2.length < clPar2.length) {
3614 // there are more closing Parenthesis than opening so chop it!!!!!
3615 url = url.slice(0, -1);
3616 // and prepend it to the suffix
3617 suffix = char + suffix;
3618 } else {
3619 // it's (kinda) balanced so our work is done
3620 break;
3621 }
3622 } else {
3623 // it's not a punctuation or a parenthesis so our work is done
3624 break;
3625 }
3626 }
3627
3628 // we copy the treated url to the text variable
3629 var text = url;
3630 // finally, if it's a www shortcut, we prepend http
3631 url = (urlPrefix === 'www.') ? 'http://' + url : url;
3632
3633 // url part is done so let's take care of text now
3634 // we need to escape the text (because of links such as www.example.com/foo__bar__baz)
3635 text = text.replace(showdown.helper.regexes.asteriskDashTildeAndColon, showdown.helper.escapeCharactersCallback);
3636
3637 // finally we dispatch the event
3638 var evt = createEvent(urlRgx, evtRootName + '.captureStart', wholeMatch, text, null, url, null, options, globals);
3639
3640 // and return the link tag, with the leadingMDChars and suffix. The leadingMDChars are added at the end too because
3641 // we consumed those characters in the regexp
3642 return leadingMDChars + writeAnchorTag(evt, options, globals) + suffix + leadingMDChars;
3643 });
3644
3645 // 2. Then mails
3646 var mailRgx = /(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gmi;
3647 text = text.replace(mailRgx, function (wholeMatch, leadingChar, mail) {
3648 var url = 'mailto:';
3649 mail = showdown.subParser('makehtml.unescapeSpecialChars')(mail, options, globals);
3650 if (options.encodeEmails) {
3651 url = showdown.helper.encodeEmailAddress(url + mail);
3652 mail = showdown.helper.encodeEmailAddress(mail);
3653 } else {
3654 url = url + mail;
3655 }
3656 var evt = createEvent(mailRgx, evtRootName + '.captureStart', wholeMatch, mail, null, url, null, options, globals);
3657 return leadingChar + writeAnchorTag(evt, options, globals);
3658 });
3659
3660
3661 text = globals.converter._dispatch(evtRootName + '.end', text, options, globals).getText();
3662 return text;
3663 });
3664 })();
3665
3666 /**
3667 * Form HTML ordered (numbered) and unordered (bulleted) lists.
3668 */
3669 showdown.subParser('makehtml.lists', function (text, options, globals) {
3670 'use strict';
3671
3672 /**
3673 * Process the contents of a single ordered or unordered list, splitting it
3674 * into individual list items.
3675 * @param {string} listStr
3676 * @param {boolean} trimTrailing
3677 * @returns {string}
3678 */
3679 function processListItems (listStr, trimTrailing) {
3680 // The $g_list_level global keeps track of when we're inside a list.
3681 // Each time we enter a list, we increment it; when we leave a list,
3682 // we decrement. If it's zero, we're not in a list anymore.
3683 //
3684 // We do this because when we're not inside a list, we want to treat
3685 // something like this:
3686 //
3687 // I recommend upgrading to version
3688 // 8. Oops, now this line is treated
3689 // as a sub-list.
3690 //
3691 // As a single paragraph, despite the fact that the second line starts
3692 // with a digit-period-space sequence.
3693 //
3694 // Whereas when we're inside a list (or sub-list), that line will be
3695 // treated as the start of a sub-list. What a kludge, huh? This is
3696 // an aspect of Markdown's syntax that's hard to parse perfectly
3697 // without resorting to mind-reading. Perhaps the solution is to
3698 // change the syntax rules such that sub-lists must start with a
3699 // starting cardinal number; e.g. "1." or "a.".
3700 globals.gListLevel++;
3701
3702 // trim trailing blank lines:
3703 listStr = listStr.replace(/\n{2,}$/, '\n');
3704
3705 // attacklab: add sentinel to emulate \z
3706 listStr += '¨0';
3707
3708 var rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,
3709 isParagraphed = (/\n[ \t]*\n(?!¨0)/.test(listStr));
3710
3711 // Since version 1.5, nesting sublists requires 4 spaces (or 1 tab) indentation,
3712 // which is a syntax breaking change
3713 // activating this option reverts to old behavior
3714 // This will be removed in version 2.0
3715 if (options.disableForced4SpacesIndentedSublists) {
3716 rgx = /(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm;
3717 }
3718
3719 listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) {
3720 checked = (checked && checked.trim() !== '');
3721
3722 var item = showdown.subParser('makehtml.outdent')(m4, options, globals),
3723 bulletStyle = '';
3724
3725 // Support for github tasklists
3726 if (taskbtn && options.tasklists) {
3727 bulletStyle = ' class="task-list-item" style="list-style-type: none;"';
3728 item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () {
3729 var otp = '<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"';
3730 if (checked) {
3731 otp += ' checked';
3732 }
3733 otp += '>';
3734 return otp;
3735 });
3736 }
3737
3738 // ISSUE #312
3739 // This input: - - - a
3740 // causes trouble to the parser, since it interprets it as:
3741 // <ul><li><li><li>a</li></li></li></ul>
3742 // instead of:
3743 // <ul><li>- - a</li></ul>
3744 // So, to prevent it, we will put a marker (¨A)in the beginning of the line
3745 // Kind of hackish/monkey patching, but seems more effective than overcomplicating the list parser
3746 item = item.replace(/^([-*+]|\d\.)[ \t]+[\S\n ]*/g, function (wm2) {
3747 return '¨A' + wm2;
3748 });
3749
3750 // SPECIAL CASE: an heading followed by a paragraph of text that is not separated by a double newline
3751 // or/nor indented. ex:
3752 //
3753 // - # foo
3754 // bar is great
3755 //
3756 // While this does now follow the spec per se, not allowing for this might cause confusion since
3757 // header blocks don't need double newlines after
3758 if (/^#+.+\n.+/.test(item)) {
3759 item = item.replace(/^(#+.+)$/m, '$1\n');
3760 }
3761
3762 // m1 - Leading line or
3763 // Has a double return (multi paragraph)
3764 if (m1 || (item.search(/\n{2,}/) > -1)) {
3765 item = showdown.subParser('makehtml.githubCodeBlocks')(item, options, globals);
3766 item = showdown.subParser('makehtml.blockGamut')(item, options, globals);
3767 } else {
3768
3769 // Recursion for sub-lists:
3770 item = showdown.subParser('makehtml.lists')(item, options, globals);
3771 item = item.replace(/\n$/, ''); // chomp(item)
3772 item = showdown.subParser('makehtml.hashHTMLBlocks')(item, options, globals);
3773
3774 // Colapse double linebreaks
3775 item = item.replace(/\n\n+/g, '\n\n');
3776
3777 if (isParagraphed) {
3778 item = showdown.subParser('makehtml.paragraphs')(item, options, globals);
3779 } else {
3780 item = showdown.subParser('makehtml.spanGamut')(item, options, globals);
3781 }
3782 }
3783
3784 // now we need to remove the marker (¨A)
3785 item = item.replace('¨A', '');
3786 // we can finally wrap the line in list item tags
3787 item = '<li' + bulletStyle + '>' + item + '</li>\n';
3788
3789 return item;
3790 });
3791
3792 // attacklab: strip sentinel
3793 listStr = listStr.replace(/¨0/g, '');
3794
3795 globals.gListLevel--;
3796
3797 if (trimTrailing) {
3798 listStr = listStr.replace(/\s+$/, '');
3799 }
3800
3801 return listStr;
3802 }
3803
3804 function styleStartNumber (list, listType) {
3805 // check if ol and starts by a number different than 1
3806 if (listType === 'ol') {
3807 var res = list.match(/^ *(\d+)\./);
3808 if (res && res[1] !== '1') {
3809 return ' start="' + res[1] + '"';
3810 }
3811 }
3812 return '';
3813 }
3814
3815 /**
3816 * Check and parse consecutive lists (better fix for issue #142)
3817 * @param {string} list
3818 * @param {string} listType
3819 * @param {boolean} trimTrailing
3820 * @returns {string}
3821 */
3822 function parseConsecutiveLists (list, listType, trimTrailing) {
3823 // check if we caught 2 or more consecutive lists by mistake
3824 // we use the counterRgx, meaning if listType is UL we look for OL and vice versa
3825 var olRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?\d+\.[ \t]/gm : /^ {0,3}\d+\.[ \t]/gm,
3826 ulRgx = (options.disableForced4SpacesIndentedSublists) ? /^ ?[*+-][ \t]/gm : /^ {0,3}[*+-][ \t]/gm,
3827 counterRxg = (listType === 'ul') ? olRgx : ulRgx,
3828 result = '';
3829
3830 if (list.search(counterRxg) !== -1) {
3831 (function parseCL (txt) {
3832 var pos = txt.search(counterRxg),
3833 style = styleStartNumber(list, listType);
3834 if (pos !== -1) {
3835 // slice
3836 result += '\n\n<' + listType + style + '>\n' + processListItems(txt.slice(0, pos), !!trimTrailing) + '</' + listType + '>\n';
3837
3838 // invert counterType and listType
3839 listType = (listType === 'ul') ? 'ol' : 'ul';
3840 counterRxg = (listType === 'ul') ? olRgx : ulRgx;
3841
3842 //recurse
3843 parseCL(txt.slice(pos));
3844 } else {
3845 result += '\n\n<' + listType + style + '>\n' + processListItems(txt, !!trimTrailing) + '</' + listType + '>\n';
3846 }
3847 })(list);
3848 } else {
3849 var style = styleStartNumber(list, listType);
3850 result = '\n\n<' + listType + style + '>\n' + processListItems(list, !!trimTrailing) + '</' + listType + '>\n';
3851 }
3852
3853 return result;
3854 }
3855
3856 // Start of list parsing
3857 var subListRgx = /^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
3858 var mainListRgx = /(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
3859
3860 text = globals.converter._dispatch('lists.before', text, options, globals).getText();
3861 // add sentinel to hack around khtml/safari bug:
3862 // http://bugs.webkit.org/show_bug.cgi?id=11231
3863 text += '¨0';
3864
3865 if (globals.gListLevel) {
3866 text = text.replace(subListRgx, function (wholeMatch, list, m2) {
3867 var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
3868 return parseConsecutiveLists(list, listType, true);
3869 });
3870 } else {
3871 text = text.replace(mainListRgx, function (wholeMatch, m1, list, m3) {
3872 var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
3873 return parseConsecutiveLists(list, listType, false);
3874 });
3875 }
3876
3877 // strip sentinel
3878 text = text.replace(/¨0/, '');
3879 text = globals.converter._dispatch('makehtml.lists.after', text, options, globals).getText();
3880 return text;
3881 });
3882
3883 /**
3884 * Parse metadata at the top of the document
3885 */
3886 showdown.subParser('makehtml.metadata', function (text, options, globals) {
3887 'use strict';
3888
3889 if (!options.metadata) {
3890 return text;
3891 }
3892
3893 text = globals.converter._dispatch('makehtml.metadata.before', text, options, globals).getText();
3894
3895 function parseMetadataContents (content) {
3896 // raw is raw so it's not changed in any way
3897 globals.metadata.raw = content;
3898
3899 // escape chars forbidden in html attributes
3900 // double quotes
3901 content = content
3902 // ampersand first
3903 .replace(/&/g, '&amp;')
3904 // double quotes
3905 .replace(/"/g, '&quot;');
3906
3907 content = content.replace(/\n {4}/g, ' ');
3908 content.replace(/^([\S ]+): +([\s\S]+?)$/gm, function (wm, key, value) {
3909 globals.metadata.parsed[key] = value;
3910 return '';
3911 });
3912 }
3913
3914 text = text.replace(/^\s*«««+(\S*?)\n([\s\S]+?)\n»»»+\n/, function (wholematch, format, content) {
3915 parseMetadataContents(content);
3916 return '¨M';
3917 });
3918
3919 text = text.replace(/^\s*---+(\S*?)\n([\s\S]+?)\n---+\n/, function (wholematch, format, content) {
3920 if (format) {
3921 globals.metadata.format = format;
3922 }
3923 parseMetadataContents(content);
3924 return '¨M';
3925 });
3926
3927 text = text.replace(/¨M/g, '');
3928
3929 text = globals.converter._dispatch('makehtml.metadata.after', text, options, globals).getText();
3930 return text;
3931 });
3932
3933 /**
3934 * Remove one level of line-leading tabs or spaces
3935 */
3936 showdown.subParser('makehtml.outdent', function (text, options, globals) {
3937 'use strict';
3938 text = globals.converter._dispatch('makehtml.outdent.before', text, options, globals).getText();
3939
3940 // attacklab: hack around Konqueror 3.5.4 bug:
3941 // "----------bug".replace(/^-/g,"") == "bug"
3942 text = text.replace(/^(\t|[ ]{1,4})/gm, '¨0'); // attacklab: g_tab_width
3943
3944 // attacklab: clean up hack
3945 text = text.replace(/¨0/g, '');
3946
3947 text = globals.converter._dispatch('makehtml.outdent.after', text, options, globals).getText();
3948 return text;
3949 });
3950
3951 /**
3952 *
3953 */
3954 showdown.subParser('makehtml.paragraphs', function (text, options, globals) {
3955 'use strict';
3956
3957 text = globals.converter._dispatch('makehtml.paragraphs.before', text, options, globals).getText();
3958 // Strip leading and trailing lines:
3959 text = text.replace(/^\n+/g, '');
3960 text = text.replace(/\n+$/g, '');
3961
3962 var grafs = text.split(/\n{2,}/g),
3963 grafsOut = [],
3964 end = grafs.length; // Wrap <p> tags
3965
3966 for (var i = 0; i < end; i++) {
3967 var str = grafs[i];
3968 // if this is an HTML marker, copy it
3969 if (str.search(/¨(K|G)(\d+)\1/g) >= 0) {
3970 grafsOut.push(str);
3971
3972 // test for presence of characters to prevent empty lines being parsed
3973 // as paragraphs (resulting in undesired extra empty paragraphs)
3974 } else if (str.search(/\S/) >= 0) {
3975 str = showdown.subParser('makehtml.spanGamut')(str, options, globals);
3976 str = str.replace(/^([ \t]*)/g, '<p>');
3977 str += '</p>';
3978 grafsOut.push(str);
3979 }
3980 }
3981
3982 /** Unhashify HTML blocks */
3983 end = grafsOut.length;
3984 for (i = 0; i < end; i++) {
3985 var blockText = '',
3986 grafsOutIt = grafsOut[i],
3987 codeFlag = false;
3988 // if this is a marker for an html block...
3989 // use RegExp.test instead of string.search because of QML bug
3990 while (/¨(K|G)(\d+)\1/.test(grafsOutIt)) {
3991 var delim = RegExp.$1,
3992 num = RegExp.$2;
3993
3994 if (delim === 'K') {
3995 blockText = globals.gHtmlBlocks[num];
3996 } else {
3997 // we need to check if ghBlock is a false positive
3998 if (codeFlag) {
3999 // use encoded version of all text
4000 blockText = showdown.subParser('makehtml.encodeCode')(globals.ghCodeBlocks[num].text, options, globals);
4001 } else {
4002 blockText = globals.ghCodeBlocks[num].codeblock;
4003 }
4004 }
4005 blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
4006
4007 grafsOutIt = grafsOutIt.replace(/(\n\n)?¨(K|G)\d+\2(\n\n)?/, blockText);
4008 // Check if grafsOutIt is a pre->code
4009 if (/^<pre\b[^>]*>\s*<code\b[^>]*>/.test(grafsOutIt)) {
4010 codeFlag = true;
4011 }
4012 }
4013 grafsOut[i] = grafsOutIt;
4014 }
4015 text = grafsOut.join('\n');
4016 // Strip leading and trailing lines:
4017 text = text.replace(/^\n+/g, '');
4018 text = text.replace(/\n+$/g, '');
4019 return globals.converter._dispatch('makehtml.paragraphs.after', text, options, globals).getText();
4020 });
4021
4022 /**
4023 * Run extension
4024 */
4025 showdown.subParser('makehtml.runExtension', function (ext, text, options, globals) {
4026 'use strict';
4027
4028 if (ext.filter) {
4029 text = ext.filter(text, globals.converter, options);
4030
4031 } else if (ext.regex) {
4032 // TODO remove this when old extension loading mechanism is deprecated
4033 var re = ext.regex;
4034 if (!(re instanceof RegExp)) {
4035 re = new RegExp(re, 'g');
4036 }
4037 text = text.replace(re, ext.replace);
4038 }
4039
4040 return text;
4041 });
4042
4043 /**
4044 * These are all the transformations that occur *within* block-level
4045 * tags like paragraphs, headers, and list items.
4046 */
4047 showdown.subParser('makehtml.spanGamut', function (text, options, globals) {
4048 'use strict';
4049
4050 text = globals.converter._dispatch('makehtml.span.before', text, options, globals).getText();
4051
4052 text = showdown.subParser('makehtml.codeSpans')(text, options, globals);
4053 text = showdown.subParser('makehtml.escapeSpecialCharsWithinTagAttributes')(text, options, globals);
4054 text = showdown.subParser('makehtml.encodeBackslashEscapes')(text, options, globals);
4055
4056 // Process link and image tags. Images must come first,
4057 // because ![foo][f] looks like a link.
4058 text = showdown.subParser('makehtml.images')(text, options, globals);
4059
4060 text = globals.converter._dispatch('smakehtml.links.before', text, options, globals).getText();
4061 text = showdown.subParser('makehtml.links')(text, options, globals);
4062 text = globals.converter._dispatch('smakehtml.links.after', text, options, globals).getText();
4063
4064 //text = showdown.subParser('makehtml.autoLinks')(text, options, globals);
4065 //text = showdown.subParser('makehtml.simplifiedAutoLinks')(text, options, globals);
4066 text = showdown.subParser('makehtml.emoji')(text, options, globals);
4067 text = showdown.subParser('makehtml.underline')(text, options, globals);
4068 text = showdown.subParser('makehtml.italicsAndBold')(text, options, globals);
4069 text = showdown.subParser('makehtml.strikethrough')(text, options, globals);
4070 text = showdown.subParser('makehtml.ellipsis')(text, options, globals);
4071
4072 // we need to hash HTML tags inside spans
4073 text = showdown.subParser('makehtml.hashHTMLSpans')(text, options, globals);
4074
4075 // now we encode amps and angles
4076 text = showdown.subParser('makehtml.encodeAmpsAndAngles')(text, options, globals);
4077
4078 // Do hard breaks
4079 if (options.simpleLineBreaks) {
4080 // GFM style hard breaks
4081 // only add line breaks if the text does not contain a block (special case for lists)
4082 if (!/\n\n¨K/.test(text)) {
4083 text = text.replace(/\n+/g, '<br />\n');
4084 }
4085 } else {
4086 // Vanilla hard breaks
4087 text = text.replace(/ +\n/g, '<br />\n');
4088 }
4089
4090 text = globals.converter._dispatch('makehtml.spanGamut.after', text, options, globals).getText();
4091 return text;
4092 });
4093
4094 showdown.subParser('makehtml.strikethrough', function (text, options, globals) {
4095 'use strict';
4096
4097 if (options.strikethrough) {
4098 text = globals.converter._dispatch('makehtml.strikethrough.before', text, options, globals).getText();
4099 text = text.replace(/(?:~){2}([\s\S]+?)(?:~){2}/g, function (wm, txt) { return '<del>' + txt + '</del>'; });
4100 text = globals.converter._dispatch('makehtml.strikethrough.after', text, options, globals).getText();
4101 }
4102
4103 return text;
4104 });
4105
4106 /**
4107 * Strips link definitions from text, stores the URLs and titles in
4108 * hash references.
4109 * Link defs are in the form: ^[id]: url "optional title"
4110 */
4111 showdown.subParser('makehtml.stripLinkDefinitions', function (text, options, globals) {
4112 'use strict';
4113
4114 var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?([^>\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm,
4115 base64Regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(data:.+?\/.+?;base64,[A-Za-z0-9+/=\n]+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n\n|(?=¨0)|(?=\n\[))/gm;
4116
4117 // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
4118 text += '¨0';
4119
4120 var replaceFunc = function (wholeMatch, linkId, url, width, height, blankLines, title) {
4121 linkId = linkId.toLowerCase();
4122 if (url.match(/^data:.+?\/.+?;base64,/)) {
4123 // remove newlines
4124 globals.gUrls[linkId] = url.replace(/\s/g, '');
4125 } else {
4126 globals.gUrls[linkId] = showdown.subParser('makehtml.encodeAmpsAndAngles')(url, options, globals); // Link IDs are case-insensitive
4127 }
4128
4129 if (blankLines) {
4130 // Oops, found blank lines, so it's not a title.
4131 // Put back the parenthetical statement we stole.
4132 return blankLines + title;
4133
4134 } else {
4135 if (title) {
4136 globals.gTitles[linkId] = title.replace(/"|'/g, '&quot;');
4137 }
4138 if (options.parseImgDimensions && width && height) {
4139 globals.gDimensions[linkId] = {
4140 width: width,
4141 height: height
4142 };
4143 }
4144 }
4145 // Completely remove the definition from the text
4146 return '';
4147 };
4148
4149 // first we try to find base64 link references
4150 text = text.replace(base64Regex, replaceFunc);
4151
4152 text = text.replace(regex, replaceFunc);
4153
4154 // attacklab: strip sentinel
4155 text = text.replace(/¨0/, '');
4156
4157 return text;
4158 });
4159
4160 showdown.subParser('makehtml.tables', function (text, options, globals) {
4161 'use strict';
4162
4163 if (!options.tables) {
4164 return text;
4165 }
4166
4167 var tableRgx = /^ {0,3}\|?.+\|.+\n {0,3}\|?[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:[-=]){2,}[\s\S]+?(?:\n\n|¨0)/gm,
4168 //singeColTblRgx = /^ {0,3}\|.+\|\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n(?: {0,3}\|.+\|\n)+(?:\n\n|¨0)/gm;
4169 singeColTblRgx = /^ {0,3}\|.+\|[ \t]*\n {0,3}\|[ \t]*:?[ \t]*(?:[-=]){2,}[ \t]*:?[ \t]*\|[ \t]*\n( {0,3}\|.+\|[ \t]*\n)*(?:\n|¨0)/gm;
4170
4171 function parseStyles (sLine) {
4172 if (/^:[ \t]*--*$/.test(sLine)) {
4173 return ' style="text-align:left;"';
4174 } else if (/^--*[ \t]*:[ \t]*$/.test(sLine)) {
4175 return ' style="text-align:right;"';
4176 } else if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) {
4177 return ' style="text-align:center;"';
4178 } else {
4179 return '';
4180 }
4181 }
4182
4183 function parseHeaders (header, style) {
4184 var id = '';
4185 header = header.trim();
4186 // support both tablesHeaderId and tableHeaderId due to error in documentation so we don't break backwards compatibility
4187 if (options.tablesHeaderId || options.tableHeaderId) {
4188 id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"';
4189 }
4190 header = showdown.subParser('makehtml.spanGamut')(header, options, globals);
4191
4192 return '<th' + id + style + '>' + header + '</th>\n';
4193 }
4194
4195 function parseCells (cell, style) {
4196 var subText = showdown.subParser('makehtml.spanGamut')(cell, options, globals);
4197 return '<td' + style + '>' + subText + '</td>\n';
4198 }
4199
4200 function buildTable (headers, cells) {
4201 var tb = '<table>\n<thead>\n<tr>\n',
4202 tblLgn = headers.length;
4203
4204 for (var i = 0; i < tblLgn; ++i) {
4205 tb += headers[i];
4206 }
4207 tb += '</tr>\n</thead>\n<tbody>\n';
4208
4209 for (i = 0; i < cells.length; ++i) {
4210 tb += '<tr>\n';
4211 for (var ii = 0; ii < tblLgn; ++ii) {
4212 tb += cells[i][ii];
4213 }
4214 tb += '</tr>\n';
4215 }
4216 tb += '</tbody>\n</table>\n';
4217 return tb;
4218 }
4219
4220 function parseTable (rawTable) {
4221 var i, tableLines = rawTable.split('\n');
4222
4223 for (i = 0; i < tableLines.length; ++i) {
4224 // strip wrong first and last column if wrapped tables are used
4225 if (/^ {0,3}\|/.test(tableLines[i])) {
4226 tableLines[i] = tableLines[i].replace(/^ {0,3}\|/, '');
4227 }
4228 if (/\|[ \t]*$/.test(tableLines[i])) {
4229 tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
4230 }
4231 // parse code spans first, but we only support one line code spans
4232
4233 tableLines[i] = showdown.subParser('makehtml.codeSpans')(tableLines[i], options, globals);
4234 }
4235
4236 var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}),
4237 rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}),
4238 rawCells = [],
4239 headers = [],
4240 styles = [],
4241 cells = [];
4242
4243 tableLines.shift();
4244 tableLines.shift();
4245
4246 for (i = 0; i < tableLines.length; ++i) {
4247 if (tableLines[i].trim() === '') {
4248 continue;
4249 }
4250 rawCells.push(
4251 tableLines[i]
4252 .split('|')
4253 .map(function (s) {
4254 return s.trim();
4255 })
4256 );
4257 }
4258
4259 if (rawHeaders.length < rawStyles.length) {
4260 return rawTable;
4261 }
4262
4263 for (i = 0; i < rawStyles.length; ++i) {
4264 styles.push(parseStyles(rawStyles[i]));
4265 }
4266
4267 for (i = 0; i < rawHeaders.length; ++i) {
4268 if (showdown.helper.isUndefined(styles[i])) {
4269 styles[i] = '';
4270 }
4271 headers.push(parseHeaders(rawHeaders[i], styles[i]));
4272 }
4273
4274 for (i = 0; i < rawCells.length; ++i) {
4275 var row = [];
4276 for (var ii = 0; ii < headers.length; ++ii) {
4277 if (showdown.helper.isUndefined(rawCells[i][ii])) {
4278
4279 }
4280 row.push(parseCells(rawCells[i][ii], styles[ii]));
4281 }
4282 cells.push(row);
4283 }
4284
4285 return buildTable(headers, cells);
4286 }
4287
4288 text = globals.converter._dispatch('makehtml.tables.before', text, options, globals).getText();
4289
4290 // find escaped pipe characters
4291 text = text.replace(/\\(\|)/g, showdown.helper.escapeCharactersCallback);
4292
4293 // parse multi column tables
4294 text = text.replace(tableRgx, parseTable);
4295
4296 // parse one column tables
4297 text = text.replace(singeColTblRgx, parseTable);
4298
4299 text = globals.converter._dispatch('makehtml.tables.after', text, options, globals).getText();
4300
4301 return text;
4302 });
4303
4304 showdown.subParser('makehtml.underline', function (text, options, globals) {
4305 'use strict';
4306
4307 if (!options.underline) {
4308 return text;
4309 }
4310
4311 text = globals.converter._dispatch('makehtml.underline.before', text, options, globals).getText();
4312
4313 if (options.literalMidWordUnderscores) {
4314 text = text.replace(/\b___(\S[\s\S]*?)___\b/g, function (wm, txt) {
4315 return '<u>' + txt + '</u>';
4316 });
4317 text = text.replace(/\b__(\S[\s\S]*?)__\b/g, function (wm, txt) {
4318 return '<u>' + txt + '</u>';
4319 });
4320 } else {
4321 text = text.replace(/___(\S[\s\S]*?)___/g, function (wm, m) {
4322 return (/\S$/.test(m)) ? '<u>' + m + '</u>' : wm;
4323 });
4324 text = text.replace(/__(\S[\s\S]*?)__/g, function (wm, m) {
4325 return (/\S$/.test(m)) ? '<u>' + m + '</u>' : wm;
4326 });
4327 }
4328
4329 // escape remaining underscores to prevent them being parsed by italic and bold
4330 text = text.replace(/(_)/g, showdown.helper.escapeCharactersCallback);
4331
4332 text = globals.converter._dispatch('makehtml.underline.after', text, options, globals).getText();
4333
4334 return text;
4335 });
4336
4337 /**
4338 * Swap back in all the special characters we've hidden.
4339 */
4340 showdown.subParser('makehtml.unescapeSpecialChars', function (text, options, globals) {
4341 'use strict';
4342 text = globals.converter._dispatch('makehtml.unescapeSpecialChars.before', text, options, globals).getText();
4343
4344 text = text.replace(/¨E(\d+)E/g, function (wholeMatch, m1) {
4345 var charCodeToReplace = parseInt(m1);
4346 return String.fromCharCode(charCodeToReplace);
4347 });
4348
4349 text = globals.converter._dispatch('makehtml.unescapeSpecialChars.after', text, options, globals).getText();
4350 return text;
4351 });
4352
4353 showdown.subParser('makeMarkdown.blockquote', function (node, globals) {
4354 'use strict';
4355
4356 var txt = '';
4357 if (node.hasChildNodes()) {
4358 var children = node.childNodes,
4359 childrenLength = children.length;
4360
4361 for (var i = 0; i < childrenLength; ++i) {
4362 var innerTxt = showdown.subParser('makeMarkdown.node')(children[i], globals);
4363
4364 if (innerTxt === '') {
4365 continue;
4366 }
4367 txt += innerTxt;
4368 }
4369 }
4370 // cleanup
4371 txt = txt.trim();
4372 txt = '> ' + txt.split('\n').join('\n> ');
4373 return txt;
4374 });
4375
4376 showdown.subParser('makeMarkdown.codeBlock', function (node, globals) {
4377 'use strict';
4378
4379 var lang = node.getAttribute('language'),
4380 num = node.getAttribute('precodenum');
4381 return '```' + lang + '\n' + globals.preList[num] + '\n```';
4382 });
4383
4384 showdown.subParser('makeMarkdown.codeSpan', function (node) {
4385 'use strict';
4386
4387 return '`' + node.innerHTML + '`';
4388 });
4389
4390 showdown.subParser('makeMarkdown.emphasis', function (node, globals) {
4391 'use strict';
4392
4393 var txt = '';
4394 if (node.hasChildNodes()) {
4395 txt += '*';
4396 var children = node.childNodes,
4397 childrenLength = children.length;
4398 for (var i = 0; i < childrenLength; ++i) {
4399 txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4400 }
4401 txt += '*';
4402 }
4403 return txt;
4404 });
4405
4406 showdown.subParser('makeMarkdown.header', function (node, globals, headerLevel) {
4407 'use strict';
4408
4409 var headerMark = new Array(headerLevel + 1).join('#'),
4410 txt = '';
4411
4412 if (node.hasChildNodes()) {
4413 txt = headerMark + ' ';
4414 var children = node.childNodes,
4415 childrenLength = children.length;
4416
4417 for (var i = 0; i < childrenLength; ++i) {
4418 txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4419 }
4420 }
4421 return txt;
4422 });
4423
4424 showdown.subParser('makeMarkdown.hr', function () {
4425 'use strict';
4426
4427 return '---';
4428 });
4429
4430 showdown.subParser('makeMarkdown.image', function (node) {
4431 'use strict';
4432
4433 var txt = '';
4434 if (node.hasAttribute('src')) {
4435 txt += '![' + node.getAttribute('alt') + '](';
4436 txt += '<' + node.getAttribute('src') + '>';
4437 if (node.hasAttribute('width') && node.hasAttribute('height')) {
4438 txt += ' =' + node.getAttribute('width') + 'x' + node.getAttribute('height');
4439 }
4440
4441 if (node.hasAttribute('title')) {
4442 txt += ' "' + node.getAttribute('title') + '"';
4443 }
4444 txt += ')';
4445 }
4446 return txt;
4447 });
4448
4449 showdown.subParser('makeMarkdown.links', function (node, globals) {
4450 'use strict';
4451
4452 var txt = '';
4453 if (node.hasChildNodes() && node.hasAttribute('href')) {
4454 var children = node.childNodes,
4455 childrenLength = children.length;
4456 txt = '[';
4457 for (var i = 0; i < childrenLength; ++i) {
4458 txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4459 }
4460 txt += '](';
4461 txt += '<' + node.getAttribute('href') + '>';
4462 if (node.hasAttribute('title')) {
4463 txt += ' "' + node.getAttribute('title') + '"';
4464 }
4465 txt += ')';
4466 }
4467 return txt;
4468 });
4469
4470 showdown.subParser('makeMarkdown.list', function (node, globals, type) {
4471 'use strict';
4472
4473 var txt = '';
4474 if (!node.hasChildNodes()) {
4475 return '';
4476 }
4477 var listItems = node.childNodes,
4478 listItemsLenght = listItems.length,
4479 listNum = node.getAttribute('start') || 1;
4480
4481 for (var i = 0; i < listItemsLenght; ++i) {
4482 if (typeof listItems[i].tagName === 'undefined' || listItems[i].tagName.toLowerCase() !== 'li') {
4483 continue;
4484 }
4485
4486 // define the bullet to use in list
4487 var bullet = '';
4488 if (type === 'ol') {
4489 bullet = listNum.toString() + '. ';
4490 } else {
4491 bullet = '- ';
4492 }
4493
4494 // parse list item
4495 txt += bullet + showdown.subParser('makeMarkdown.listItem')(listItems[i], globals);
4496 ++listNum;
4497 }
4498
4499 return txt.trim();
4500 });
4501
4502 showdown.subParser('makeMarkdown.listItem', function (node, globals) {
4503 'use strict';
4504
4505 var listItemTxt = '';
4506
4507 var children = node.childNodes,
4508 childrenLenght = children.length;
4509
4510 for (var i = 0; i < childrenLenght; ++i) {
4511 listItemTxt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4512 }
4513 // if it's only one liner, we need to add a newline at the end
4514 if (!/\n$/.test(listItemTxt)) {
4515 listItemTxt += '\n';
4516 } else {
4517 // it's multiparagraph, so we need to indent
4518 listItemTxt = listItemTxt
4519 .split('\n')
4520 .join('\n ')
4521 .replace(/^ {4}$/gm, '')
4522 .replace(/\n\n+/g, '\n\n');
4523 }
4524
4525 return listItemTxt;
4526 });
4527
4528
4529
4530 showdown.subParser('makeMarkdown.node', function (node, globals, spansOnly) {
4531 'use strict';
4532
4533 spansOnly = spansOnly || false;
4534
4535 var txt = '';
4536
4537 // edge case of text without wrapper paragraph
4538 if (node.nodeType === 3) {
4539 return showdown.subParser('makeMarkdown.txt')(node, globals);
4540 }
4541
4542 // HTML comment
4543 if (node.nodeType === 8) {
4544 return '<!--' + node.data + '-->\n\n';
4545 }
4546
4547 // process only node elements
4548 if (node.nodeType !== 1) {
4549 return '';
4550 }
4551
4552 var tagName = node.tagName.toLowerCase();
4553
4554 switch (tagName) {
4555
4556 //
4557 // BLOCKS
4558 //
4559 case 'h1':
4560 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 1) + '\n\n'; }
4561 break;
4562 case 'h2':
4563 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 2) + '\n\n'; }
4564 break;
4565 case 'h3':
4566 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 3) + '\n\n'; }
4567 break;
4568 case 'h4':
4569 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 4) + '\n\n'; }
4570 break;
4571 case 'h5':
4572 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 5) + '\n\n'; }
4573 break;
4574 case 'h6':
4575 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.header')(node, globals, 6) + '\n\n'; }
4576 break;
4577
4578 case 'p':
4579 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.paragraph')(node, globals) + '\n\n'; }
4580 break;
4581
4582 case 'blockquote':
4583 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.blockquote')(node, globals) + '\n\n'; }
4584 break;
4585
4586 case 'hr':
4587 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.hr')(node, globals) + '\n\n'; }
4588 break;
4589
4590 case 'ol':
4591 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.list')(node, globals, 'ol') + '\n\n'; }
4592 break;
4593
4594 case 'ul':
4595 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.list')(node, globals, 'ul') + '\n\n'; }
4596 break;
4597
4598 case 'precode':
4599 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.codeBlock')(node, globals) + '\n\n'; }
4600 break;
4601
4602 case 'pre':
4603 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.pre')(node, globals) + '\n\n'; }
4604 break;
4605
4606 case 'table':
4607 if (!spansOnly) { txt = showdown.subParser('makeMarkdown.table')(node, globals) + '\n\n'; }
4608 break;
4609
4610 //
4611 // SPANS
4612 //
4613 case 'code':
4614 txt = showdown.subParser('makeMarkdown.codeSpan')(node, globals);
4615 break;
4616
4617 case 'em':
4618 case 'i':
4619 txt = showdown.subParser('makeMarkdown.emphasis')(node, globals);
4620 break;
4621
4622 case 'strong':
4623 case 'b':
4624 txt = showdown.subParser('makeMarkdown.strong')(node, globals);
4625 break;
4626
4627 case 'del':
4628 txt = showdown.subParser('makeMarkdown.strikethrough')(node, globals);
4629 break;
4630
4631 case 'a':
4632 txt = showdown.subParser('makeMarkdown.links')(node, globals);
4633 break;
4634
4635 case 'img':
4636 txt = showdown.subParser('makeMarkdown.image')(node, globals);
4637 break;
4638
4639 default:
4640 txt = node.outerHTML + '\n\n';
4641 }
4642
4643 // common normalization
4644 // TODO eventually
4645
4646 return txt;
4647 });
4648
4649 showdown.subParser('makeMarkdown.paragraph', function (node, globals) {
4650 'use strict';
4651
4652 var txt = '';
4653 if (node.hasChildNodes()) {
4654 var children = node.childNodes,
4655 childrenLength = children.length;
4656 for (var i = 0; i < childrenLength; ++i) {
4657 txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4658 }
4659 }
4660
4661 // some text normalization
4662 txt = txt.trim();
4663
4664 return txt;
4665 });
4666
4667 showdown.subParser('makeMarkdown.pre', function (node, globals) {
4668 'use strict';
4669
4670 var num = node.getAttribute('prenum');
4671 return '<pre>' + globals.preList[num] + '</pre>';
4672 });
4673
4674 showdown.subParser('makeMarkdown.strikethrough', function (node, globals) {
4675 'use strict';
4676
4677 var txt = '';
4678 if (node.hasChildNodes()) {
4679 txt += '~~';
4680 var children = node.childNodes,
4681 childrenLength = children.length;
4682 for (var i = 0; i < childrenLength; ++i) {
4683 txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4684 }
4685 txt += '~~';
4686 }
4687 return txt;
4688 });
4689
4690 showdown.subParser('makeMarkdown.strong', function (node, globals) {
4691 'use strict';
4692
4693 var txt = '';
4694 if (node.hasChildNodes()) {
4695 txt += '**';
4696 var children = node.childNodes,
4697 childrenLength = children.length;
4698 for (var i = 0; i < childrenLength; ++i) {
4699 txt += showdown.subParser('makeMarkdown.node')(children[i], globals);
4700 }
4701 txt += '**';
4702 }
4703 return txt;
4704 });
4705
4706 showdown.subParser('makeMarkdown.table', function (node, globals) {
4707 'use strict';
4708
4709 var txt = '',
4710 tableArray = [[], []],
4711 headings = node.querySelectorAll('thead>tr>th'),
4712 rows = node.querySelectorAll('tbody>tr'),
4713 i, ii;
4714 for (i = 0; i < headings.length; ++i) {
4715 var headContent = showdown.subParser('makeMarkdown.tableCell')(headings[i], globals),
4716 allign = '---';
4717
4718 if (headings[i].hasAttribute('style')) {
4719 var style = headings[i].getAttribute('style').toLowerCase().replace(/\s/g, '');
4720 switch (style) {
4721 case 'text-align:left;':
4722 allign = ':---';
4723 break;
4724 case 'text-align:right;':
4725 allign = '---:';
4726 break;
4727 case 'text-align:center;':
4728 allign = ':---:';
4729 break;
4730 }
4731 }
4732 tableArray[0][i] = headContent.trim();
4733 tableArray[1][i] = allign;
4734 }
4735
4736 for (i = 0; i < rows.length; ++i) {
4737 var r = tableArray.push([]) - 1,
4738 cols = rows[i].getElementsByTagName('td');
4739
4740 for (ii = 0; ii < headings.length; ++ii) {
4741 var cellContent = ' ';
4742 if (typeof cols[ii] !== 'undefined') {
4743 cellContent = showdown.subParser('makeMarkdown.tableCell')(cols[ii], globals);
4744 }
4745 tableArray[r].push(cellContent);
4746 }
4747 }
4748
4749 var cellSpacesCount = 3;
4750 for (i = 0; i < tableArray.length; ++i) {
4751 for (ii = 0; ii < tableArray[i].length; ++ii) {
4752 var strLen = tableArray[i][ii].length;
4753 if (strLen > cellSpacesCount) {
4754 cellSpacesCount = strLen;
4755 }
4756 }
4757 }
4758
4759 for (i = 0; i < tableArray.length; ++i) {
4760 for (ii = 0; ii < tableArray[i].length; ++ii) {
4761 if (i === 1) {
4762 if (tableArray[i][ii].slice(-1) === ':') {
4763 tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii].slice(-1), cellSpacesCount - 1, '-') + ':';
4764 } else {
4765 tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount, '-');
4766 }
4767 } else {
4768 tableArray[i][ii] = showdown.helper.padEnd(tableArray[i][ii], cellSpacesCount);
4769 }
4770 }
4771 txt += '| ' + tableArray[i].join(' | ') + ' |\n';
4772 }
4773
4774 return txt.trim();
4775 });
4776
4777 showdown.subParser('makeMarkdown.tableCell', function (node, globals) {
4778 'use strict';
4779
4780 var txt = '';
4781 if (!node.hasChildNodes()) {
4782 return '';
4783 }
4784 var children = node.childNodes,
4785 childrenLength = children.length;
4786
4787 for (var i = 0; i < childrenLength; ++i) {
4788 txt += showdown.subParser('makeMarkdown.node')(children[i], globals, true);
4789 }
4790 return txt.trim();
4791 });
4792
4793 showdown.subParser('makeMarkdown.txt', function (node) {
4794 'use strict';
4795
4796 var txt = node.nodeValue;
4797
4798 // multiple spaces are collapsed
4799 txt = txt.replace(/ +/g, ' ');
4800
4801 // replace the custom ¨NBSP; with a space
4802 txt = txt.replace(/¨NBSP;/g, ' ');
4803
4804 // ", <, > and & should replace escaped html entities
4805 txt = showdown.helper.unescapeHTMLEntities(txt);
4806
4807 // escape markdown magic characters
4808 // emphasis, strong and strikethrough - can appear everywhere
4809 // we also escape pipe (|) because of tables
4810 // and escape ` because of code blocks and spans
4811 txt = txt.replace(/([*_~|`])/g, '\\$1');
4812
4813 // escape > because of blockquotes
4814 txt = txt.replace(/^(\s*)>/g, '\\$1>');
4815
4816 // hash character, only troublesome at the beginning of a line because of headers
4817 txt = txt.replace(/^#/gm, '\\#');
4818
4819 // horizontal rules
4820 txt = txt.replace(/^(\s*)([-=]{3,})(\s*)$/, '$1\\$2$3');
4821
4822 // dot, because of ordered lists, only troublesome at the beginning of a line when preceded by an integer
4823 txt = txt.replace(/^( {0,3}\d+)\./gm, '$1\\.');
4824
4825 // +, * and -, at the beginning of a line becomes a list, so we need to escape them also (asterisk was already escaped)
4826 txt = txt.replace(/^( {0,3})([+-])/gm, '$1\\$2');
4827
4828 // images and links, ] followed by ( is problematic, so we escape it
4829 txt = txt.replace(/]([\s]*)\(/g, '\\]$1\\(');
4830
4831 // reference URIs must also be escaped
4832 txt = txt.replace(/^ {0,3}\[([\S \t]*?)]:/gm, '\\[$1]:');
4833
4834 return txt;
4835 });
4836
4837 /**
4838 * Created by Estevao on 31-05-2015.
4839 */
4840
4841 /**
4842 * Showdown Converter class
4843 * @class
4844 * @param {object} [converterOptions]
4845 * @returns {Converter}
4846 */
4847 showdown.Converter = function (converterOptions) {
4848 'use strict';
4849
4850 var
4851 /**
4852 * Options used by this converter
4853 * @private
4854 * @type {{}}
4855 */
4856 options = {},
4857
4858 /**
4859 * Language extensions used by this converter
4860 * @private
4861 * @type {Array}
4862 */
4863 langExtensions = [],
4864
4865 /**
4866 * Output modifiers extensions used by this converter
4867 * @private
4868 * @type {Array}
4869 */
4870 outputModifiers = [],
4871
4872 /**
4873 * Event listeners
4874 * @private
4875 * @type {{}}
4876 */
4877 listeners = {},
4878
4879 /**
4880 * The flavor set in this converter
4881 */
4882 setConvFlavor = setFlavor,
4883
4884 /**
4885 * Metadata of the document
4886 * @type {{parsed: {}, raw: string, format: string}}
4887 */
4888 metadata = {
4889 parsed: {},
4890 raw: '',
4891 format: ''
4892 };
4893
4894 _constructor();
4895
4896 /**
4897 * Converter constructor
4898 * @private
4899 */
4900 function _constructor () {
4901 converterOptions = converterOptions || {};
4902
4903 for (var gOpt in globalOptions) {
4904 if (globalOptions.hasOwnProperty(gOpt)) {
4905 options[gOpt] = globalOptions[gOpt];
4906 }
4907 }
4908
4909 // Merge options
4910 if (typeof converterOptions === 'object') {
4911 for (var opt in converterOptions) {
4912 if (converterOptions.hasOwnProperty(opt)) {
4913 options[opt] = converterOptions[opt];
4914 }
4915 }
4916 } else {
4917 throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions +
4918 ' was passed instead.');
4919 }
4920
4921 if (options.extensions) {
4922 showdown.helper.forEach(options.extensions, _parseExtension);
4923 }
4924 }
4925
4926 /**
4927 * Parse extension
4928 * @param {*} ext
4929 * @param {string} [name='']
4930 * @private
4931 */
4932 function _parseExtension (ext, name) {
4933
4934 name = name || null;
4935 // If it's a string, the extension was previously loaded
4936 if (showdown.helper.isString(ext)) {
4937 ext = showdown.helper.stdExtName(ext);
4938 name = ext;
4939
4940 // LEGACY_SUPPORT CODE
4941 if (showdown.extensions[ext]) {
4942 console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' +
4943 'Please inform the developer that the extension should be updated!');
4944 legacyExtensionLoading(showdown.extensions[ext], ext);
4945 return;
4946 // END LEGACY SUPPORT CODE
4947
4948 } else if (!showdown.helper.isUndefined(extensions[ext])) {
4949 ext = extensions[ext];
4950
4951 } else {
4952 throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.');
4953 }
4954 }
4955
4956 if (typeof ext === 'function') {
4957 ext = ext();
4958 }
4959
4960 if (!showdown.helper.isArray(ext)) {
4961 ext = [ext];
4962 }
4963
4964 var validExt = validate(ext, name);
4965 if (!validExt.valid) {
4966 throw Error(validExt.error);
4967 }
4968
4969 for (var i = 0; i < ext.length; ++i) {
4970 switch (ext[i].type) {
4971
4972 case 'lang':
4973 langExtensions.push(ext[i]);
4974 break;
4975
4976 case 'output':
4977 outputModifiers.push(ext[i]);
4978 break;
4979 }
4980 if (ext[i].hasOwnProperty('listeners')) {
4981 for (var ln in ext[i].listeners) {
4982 if (ext[i].listeners.hasOwnProperty(ln)) {
4983 listen(ln, ext[i].listeners[ln]);
4984 }
4985 }
4986 }
4987 }
4988
4989 }
4990
4991 /**
4992 * LEGACY_SUPPORT
4993 * @param {*} ext
4994 * @param {string} name
4995 */
4996 function legacyExtensionLoading (ext, name) {
4997 if (typeof ext === 'function') {
4998 ext = ext(new showdown.Converter());
4999 }
5000 if (!showdown.helper.isArray(ext)) {
5001 ext = [ext];
5002 }
5003 var valid = validate(ext, name);
5004
5005 if (!valid.valid) {
5006 throw Error(valid.error);
5007 }
5008
5009 for (var i = 0; i < ext.length; ++i) {
5010 switch (ext[i].type) {
5011 case 'lang':
5012 langExtensions.push(ext[i]);
5013 break;
5014 case 'output':
5015 outputModifiers.push(ext[i]);
5016 break;
5017 default:// should never reach here
5018 throw Error('Extension loader error: Type unrecognized!!!');
5019 }
5020 }
5021 }
5022
5023 /**
5024 * Listen to an event
5025 * @param {string} name
5026 * @param {function} callback
5027 */
5028 function listen (name, callback) {
5029 if (!showdown.helper.isString(name)) {
5030 throw Error('Invalid argument in converter.listen() method: name must be a string, but ' + typeof name + ' given');
5031 }
5032
5033 if (typeof callback !== 'function') {
5034 throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given');
5035 }
5036 name = name.toLowerCase();
5037 if (!listeners.hasOwnProperty(name)) {
5038 listeners[name] = [];
5039 }
5040 listeners[name].push(callback);
5041 }
5042
5043 function rTrimInputText (text) {
5044 var rsp = text.match(/^\s*/)[0].length,
5045 rgx = new RegExp('^\\s{0,' + rsp + '}', 'gm');
5046 return text.replace(rgx, '');
5047 }
5048
5049 /**
5050 *
5051 * @param {string} evtName Event name
5052 * @param {string} text Text
5053 * @param {{}} options Converter Options
5054 * @param {{}} globals Converter globals
5055 * @param {{}} pParams extra params for event
5056 * @returns showdown.helper.Event
5057 * @private
5058 */
5059 this._dispatch = function dispatch (evtName, text, options, globals, pParams) {
5060 evtName = evtName.toLowerCase();
5061 var params = pParams || {};
5062 params.converter = this;
5063 params.text = text;
5064 params.options = options;
5065 params.globals = globals;
5066 var event = new showdown.helper.Event(evtName, text, params);
5067
5068 if (listeners.hasOwnProperty(evtName)) {
5069 for (var ei = 0; ei < listeners[evtName].length; ++ei) {
5070 var nText = listeners[evtName][ei](event);
5071 if (nText && typeof nText !== 'undefined') {
5072 event.setText(nText);
5073 }
5074 }
5075 }
5076 return event;
5077 };
5078
5079 /**
5080 * Listen to an event
5081 * @param {string} name
5082 * @param {function} callback
5083 * @returns {showdown.Converter}
5084 */
5085 this.listen = function (name, callback) {
5086 listen(name, callback);
5087 return this;
5088 };
5089
5090 /**
5091 * Converts a markdown string into HTML string
5092 * @param {string} text
5093 * @returns {*}
5094 */
5095 this.makeHtml = function (text) {
5096 //check if text is not falsy
5097 if (!text) {
5098 return text;
5099 }
5100
5101 var globals = {
5102 gHtmlBlocks: [],
5103 gHtmlMdBlocks: [],
5104 gHtmlSpans: [],
5105 gUrls: {},
5106 gTitles: {},
5107 gDimensions: {},
5108 gListLevel: 0,
5109 hashLinkCounts: {},
5110 langExtensions: langExtensions,
5111 outputModifiers: outputModifiers,
5112 converter: this,
5113 ghCodeBlocks: [],
5114 metadata: {
5115 parsed: {},
5116 raw: '',
5117 format: ''
5118 }
5119 };
5120
5121 // This lets us use ¨ trema as an escape char to avoid md5 hashes
5122 // The choice of character is arbitrary; anything that isn't
5123 // magic in Markdown will work.
5124 text = text.replace(/¨/g, '¨T');
5125
5126 // Replace $ with ¨D
5127 // RegExp interprets $ as a special character
5128 // when it's in a replacement string
5129 text = text.replace(/\$/g, '¨D');
5130
5131 // Standardize line endings
5132 text = text.replace(/\r\n/g, '\n'); // DOS to Unix
5133 text = text.replace(/\r/g, '\n'); // Mac to Unix
5134
5135 // Stardardize line spaces
5136 text = text.replace(/\u00A0/g, '&nbsp;');
5137
5138 if (options.smartIndentationFix) {
5139 text = rTrimInputText(text);
5140 }
5141
5142 // Make sure text begins and ends with a couple of newlines:
5143 text = '\n\n' + text + '\n\n';
5144
5145 // detab
5146 text = showdown.subParser('makehtml.detab')(text, options, globals);
5147
5148 /**
5149 * Strip any lines consisting only of spaces and tabs.
5150 * This makes subsequent regexs easier to write, because we can
5151 * match consecutive blank lines with /\n+/ instead of something
5152 * contorted like /[ \t]*\n+/
5153 */
5154 text = text.replace(/^[ \t]+$/mg, '');
5155
5156 //run languageExtensions
5157 showdown.helper.forEach(langExtensions, function (ext) {
5158 text = showdown.subParser('makehtml.runExtension')(ext, text, options, globals);
5159 });
5160
5161 // run the sub parsers
5162 text = showdown.subParser('makehtml.metadata')(text, options, globals);
5163 text = showdown.subParser('makehtml.hashPreCodeTags')(text, options, globals);
5164 text = showdown.subParser('makehtml.githubCodeBlocks')(text, options, globals);
5165 text = showdown.subParser('makehtml.hashHTMLBlocks')(text, options, globals);
5166 text = showdown.subParser('makehtml.hashCodeTags')(text, options, globals);
5167 text = showdown.subParser('makehtml.stripLinkDefinitions')(text, options, globals);
5168 text = showdown.subParser('makehtml.blockGamut')(text, options, globals);
5169 text = showdown.subParser('makehtml.unhashHTMLSpans')(text, options, globals);
5170 text = showdown.subParser('makehtml.unescapeSpecialChars')(text, options, globals);
5171
5172 // attacklab: Restore dollar signs
5173 text = text.replace(/¨D/g, '$$');
5174
5175 // attacklab: Restore tremas
5176 text = text.replace(/¨T/g, '¨');
5177
5178 // render a complete html document instead of a partial if the option is enabled
5179 text = showdown.subParser('makehtml.completeHTMLDocument')(text, options, globals);
5180
5181 // Run output modifiers
5182 showdown.helper.forEach(outputModifiers, function (ext) {
5183 text = showdown.subParser('makehtml.runExtension')(ext, text, options, globals);
5184 });
5185
5186 // update metadata
5187 metadata = globals.metadata;
5188 return text;
5189 };
5190
5191 /**
5192 * Converts an HTML string into a markdown string
5193 * @param src
5194 * @returns {string}
5195 */
5196 this.makeMarkdown = function (src) {
5197
5198 // replace \r\n with \n
5199 src = src.replace(/\r\n/g, '\n');
5200 src = src.replace(/\r/g, '\n'); // old macs
5201
5202 // due to an edge case, we need to find this: > <
5203 // to prevent removing of non silent white spaces
5204 // ex: <em>this is</em> <strong>sparta</strong>
5205 src = src.replace(/>[ \t]+</, '>¨NBSP;<');
5206
5207 var doc = showdown.helper.document.createElement('div');
5208 doc.innerHTML = src;
5209
5210 var globals = {
5211 preList: substitutePreCodeTags(doc)
5212 };
5213
5214 // remove all newlines and collapse spaces
5215 clean(doc);
5216
5217 // some stuff, like accidental reference links must now be escaped
5218 // TODO
5219 // doc.innerHTML = doc.innerHTML.replace(/\[[\S\t ]]/);
5220
5221 var nodes = doc.childNodes,
5222 mdDoc = '';
5223
5224 for (var i = 0; i < nodes.length; i++) {
5225 mdDoc += showdown.subParser('makeMarkdown.node')(nodes[i], globals);
5226 }
5227
5228 function clean (node) {
5229 for (var n = 0; n < node.childNodes.length; ++n) {
5230 var child = node.childNodes[n];
5231 if (child.nodeType === 3) {
5232 if (!/\S/.test(child.nodeValue)) {
5233 node.removeChild(child);
5234 --n;
5235 } else {
5236 child.nodeValue = child.nodeValue.split('\n').join(' ');
5237 child.nodeValue = child.nodeValue.replace(/(\s)+/g, '$1');
5238 }
5239 } else if (child.nodeType === 1) {
5240 clean(child);
5241 }
5242 }
5243 }
5244
5245 // find all pre tags and replace contents with placeholder
5246 // we need this so that we can remove all indentation from html
5247 // to ease up parsing
5248 function substitutePreCodeTags (doc) {
5249
5250 var pres = doc.querySelectorAll('pre'),
5251 presPH = [];
5252
5253 for (var i = 0; i < pres.length; ++i) {
5254
5255 if (pres[i].childElementCount === 1 && pres[i].firstChild.tagName.toLowerCase() === 'code') {
5256 var content = pres[i].firstChild.innerHTML.trim(),
5257 language = pres[i].firstChild.getAttribute('data-language') || '';
5258
5259 // if data-language attribute is not defined, then we look for class language-*
5260 if (language === '') {
5261 var classes = pres[i].firstChild.className.split(' ');
5262 for (var c = 0; c < classes.length; ++c) {
5263 var matches = classes[c].match(/^language-(.+)$/);
5264 if (matches !== null) {
5265 language = matches[1];
5266 break;
5267 }
5268 }
5269 }
5270
5271 // unescape html entities in content
5272 content = showdown.helper.unescapeHTMLEntities(content);
5273
5274 presPH.push(content);
5275 pres[i].outerHTML = '<precode language="' + language + '" precodenum="' + i.toString() + '"></precode>';
5276 } else {
5277 presPH.push(pres[i].innerHTML);
5278 pres[i].innerHTML = '';
5279 pres[i].setAttribute('prenum', i.toString());
5280 }
5281 }
5282 return presPH;
5283 }
5284
5285 return mdDoc;
5286 };
5287
5288 /**
5289 * Set an option of this Converter instance
5290 * @param {string} key
5291 * @param {*} value
5292 */
5293 this.setOption = function (key, value) {
5294 options[key] = value;
5295 };
5296
5297 /**
5298 * Get the option of this Converter instance
5299 * @param {string} key
5300 * @returns {*}
5301 */
5302 this.getOption = function (key) {
5303 return options[key];
5304 };
5305
5306 /**
5307 * Get the options of this Converter instance
5308 * @returns {{}}
5309 */
5310 this.getOptions = function () {
5311 return options;
5312 };
5313
5314 /**
5315 * Add extension to THIS converter
5316 * @param {{}} extension
5317 * @param {string} [name=null]
5318 */
5319 this.addExtension = function (extension, name) {
5320 name = name || null;
5321 _parseExtension(extension, name);
5322 };
5323
5324 /**
5325 * Use a global registered extension with THIS converter
5326 * @param {string} extensionName Name of the previously registered extension
5327 */
5328 this.useExtension = function (extensionName) {
5329 _parseExtension(extensionName);
5330 };
5331
5332 /**
5333 * Set the flavor THIS converter should use
5334 * @param {string} name
5335 */
5336 this.setFlavor = function (name) {
5337 if (!flavor.hasOwnProperty(name)) {
5338 throw Error(name + ' flavor was not found');
5339 }
5340 var preset = flavor[name];
5341 setConvFlavor = name;
5342 for (var option in preset) {
5343 if (preset.hasOwnProperty(option)) {
5344 options[option] = preset[option];
5345 }
5346 }
5347 };
5348
5349 /**
5350 * Get the currently set flavor of this converter
5351 * @returns {string}
5352 */
5353 this.getFlavor = function () {
5354 return setConvFlavor;
5355 };
5356
5357 /**
5358 * Remove an extension from THIS converter.
5359 * Note: This is a costly operation. It's better to initialize a new converter
5360 * and specify the extensions you wish to use
5361 * @param {Array} extension
5362 */
5363 this.removeExtension = function (extension) {
5364 if (!showdown.helper.isArray(extension)) {
5365 extension = [extension];
5366 }
5367 for (var a = 0; a < extension.length; ++a) {
5368 var ext = extension[a];
5369 for (var i = 0; i < langExtensions.length; ++i) {
5370 if (langExtensions[i] === ext) {
5371 langExtensions[i].splice(i, 1);
5372 }
5373 }
5374 for (var ii = 0; ii < outputModifiers.length; ++i) {
5375 if (outputModifiers[ii] === ext) {
5376 outputModifiers[ii].splice(i, 1);
5377 }
5378 }
5379 }
5380 };
5381
5382 /**
5383 * Get all extension of THIS converter
5384 * @returns {{language: Array, output: Array}}
5385 */
5386 this.getAllExtensions = function () {
5387 return {
5388 language: langExtensions,
5389 output: outputModifiers
5390 };
5391 };
5392
5393 /**
5394 * Get the metadata of the previously parsed document
5395 * @param raw
5396 * @returns {string|{}}
5397 */
5398 this.getMetadata = function (raw) {
5399 if (raw) {
5400 return metadata.raw;
5401 } else {
5402 return metadata.parsed;
5403 }
5404 };
5405
5406 /**
5407 * Get the metadata format of the previously parsed document
5408 * @returns {string}
5409 */
5410 this.getMetadataFormat = function () {
5411 return metadata.format;
5412 };
5413
5414 /**
5415 * Private: set a single key, value metadata pair
5416 * @param {string} key
5417 * @param {string} value
5418 */
5419 this._setMetadataPair = function (key, value) {
5420 metadata.parsed[key] = value;
5421 };
5422
5423 /**
5424 * Private: set metadata format
5425 * @param {string} format
5426 */
5427 this._setMetadataFormat = function (format) {
5428 metadata.format = format;
5429 };
5430
5431 /**
5432 * Private: set metadata raw text
5433 * @param {string} raw
5434 */
5435 this._setMetadataRaw = function (raw) {
5436 metadata.raw = raw;
5437 };
5438 };
5439
5440 var root = this;
5441
5442 // AMD Loader
5443 if (typeof define === 'function' && define.amd) {
5444 define(function () {
5445 'use strict';
5446 return showdown;
5447 });
5448
5449 // CommonJS/nodeJS Loader
5450 } else if (typeof module !== 'undefined' && module.exports) {
5451 module.exports = showdown;
5452
5453 // Regular Browser loader
5454 } else {
5455 root.showdown = showdown;
5456 }
5457 }).call(this);
5458
5459 //# sourceMappingURL=showdown.js.map
1919 $scope.agentToken = {id: null, token: null};
2020 $scope.workspace = null;
2121 $scope.agents = [];
22 $scope.executors = [];
2223 $scope.selectAll = false;
2324 $scope.options = [];
2425 $scope.disableExecute = false;
26 $scope.parameters_metadata = {};
27 $scope.data = {
28 selectedExecutor: null
29 };
2530
2631 $scope.init = function () {
2732 getWorkspaces();
7580 });
7681 };
7782
78 var removeAgentFromScope = function (agentId) {
83 $scope.selectAgent = function (agent) {
84 $scope.executors = [];
85 $scope.parameters_values = {};
86 agent.executors.forEach((executor) => {
87 let exec = {
88 id: executor.id,
89 name: executor.name,
90 parameters_metadata: [],
91 parameters_values: {}
92 };
93 let params = executor.parameters_metadata;
94 for (let [key, value] of Object.entries(params)) {
95 let parameter = { name: key, isRequired: value };
96 exec.parameters_metadata.push(parameter);
97 exec.parameters_values[key] = '';
98 }
99
100 $scope.executors.push(exec);
101 });
102 };
103
104 let removeAgentFromScope = function (agentId) {
79105 for (var i = 0; i < $scope.agents.length; i++) {
80106 if ($scope.agents[i].id === agentId) {
81107 $scope.agents.splice(i, 1);
95121
96122 $scope.runAgent = function (agentId) {
97123 $scope.disableExecute = true;
98 agentFact.runAgent($scope.workspace, agentId).then(
99 function (response) {
124 let executorData = {
125 agent_id: agentId,
126 executor: $scope.data.selectedExecutor.name,
127 args: {}
128 };
129 for (let [key, value] of Object.entries($scope.data.selectedExecutor.parameters_values)) {
130 executorData.args[key] = value;
131 }
132 agentFact.runAgent($scope.workspace, agentId, executorData).then(
133 function (response) {
134 $('#selectExecutorModal-' + agentId).modal('toggle');
100135 Notification.success("The Agent is running");
101136 setInterval(function () {
102137 $scope.disableExecute = false;
55 <div class="panel panel-default custom-panel">
66 <div class="panel-body custom-panel-body">
77 <span class="agent-token-container"><code>{{agentToken.token}}</code> &nbsp;<span class="cursor"
8 title="Copy token to clipboard"
8 title="Copy registration token to clipboard"
99 ng-click="copyToClipboard()"><i
1010 class="fa fa-copy"></i></span></span>
1111 <span class="glyphicon glyphicon-question-sign margin-right-15px"
9191
9292 <td class="ui-grid-cell-contents active-toggle" selection-model-ignore="">
9393 <button type="button" class="btn btn-info"
94 ng-click="selectAgent(agent)"
9495 title="Run Agent"
95 ng-click="runAgent(agent.id)" ng-disabled="!agent.active || disableExecute">
96 data-toggle="modal" data-target="#selectExecutorModal-{{agent.id}}" ng-disabled="!agent.active || disableExecute">
9697 Execute
9798 </button>
9899 <button type="button" class="btn"
105106 Remove
106107 </button>
107108 </td>
109
110
108111 </tr>
109112 </tbody>
110113 </table><!-- #hosts -->
117120
118121 </div>
119122
123 <div ng-repeat="agent in agents">
124 <div class="modal fade" id="selectExecutorModal-{{agent.id}}" tabindex="-1" role="dialog"
125 aria-labelledby="exampleModalLabel"
126 aria-hidden="true">
127 <div class="modal-dialog" role="document">
128 <div class="modal-content">
129 <div class="modal-header">
130 <h4 class="modal-title" id="selectExecutorModalLabel">Agent Executors</h4>
131 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
132 <span aria-hidden="true">&times;</span>
133 </button>
134 </div>
135 <form role="form" name="form">
136 <div class="modal-body">
137
138 <div class="form-group">
139 <label for="selectedExecutor">Select Executor</label>
140 <select class="form-control" ng-options="executor as executor.name for executor in executors track by executor.id" id="selectedExecutor" ng-model="data.selectedExecutor"></select>
141 </div>
142
143 <div class="var-item margin-top-65px row">
144 <div class="form-group col-md-6" ng-repeat="parameter in data.selectedExecutor.parameters_metadata">
145 <label for="paramName">{{parameter.name}}</label>
146 <input type="text" class="form-control" id="paramName" placeholder="{{parameter.name}}" ng-model="data.selectedExecutor.parameters_values[parameter.name]" ng-required="parameter.isRequired">
147 </div>
148 </div>
149 </div>
150 <div class="modal-footer">
151 <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
152 <button class="btn btn-primary" ng-disabled="form.$invalid" ng-click="runAgent(agent.id)">Run</button>
153 </div>
154 </form>
155 </div>
156 </div>
157 </div>
158 </div>
3434 };
3535
3636
37 agentFact.runAgent = function(wsName, agentId) {
37 agentFact.runAgent = function(wsName, agentId, executorData) {
3838 var deferred = $q.defer();
3939 $http.get('/_api/session').then(function(response){
40 var fd = new FormData();
41 fd.append('csrf_token', response.data.csrf_token);
40 let data = {
41 'csrf_token': response.data.csrf_token,
42 'executorData': executorData
43 };
4244 var postUrl = '_api/v2/ws/' + wsName + '/agents/' + agentId + '/run/';
43 $http.post(postUrl, fd, {
45 $http.post(postUrl, JSON.stringify(data), {
4446 transformRequest: angular.identity,
4547 withCredentials: false,
46 headers: {'Content-Type': undefined},
48 headers: {'Content-Type': 'application/json'}
4749 }).then(
4850 function(tokenResponse) {
4951 deferred.resolve(tokenResponse)
99 $scope.selected_cf = {
1010 field_display_name: "",
1111 field_name: "",
12 field_metadata: [],
1213 field_order: null,
1314 field_type: null,
1415 table_name: 'vulnerability'
1516 };
1617 $scope.isEditable = false;
17
18 $scope.data = {
19 newOption : ''
20 };
1821
1922 $scope.models = {
2023 selected: null
8689 }
8790 };
8891
92 $scope.addOption = function(){
93 if ( $scope.data.newOption !== ''){
94 $scope.selected_cf.field_metadata.push($scope.data.newOption);
95 $scope.data.newOption = '';
96 }
97 };
98
8999 $scope.createCustomCustomField = function () {
90100 $scope.selected_cf.table_name = 'vulnerability';
91101
92102 if ($scope.selected_cf.field_order === null)
93103 $scope.selected_cf.field_order = getMaxOrder() + 1;
94104
105 if($scope.selected_cf.field_metadata.length === 0){
106 $scope.selected_cf.field_metadata = null;
107 }
108
109 if ($scope.selected_cf.field_type === 'choice'){
110 $scope.selected_cf.field_metadata = JSON.stringify($scope.selected_cf.field_metadata)
111 }
95112 customFieldFact.createCustomField($scope.selected_cf).then(
96113 function (response) {
97114 $scope.customFields.push(response.data);
101118
102119
103120 $scope.updateCustomCustomField = function () {
121 if($scope.selected_cf.field_metadata.length === 0){
122 $scope.selected_cf.field_metadata = null;
123 }
124
125 if ($scope.selected_cf.field_type === 'choice'){
126 $scope.selected_cf.field_metadata = JSON.stringify($scope.selected_cf.field_metadata)
127 }
128
104129 customFieldFact.updateCustomField($scope.selected_cf).then(
105130 function (response) {
106131 if (response) {
148173
149174 $scope.setCustomField = function (cf) {
150175 $scope.selected_cf = angular.copy(cf);
176 if (cf.field_type === 'choice'){
177 $scope.selected_cf.field_metadata = cf.field_metadata === null ? [] : JSON.parse(cf.field_metadata);
178 }
151179 $scope.isEditable = true;
152180 $scope.changeType(cf.field_type);
153181
166194 case "int":
167195 color = '#932ebe';
168196 break;
197 case "choice":
198 color = '#be2743';
199 break;
169200 default:
170201 color = '#AAAAAA';
171202 break;
183214 $scope.clearSelection = function () {
184215 $scope.selected_cf = {
185216 field_display_name: "",
217 field_metadata: [],
186218 field_name: "",
187219 field_order: null,
188220 field_type: null
2020 <div class="cf-type type-int" ng-if="cf.field_type == 'int'">INTEGER</div>
2121 <div class="cf-type type-list" ng-if="cf.field_type == 'list'">LIST</div>
2222 <div class="cf-type type-str" ng-if="cf.field_type == 'str'">STRING</div>
23 <div class="cf-type type-choice" ng-if="cf.field_type == 'choice'">CHOICE</div>
2324 </div>
2425 <div class="cf-actions col-md-2">
2526 <span class="cf-icon-action" ng-click="_delete(cf)"><i
5758 <div class="tab-pane-header">Type</div>
5859 <button type="button" class="dropdown-toggle btn-change-property primary-btn"
5960 data-toggle="dropdown"
60 id="btn-chg-type">
61 id="btn-chg-type"
62 ng-disabled="isEditable == true">
6163 <span ng-if="selected_cf.field_type == null">UNDEFINED</span>
6264 <span ng-if="selected_cf.field_type == 'int'">INTEGER</span>
6365 <span ng-if="selected_cf.field_type == 'list'">LIST</span>
6466 <span ng-if="selected_cf.field_type == 'str'">STRING</span>
67 <span ng-if="selected_cf.field_type == 'choice'">CHOICE</span>
6568 </button>
6669 <button type="button" class="dropdown-toggle secondary-btn btn-change-property"
6770 data-toggle="dropdown"
6871 id="caret-chg-type"
69 title="Change type">
72 title="Change type"
73 ng-disabled="isEditable == true">
7074 <span> <i class="fa fa-angle-down fa-lg" aria-hidden="true"></i> </span>
7175 </button>
7276 <ul class="dropdown-menu dropdown-menu-right" role="menu">
7983 ng-click="changeType('int')">INTEGER</a>
8084 <a class="ws"
8185 ng-click="changeType('list')">LIST</a>
86 <a class="ws"
87 ng-click="changeType('choice')">CHOICE</a>
8288 </li>
8389 </ul>
8490 </div>
8591
86
87 <!--<div class="col-md-6">-->
88 <!--<div class="tab-pane-header">Order</div>-->
89 <!--<div class="form-group">-->
90 <!--<label class="sr-only control-label" for="cf_order">Order</label>-->
91 <!--<input type="text" class="form-control" id="cf_order" placeholder="Order"-->
92 <!--ng-model="selected_cf.field_order" check-custom-type="int" title="Type only numbers"/>-->
93 <!--</div>&lt;!&ndash; .form-group &ndash;&gt;-->
94 <!--</div>-->
92 <div class="add-options col-md-12 margin-top-30px" ng-if="selected_cf.field_type == 'choice'">
93 <div class="form-group">
94 <div class="col-md-12">
95 <div class="tab-pane-header">Options</div>
96 <div class="input-group margin-bottom-sm">
97 <label class="sr-only" for="option">Options</label>
98 <input type="text" class="form-control" id="option" placeholder="Option"
99 ng-model="data.newOption"/>
100 <span class="input-group-addon cursor" ng-click="addOption()">
101 <i class="fa fa-plus-circle"></i>
102 </span>
103 </div>
104 </div>
105 <div class="col-md-12 reference" ng-repeat="option in selected_cf.field_metadata">
106 <div class="input-group margin-bottom-sm">
107 <label class="sr-only" for="vuln-refs-create">Options</label>
108 <input type="text" class="form-control" id="vuln-refs-create" placeholder="Option"
109 ng-model="option"
110 role="button"
111 readonly/>
112 <span class="input-group-addon cursor" ng-click="selected_cf.field_metadata.splice($index, 1)"><i
113 class="fa fa-minus-circle"></i></span>
114 </div>
115 </div>
116 </div><!-- .form-group -->
117 </div>
95118 </div>
96119
97120 <div class="btn-tools">
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4
5 var evidenceRender = function () {
6 // This could be a lot more simple code, but
7 // we found a bug on filename with spaces and we need to espape it.
8 // take a look to the history of this extension to see the simple version of this code
9 var ws_index = window.location.href.split('/').indexOf('ws');
10 var workspace = window.location.href.split('/')[ws_index + 1];
11 var evidenceExtension = {
12 type: 'lang',
13 filter: function(text, converter) {
14 var regex = /\(evidence\:(\w+)\:(\d+)\:([a-zA-Z0-9\s_\\.\-\(:]+\.\w+)\)/g;
15 var matches = text.match(regex);
16 var markdown = text;
17 var filename;
18 for (var evidence_index in matches) {
19 filename = escape(matches[evidence_index].split(':')[3].slice(0, -1));
20 evidence = matches[evidence_index].replace(regex, '![$1 With Id $2 Evidence ](' + window.location.origin + '/_api/v2/ws/' + workspace + '/vulns/$2/attachment/' + filename + ' =500x281)');
21 console.log(evidence);
22 markdown = markdown.replace(matches[evidence_index], evidence);
23 }
24 return markdown;
25 }
26 };
27 return [evidenceExtension];
28 }
29
30 angular.module('faradayApp').filter('markdown', function () {
31 return function (md) {
32 var converter = new showdown.Converter({extensions: ['table', evidenceRender]});
33 converter.setOption('tables', 'true');
34 converter.setOption('tasklists', 'true');
35 return converter.makeHtml(md);
36 }
37 });
8181 </div>
8282 <div class="jumbotron" ng-class="{ 'jumbotron-font':header === 'workspace comparison' }">
8383 <h1><b>Welcome to the <span style="text-transform: capitalize;">{{header}}</span> panel!</b></h1>
84 <p>This feature belongs to our commercial versions</p>
85 <p>For more information, please contact us at <span class="bold">[email protected]</span> or visit <a href="http://www.faradaysec.com">www.faradaysec.com</a> !</p>
84 <br><br><br><br>
85 <p>This feature belongs to our commercial versions.
86 <br>
87 For more information, please contact us at <span class="bold">[email protected]</span> or visit <a href="http://www.faradaysec.com">www.faradaysec.com</a>!</p>
8688 </div><!-- .jumbotron -->
8789 </div><!-- #reports-main --></div><!-- .right-main -->
8890 </section><!-- #main -->
55 <h5>{{msg}}</h5>
66 </div>
77 <div class="modal-body change-padding-to-margin">
8 <div class="severities">
8 <div class="form-group" style="min-height: 18px">
9 <div class="col-md-12 form-group severities">
910 <button type="button" class="btn btn-default dropdown-toggle color-{{data.property}}" data-toggle="dropdown" title="Change severity">
1011 {{data.property || 'Edit property'}} <span class="caret"></span>
1112 </button>
99 $scope.osint = osint;
1010 $scope.sortField = 'name';
1111 $scope.sortReverse = false;
12 $scope.clipText = "Copy to Clipboard";
12 $scope.clipText = "Copy names to Clipboard";
1313 $scope.workspace = workspace
1414
1515 // toggles sort field and order
3232 $scope.name = srv_name;
3333 $scope.hosts = hosts;
3434 $scope.clip = "";
35 $scope.hosts.forEach(function(h, index){
36 $scope.clip += h.name + "\n";
35 $scope.hosts.forEach(function(h, index, array){
3736 let port = $scope.getPort(h.service_summaries);
3837 $scope.hosts[index].port = port;
38 if(index === array.length -1)
39 $scope.clip += h.name + ":" + port;
40 else
41 $scope.clip += h.name + ":" + port + " - ";
3942 });
4043 });
4144
22 <!-- See the file 'doc/LICENSE' for the license information -->
33
44 <div class="modal-header" hotkey="{enter:ok}">
5 <h3 ng-style="{'word-wrap': 'break-word'}">Services for {{name}} ({{hosts.length}} total)</h3>
5 <h3 ng-style="{'word-wrap': 'break-word'}">Hosts for {{name}} ({{hosts.length}} total)</h3>
66 </div>
77
88 <div class="modal-body">
1010 <thead>
1111 <tr class="ui-grid-header">
1212 <th class="ui-grid-cell-contents ui-grid-header-cell">
13 <span href="" ng-click="toggleSort('hostnames')">Hostnames</span>
13 <span href="" ng-click="toggleSort('name')">Name</span>
1414 </th>
1515 <th class="ui-grid-cell-contents ui-grid-header-cell">
16 <span href="" ng-click="toggleSort('name')">Name</span>
16 <span href="" ng-click="toggleSort('hostnames')">Hostnames</span>
1717 </th>
1818 <th class="ui-grid-cell-contents ui-grid-header-cell">
1919 <span href="" ng-click="toggleSort('os')">OS</span>
2525 </thead>
2626 <tbody>
2727 <tr class="ui-grid-row" ng-repeat="host in hosts | orderBy:sortField:sortReverse">
28 <td class="ui-grid-cell-contents">
29 <a ng-href="#/host/ws/{{workspace}}/hid/{{host._id}}" class="ng-binding" ng-click="ok()">{{host.name}}:{{host.port}}</a>
30 </td>
2831 <td class="ui-grid-cell-contents" ng-if="host.hostnames.length > 0">
29 <span ng-repeat="hostname in host.hostnames">{{hostname}}&nbsp;</span>
32 <span ng-repeat="hostname in host.hostnames">{{hostname}}&nbsp;<br></span>
3033 </td>
3134 <td class="ui-grid-cell-contents" ng-if="host.hostnames.length == 0">
3235 <span>-</span>
33 </td>
34 <td class="ui-grid-cell-contents">
35 <a ng-href="#/host/ws/{{workspace}}/hid/{{host._id}}" class="ng-binding" ng-click="ok()">{{host.name}}:{{host.port}}</a>
3636 </td>
3737 <td class="ui-grid-cell-contents">
3838 {{host.os}}
4343 </tr>
4444 </tbody>
4545 </table>
46 <div class="">
46 <div class="modal-footer">
4747 <div class="col-md-6" style="padding: 0px">
48 <button class="btn btn-danger" clipboard supported="supported" text="clip" on-copied="messageCopied()">{{clipText}}</button>
48 <button class="btn btn-danger pull-left" clipboard supported="supported" text="clip" on-copied="messageCopied()" ng-disabled="clipText === 'Copied!'">{{clipText}}</button>
4949 </div>
50
5150 <div class="col-md-6 btn-group" style="padding: 0px">
52 <button class="btn btn-success pull-right" ng-click="ok()">OK</button>
51 <button class="btn btn-success pull-right" ng-click="ok()" id="buttonAtRight">OK</button>
5352 </div>
5453 </div>
55 <div class="modal-footer"></div>
5654 </div>
88 <div class="modal-body">
99 <span class="help-block">
1010 <p> <strong>CSV File Example (must contain the headers)</strong></p>
11 <p> ip, description, os, hostnames</p>
12 <p> 10.10.10.10, test_host, linux, "['localhost', 'test_host']"</p>
11 <p> ip,description,os,hostnames</p>
12 <p> 10.10.10.10,test_host,linux,"['localhost','test_host']"</p>
1313 </span>
1414 <div class="pl">
1515 <span class="file-container">
2828 </div>
2929 </div>
3030 </form>
31
31
1414 <div class="form-group" ng-class="{'has-error': form.name.$invalid }">
1515 <div class="col-md-12">
1616 <label class="sr-only" for="name">Name</label>
17 <input type="text" class="form-control" id="name" name="name" placeholder="Name" ng-model="data.name" />
17 <input type="text" class="form-control" id="name" required name="name" placeholder="Name" ng-model="data.name" />
1818 </div>
1919 </div><!-- .form-group -->
2020 <div class="form-group">
3838 <h5>Port</h5>
3939 <div class="input-margin">
4040 <label class="sr-only" for="ports">Port</label>
41 <input type="number" min="0" class="form-control" id="ports" placeholder="Port" ng-model="data.ports"/>
41 <input type="number" min="0" max="65535" class="form-control" id="ports" placeholder="Port" ng-model="data.ports"/>
4242 </div>
4343 </div>
4444 <div class="col-md-3 protocol">
1414 <div class="form-group">
1515 <div class="col-md-12">
1616 <label class="sr-only" for="service">Name</label>
17 <input type="text" class="form-control" id="service" name="name" placeholder="Name" ng-model="data.name" />
17 <input type="text" class="form-control" id="service" required name="name" placeholder="Name" ng-model="data.name" />
1818 </div>
1919 </div><!-- .form-group -->
2020 <div class="form-group">
3838 <h5>Port</h5>
3939 <div class="input-margin" ng-class="{'has-error': form.ports.$invalid }">
4040 <label class="sr-only" for="ports">Port</label>
41 <input type="number" min="0" class="form-control" id="ports" name="ports" placeholder="Port" ng-model="data.ports" required/>
41 <input type="number" min="0" max="65535" class="form-control" id="ports" name="ports" placeholder="Port" ng-model="data.ports" required/>
4242 </div>
4343 </div>
4444 <div class="col-md-3 protocol" ng-class="{'has-error': form.protocol.$invalid }">
77 restrict: 'E',
88 scope: true,
99 replace: true,
10 template: '<div><div class="tab-pane-header">{{cf.field_display_name}}</div> \n\
11 <div class="form-group" ng-if="cf.field_type !== \'list\'"> \n\
12 <label class="sr-only" for="{{cf.field_name}}">{{cf.field_display_name}}</label> \n\
13 <input type="text" class="form-control input-sm" id="{{cf.field_name}}" name="{{cf.field_name}}" \n\
14 placeholder="{{cf.field_display_name}}" \n\
15 ng-model="modal.data.custom_fields[cf.field_name]" check-custom-type="{{cf.field_type}}" \n\
16 uib-tooltip="{{(cf.field_type === \'int\') ? \'Type only numbers\' : \'Input type text\'}}"/> \n\
17 </div> \n\
18 <div class="form-group " ng-if="cf.field_type === \'list\'" ng-class="modal.data.custom_fields[cf.field_name].length > 0 ? \'no-margin-bottom\' : \'\'">\n\
19 <div class="input-group"> \n\
10 template: '<div>\
11 <div ng-if="cf.field_type === \'str\'" ng-init="isEditable = true"> \n\
12 <div class="tab-pane-header" ng-dblclick="isEditable = true" title="Double click to edit">{{cf.field_display_name}} <span class="glyphicon glyphicon-question-sign" title="Edit using markdown code"></span></div> \n\
13 <div class="form-group"> \n\
2014 <label class="sr-only" for="{{cf.field_name}}">{{cf.field_display_name}}</label> \n\
21 <input type="text" class="form-control input-sm" id="{{cf.field_name}}_list" name="{{cf.field_name}}" \n\
22 placeholder="{{cf.field_display_name}}" \n\
23 ng-model="valueField" \n\
24 uib-tooltip="Input type list"/> \n\
25 <span class="input-group-addon cursor" ng-click="newValueField(valueField)"><i class="fa fa-plus-circle"></i></span> \n\
15 <textarea class="form-control" rows="5" id="vuln-desc" name="desc" ng-show="isEditable === true" \n\
16 ng-model="modal.data.custom_fields[cf.field_name]" ng-bind-html="modal.data.custom_fields[cf.field_name] | markdown" \n\
17 style="margin: 0 2px 0 0;" ng-blur="isEditable = isEditable.length==0 || !modal.data.custom_fields[cf.field_name]" autofocus> \n\
18 </textarea> \n\
19 <div class="col-md-12" ng-cloak ng-show="modal.data.custom_fields[cf.field_name].length > 0 && isEditable === false"> \n\
20 <div class="markdown-preview" style="height: 100px;!important;" ng-bind-html="modal.data.custom_fields[cf.field_name] | markdown" ng-dblclick="isEditable = true">{{modal.data.custom_fields[cf.field_name] | markdown}}</div> \n\
21 </div> \n\
2622 </div> \n\
2723 </div> \n\
28 <div class="reference" ng-repeat="item in modal.data.custom_fields[cf.field_name] track by $index" ng-class="{\'last-item-field\':$last}" ng-if="cf.field_type === \'list\'"> \n\
29 <div class="input-group margin-bottom-sm"> \n\
30 <label class="sr-only" for="vuln-refs-create">{{cf.field_display_name}}</label> \n\
31 <input ng-if="item.value" type="text" class="form-control input-sm" id="vuln-refs-create" placeholder="{{cf.field_display_name}}" \n\
32 ng-model="item.value" \n\
33 role="button" readonly/> \n\
34 <input ng-if="!item.value" type="text" class="form-control input-sm" id="vuln-refs-create" placeholder="{{cf.field_display_name}}" \n\
35 ng-model="item" \n\
36 role="button" readonly/> \n\
37 <span class="input-group-addon cursor" ng-click="modal.data.custom_fields[cf.field_name].splice($index, 1)"> \n\
38 <i class="fa fa-minus-circle"></i></span> \n\
24 <div ng-if="cf.field_type === \'int\'"> \n\
25 <div class="tab-pane-header">{{cf.field_display_name}}</div> \n\
26 <div class="form-group"> \n\
27 <label class="sr-only" for="{{cf.field_name}}">{{cf.field_display_name}}</label> \n\
28 <input type="text" class="form-control input-sm" id="{{cf.field_name}}" name="{{cf.field_name}}" \n\
29 placeholder="{{cf.field_display_name}}" \n\
30 ng-model="modal.data.custom_fields[cf.field_name]" check-custom-type="{{cf.field_type}}" \n\
31 uib-tooltip="Type only numbers"/> \n\
3932 </div> \n\
33 </div> \n\ \
34 <div ng-if="cf.field_type === \'choice\'"> \n\
35 <div class="tab-pane-header">{{cf.field_display_name}}</div> \n\
36 <div class="btn-group col-md-6 col-sm-6 col-xs-6 btn-cf-choice" ng-if="cf.field_type === \'choice\'"> \n\
37 <button type = "button" class="dropdown-toggle btn-change-property primary-btn btn-primary-white" data-toggle = "dropdown" id="btn-chg-choice" title="Choices">\n\
38 <span ng-if="modal.data.custom_fields[cf.field_name] !== null">{{modal.data.custom_fields[cf.field_name]}}</span>\n\
39 <span ng-if="modal.data.custom_fields[cf.field_name] === null">Select {{cf.field_display_name}}</span>\n\
40 </button>\n\
41 <button type="button" class="dropdown-toggle secondary-btn btn-change-property btn-secondary-white" data-toggle="dropdown" id="caret-choice" title="Choices">\n\
42 <span> <i class="fa fa-angle-down fa-lg" aria-hidden="true"></i> </span> \n\
43 </button> \n\
44 <ul class="dropdown-menu dropdown-menu-right col-md-12 dropd-cf-choice" role="menu"> \n\
45 <li ng-repeat="choice in parserOptions(cf.field_metadata)">\n\
46 <a class="ws" ng-click="modal.data.custom_fields[cf.field_name] = choice">{{choice}}</a> \n\
47 </li>\n\
48 </ul>\n\
49 </div> \n\
50 </div> \n\
51 <div ng-if="cf.field_type === \'list\'"> \n\
52 <div class="tab-pane-header">{{cf.field_display_name}}</div> \n\
53 <div class="form-group" ng-class="modal.data.custom_fields[cf.field_name].length > 0 ? \'no-margin-bottom\' : \'\'"> \n\
54 <div class="input-group"> \n\
55 <label class="sr-only" for="{{cf.field_name}}">{{cf.field_display_name}}</label> \n\
56 <input type="text" class="form-control input-sm" id="{{cf.field_name}}_list" name="{{cf.field_name}}" \n\
57 placeholder="{{cf.field_display_name}}" \n\
58 ng-model="valueField" \n\
59 uib-tooltip="Input type list"/> \n\
60 <span class="input-group-addon cursor" ng-click="newValueField(valueField)"><i class="fa fa-plus-circle"></i></span> \n\
61 </div> \n\
62 </div> \n\
63 <div class="reference" ng-repeat="item in modal.data.custom_fields[cf.field_name] track by $index" ng-class="{\'last-item-field\':$last}" ng-if="cf.field_type === \'list\'"> \n\
64 <div class="input-group margin-bottom-sm"> \n\
65 <label class="sr-only" for="vuln-refs-create">{{cf.field_display_name}}</label> \n\
66 <input ng-if="item.value" type="text" class="form-control input-sm" id="vuln-refs-create" placeholder="{{cf.field_display_name}}" \n\
67 ng-model="item.value" \n\
68 role="button" readonly/> \n\
69 <input ng-if="!item.value" type="text" class="form-control input-sm" id="vuln-refs-create" placeholder="{{cf.field_display_name}}" \n\
70 ng-model="item" \n\
71 role="button" readonly/> \n\
72 <span class="input-group-addon cursor" ng-click="modal.data.custom_fields[cf.field_name].splice($index, 1)"> \n\
73 <i class="fa fa-minus-circle"></i></span> \n\
74 </div> \n\
75 </div> \n\
4076 </div> \n\
4177 </div></div>',
4278 link: function (scope, element, attrs) {
5490 angular.element('#'+scope.cf.field_name+'_list').val("");
5591 }
5692 }
93
94 scope.parserOptions = function (rawOptions) {
95 return JSON.parse(rawOptions)
96 }
5797 }
5898 }
5999 }]);
77 restrict: 'E',
88 scope: false,
99 replace: true,
10 template: '<div><div class="tab-pane-header"><i class="fa fa-spinner fa-spin" ng-show="isUpdatingVuln === true && fieldToEdit === cf.field_name"></i> {{cf.field_display_name}}</div> \n\
11 <div class="form-group" ng-if="cf.field_type !== \'list\'"> \n\
12 <label class="sr-only" for="{{cf.field_name}}">{{cf.field_display_name}}</label> \n\
13 <input type="text" class="form-control input-sm" id="{{cf.field_name}}" name="{{cf.field_name}}" \n\
14 placeholder="{{cf.field_display_name}}" \n\
15 ng-focus="activeEditPreview(cf.field_name)" \
16 ng-blur="processToEditPreview(false)"\
17 ng-model="lastClickedVuln.custom_fields[cf.field_name]" check-custom-type="{{cf.field_type}}" \n\
18 uib-tooltip="{{(cf.field_type === \'int\') ? \'Type only numbers\' : \'Input type text\'}}"/> \n\
19 </div> \n\
20 <div class="form-group " ng-if="cf.field_type === \'list\'" ng-class="lastClickedVuln.custom_fields[cf.field_name].length > 0 ? \'no-margin-bottom\' : \'\'">\n\
21 <div class="input-group"> \n\
10 template: '<div> \n\
11 <div ng-if="cf.field_type === \'str\'" ng-init="isEditable = true"> \n\
12 <div class="tab-pane-header" ng-dblclick="isEditable = true" title="Double click to edit"><i class="fa fa-spinner fa-spin" ng-show="isUpdatingVuln === true && fieldToEdit === cf.field_name"></i> {{cf.field_display_name}} <span class="glyphicon glyphicon-question-sign" title="Edit using markdown code"></span></div> \n\
13 <div class="form-group"> \n\
2214 <label class="sr-only" for="{{cf.field_name}}">{{cf.field_display_name}}</label> \n\
23 <input type="text" class="form-control input-sm" id="{{cf.field_name}}" name="{{cf.field_name}}" \n\
24 placeholder="{{cf.field_display_name}}" \n\
25 ng-focus="activeEditPreview(cf.field_name)" \
26 ng-model="valueField" \n\
27 uib-tooltip="Input type list"/> \n\
28 <span class="input-group-addon cursor" ng-click="newValueField(valueField)"><i class="fa fa-plus-circle"></i></span> \n\
15 <textarea class="form-control" placeholder="{{cf.field_display_name}}" rows="5" id="vuln-desc" name="desc" ng-show="isEditable === true" \n\
16 ng-focus="activeEditPreview(cf.field_name)" ng-blur="processToEditPreview(false); isEditable = isEditable.length==0 || !lastClickedVuln.custom_fields[cf.field_name]" ng-model="lastClickedVuln.custom_fields[cf.field_name]" ng-bind-html="lastClickedVuln.custom_fields[cf.field_name] | markdown" \n\
17 style="margin: 0 2px 0 0;" autofocus> \n\
18 </textarea> \n\
19 <div class="col-md-12" ng-cloak ng-show="lastClickedVuln.custom_fields[cf.field_name].length > 0 && isEditable === false"> \n\
20 <div class="markdown-preview" style="height: 100px;!important;" ng-bind-html="lastClickedVuln.custom_fields[cf.field_name] | markdown" ng-dblclick="isEditable = true">{{lastClickedVuln.custom_fields[cf.field_name] | markdown}}</div> \n\
21 </div> \n\
2922 </div> \n\
3023 </div> \n\
31 <div class="reference" ng-repeat="item in lastClickedVuln.custom_fields[cf.field_name] track by $index" ng-class="{\'last-item-field\':$last}" ng-if="cf.field_type === \'list\'"> \n\
24 <div ng-if="cf.field_type === \'int\'"> \n\
25 <div class="tab-pane-header"><i class="fa fa-spinner fa-spin" ng-show="isUpdatingVuln === true && fieldToEdit === cf.field_name"></i> {{cf.field_display_name}}</div> \n\
26 <div class="form-group">\n\
27 <label class="sr-only" for="{{cf.field_name}}">{{cf.field_display_name}}</label> \n\
28 <input type="text" class="form-control input-sm" id="{{cf.field_name}}" name="{{cf.field_name}}" \n\
29 placeholder="{{cf.field_display_name}}" \n\
30 ng-focus="activeEditPreview(cf.field_name)" \
31 ng-blur="processToEditPreview(false)"\
32 ng-model="lastClickedVuln.custom_fields[cf.field_name]" check-custom-type="{{cf.field_type}}" \n\
33 uib-tooltip="Type only numbers"/> \n\
34 </div> \n\
35 </div> \n\ \
36 <div ng-if="cf.field_type === \'choice\'"> \n\
37 <div class="tab-pane-header"><i class="fa fa-spinner fa-spin" ng-show="isUpdatingVuln === true && fieldToEdit === cf.field_name"></i> {{cf.field_display_name}}</div> \n\
38 <div class="btn-group col-md-6 col-sm-6 col-xs-6 btn-cf-choice" ng-if="cf.field_type === \'choice\'"> \n\
39 <button type="button" class="dropdown-toggle btn-change-property primary-btn btn-primary-white" data-toggle = "dropdown" id = "btn-chg-choice" title = "Choices">\n\
40 <span ng-if="lastClickedVuln.custom_fields[cf.field_name] !== null" > {{lastClickedVuln.custom_fields[cf.field_name]}}</span>\n\
41 <span ng-if="lastClickedVuln.custom_fields[cf.field_name] === null">Select {{cf.field_display_name}}</span> \n\
42 </button> \n\
43 <button type="button" class="dropdown-toggle secondary-btn btn-change-property btn-secondary-white" data-toggle="dropdown" id="caret-choice" title="Choices"> \n\
44 <span><i class="fa fa-angle-down fa-lg" aria-hidden="true"></i> </span> \n\
45 </button> \n\
46 <ul class="dropdown-menu dropdown-menu-right col-md-12 dropd-cf-choice" role="menu"> \n\
47 <li ng-repeat="choice in parserOptions(cf.field_metadata)"> \n\
48 <a class="ws" href="javascript:;" ng-click="onChangeChoiceCf(choice)">{{choice}}</a> \n\
49 </li> \n\
50 </ul> \n\
51 </div> \n\
52 </div> \n\
53 <div ng-if="cf.field_type === \'list\'"> \n\
54 <div class="tab-pane-header"><i class="fa fa-spinner fa-spin" ng-show="isUpdatingVuln === true && fieldToEdit === cf.field_name"></i> {{cf.field_display_name}}</div> \n\
55 <div class="form-group" ng-class="lastClickedVuln.custom_fields[cf.field_name].length > 0 ? \'no-margin-bottom\' : \'\'">\n\
56 <div class="input-group"> \n\
57 <label class="sr-only" for="{{cf.field_name}}">{{cf.field_display_name}}</label> \n\
58 <input type="text" class="form-control input-sm" id="{{cf.field_name}}" name="{{cf.field_name}}" \n\
59 placeholder="{{cf.field_display_name}}" \n\
60 ng-focus="activeEditPreview(cf.field_name)" \
61 ng-model="valueField" \n\
62 uib-tooltip="Input type list"/> \n\
63 <span class="input-group-addon cursor" ng-click="newValueField(valueField)"><i class="fa fa-plus-circle"></i></span> \n\
64 </div> \n\
65 </div> \n\
66 <div class="reference" ng-repeat="item in lastClickedVuln.custom_fields[cf.field_name] track by $index" ng-class="{\'last-item-field\':$last}" ng-if="cf.field_type === \'list\'"> \n\
3267 <div class="input-group margin-bottom-sm"> \n\
3368 <label class="sr-only" for="vuln-refs-create">{{cf.field_display_name}}</label> \n\
3469 <input ng-if="item.value" type="text" class="form-control input-sm" id="vuln-refs-create" placeholder="{{cf.field_display_name}}" \n\
4580 link: function (scope, element, attrs) {
4681 scope.newValueField = function (valueField) {
4782 if (valueField !== "" && valueField !== undefined) {
48 if(scope.lastClickedVuln.custom_fields[scope.cf.field_name] === null )
83 if (scope.lastClickedVuln.custom_fields[scope.cf.field_name] === null)
4984 scope.lastClickedVuln.custom_fields[scope.cf.field_name] = [];
5085
5186 // we need to check if the ref already exists
52 if (scope.lastClickedVuln.custom_fields[scope.cf.field_name].filter(function(field) {return field.value === valueField}).length === 0) {
87 if (scope.lastClickedVuln.custom_fields[scope.cf.field_name].filter(function (field) {
88 return field.value === valueField
89 }).length === 0) {
5390 scope.lastClickedVuln.custom_fields[scope.cf.field_name].push({value: valueField});
5491 scope.valueField = "";
5592 }
56 angular.element('#'+scope.cf.field_name).val("");
93 angular.element('#' + scope.cf.field_name).val("");
5794
5895 scope.fieldToEdit = scope.cf.field_name;
5996 scope.processToEditPreview(false);
69106 vulnsManager.updateVuln(scope.realVuln, scope.lastClickedVuln).then(function () {
70107 scope.isUpdatingVuln = false;
71108 scope.fieldToEdit = undefined;
72 }, function (data) {
73 scope.hideVulnPreview();
74 commonsFact.showMessage("Error updating vuln " + scope.realVuln.name + " (" + scope.realVuln._id + "): " + (data.message || JSON.stringify(data.messages)));
75 scope.fieldToEdit = undefined;
76 scope.isUpdatingVuln = false;
109 }, function (data) {
110 scope.hideVulnPreview();
111 commonsFact.showMessage("Error updating vuln " + scope.realVuln.name + " (" + scope.realVuln._id + "): " + (data.message || JSON.stringify(data.messages)));
112 scope.fieldToEdit = undefined;
113 scope.isUpdatingVuln = false;
77114
78 });
79 };
80 }
81 }
115 });
116 };
117
118 scope.onChangeChoiceCf = function (value) {
119 scope.fieldToEdit = scope.cf.field_name;
120 scope.activeEditPreview(scope.cf.field_name);
121 scope.lastClickedVuln.custom_fields[scope.cf.field_name] = value;
122 scope.processToEditPreview(false);
123 };
124
125 scope.parserOptions = function (rawOptions) {
126 return JSON.parse(rawOptions)
127 };
128 }}
82129 }]);
1616 $scope.easeofresolution = EASEOFRESOLUTION;
1717 $scope.data = new VulnModel;
1818 $scope.data.set(model);
19 $scope.impact = angular.copy($scope.data.impact);
19 $scope.impact = {
20 accountability: false,
21 availability: false,
22 confidentiality: false,
23 integrity: false
24 }
25 for (var [key, value] of Object.entries(angular.copy($scope.data.impact))) {
26 $scope.impact[key] = value
27 }
2028 $scope.policyviolations = clearList(angular.copy($scope.data.policyviolations), EXCLUDED_TOKENS);
2129 $scope.references = clearList(angular.copy($scope.data.refs), EXCLUDED_TOKENS);
2230 $scope.new_policyviolation = "";
8888 if (error.status == 409) {
8989 return "A workspace with that name already exists"
9090 }
91 return error;
91 return error.data.message;
9292 }
9393 }
9494 });
0 #!/usr/bin/env python2.7
10 # Faraday Penetration Test IDE
21 # Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
32 # See the file 'doc/LICENSE' for the license information
1514 import sqlalchemy
1615 import faraday.server.config
1716 import faraday.server.utils.logger
17 import faraday.server.web
1818 from faraday.server.models import db, Workspace
1919 from faraday.server.utils import daemonize
2020 from faraday.server.web import app
2121 from faraday.utils import dependencies
22 from faraday.utils.user_input import query_yes_no
2322 from faraday.server.config import FARADAY_BASE
2423 from alembic.script import ScriptDirectory
2524 from alembic.config import Config
3635 def setup_environment(check_deps=False):
3736 # Configuration files generation
3837 faraday.server.config.copy_default_config_to_local()
39
4038 if check_deps:
41
4239 # Check dependencies
4340 installed_deps, missing_deps, conflict_deps = dependencies.check_dependencies(
4441 requirements_file=faraday.server.config.REQUIREMENTS_FILE)
45
4642 logger.info("Checking dependencies...")
47
4843 if conflict_deps:
4944 logger.info("Some dependencies are old. Update them with \"pip install -r requirements_server.txt -U\"")
50
5145 logger.info("Dependencies met")
52
5346 # Web configuration file generation
5447 faraday.server.config.gen_web_config()
5548
7265
7366
7467 def run_server(args):
75 import faraday.server.web
76
7768 web_server = faraday.server.web.WebServer(enable_ssl=args.ssl)
7869 daemonize.create_pid_file(args.port)
7970 web_server.run()
8071
72
8173 def restart_server(args_port):
8274 devnull = open('/dev/null', 'w')
83
8475 if args_port:
8576 ports = [args_port]
8677 else:
8778 ports = daemonize.get_ports_running()
88
8979 if not ports:
9080 logger.error('Faraday Server is not running')
9181 sys.exit(1)
92
9382 for port in ports:
9483 stop_server(port)
95 params = ['/usr/bin/env', 'python2.7',\
84 params = ['/usr/bin/env', 'python3', # TODO que hacemos con esto???
9685 os.path.join(faraday.server.config.FARADAY_BASE, __file__), '--no-setup', '--port', str(port)]
97
9886 logger.info('Restarting Faraday Server...')
9987 subprocess.Popen(params, stdout=devnull, stderr=devnull)
10088 logger.info('Faraday Server is running as a daemon in port {}'.format(port))
114102 logger.error(
115103 '\n\n{RED}Could not connect to PostgreSQL.\n{WHITE}Please check: \n{YELLOW} * if database is running \n * configuration settings are correct. \n\n{WHITE}For first time installations execute{WHITE}: \n\n {GREEN} faraday-manage initdb\n\n'.format(GREEN=Fore.GREEN, YELLOW=Fore.YELLOW, WHITE=Fore.WHITE, RED=Fore.RED))
116104 sys.exit(1)
117 except sqlalchemy.exc.ProgrammingError:
105 except sqlalchemy.exc.ProgrammingError as e:
118106 logger.error(
119107 '\n\nn{WHITE}Missing migrations, please execute: \n\nfaraday-manage migrate'.format(WHITE=Fore.WHITE, RED=Fore.RED))
120108 sys.exit(1)
170158 parser.add_argument('--port', help='Overides server.ini port configuration')
171159 parser.add_argument('--websocket_port', help='Overides server.ini websocket port configuration')
172160 parser.add_argument('--bind_address', help='Overides server.ini bind_address configuration')
173
174161 f_version = faraday.__version__
175
176 parser.add_argument('-v', '--version', action='version',
177 version='Faraday v{version}'.format(version=f_version))
178
162 parser.add_argument('-v', '--version', action='version', version='Faraday v{version}'.format(version=f_version))
179163 args = parser.parse_args()
180
181164 if args.debug or faraday.server.config.faraday_server.debug:
182165 faraday.server.utils.logger.set_logging_level(faraday.server.config.DEBUG)
183
184166 if args.restart:
185167 restart_server(args.port)
186168 sys.exit()
187
188169 if args.stop:
189170 if args.port:
190171 sys.exit(0 if stop_server(args.port) else 1)
196177 for port in ports:
197178 exit_code += 0 if stop_server(port) else 1
198179 sys.exit(exit_code)
199
200180 else:
201181 if not args.port:
202182 args.port = '5985'
203
204
205183 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
206 result = sock.connect_ex((args.bind_address or faraday.server.config.faraday_server.bind_address, int(args.port or faraday.server.config.faraday_server.port)))
207
184 result = sock.connect_ex((args.bind_address or faraday.server.config.faraday_server.bind_address,
185 int(args.port or faraday.server.config.faraday_server.port)))
208186 if is_server_running(args.port) and result == 0:
209187 sys.exit(1)
210
211188 if result == 0:
212189 logger.error("Faraday Server port in use. Check your processes and run the server again...")
213190 sys.exit(1)
214
215191 # Overwrites config option if SSL is set by argument
216192 if args.ssl:
217193 faraday.server.config.ssl.enabled = 'true'
218
219194 if not args.no_setup:
220195 setup_environment(not args.nodeps)
221
222196 if args.port:
223197 faraday.server.config.faraday_server.port = args.port
224
225198 if args.bind_address:
226199 faraday.server.config.faraday_server.bind_address = args.bind_address
227
228200 if args.websocket_port:
229201 faraday.server.config.faraday_server.websocket_port = args.websocket_port
230
231202 if args.start:
232203 # Starts a new process on background with --ignore-setup
233204 # and without --start nor --stop
234205 devnull = open('/dev/null', 'w')
235 params = ['/usr/bin/env', 'python2.7', os.path.join(faraday.server.config.FARADAY_BASE, __file__), '--no-setup']
206 params = ['/usr/bin/env', 'python3', os.path.join(faraday.server.config.FARADAY_BASE, __file__), '--no-setup']
236207 arg_dict = vars(args)
237208 for arg in arg_dict:
238209 if arg not in ["start", "stop"] and arg_dict[arg]:
239210 params.append('--'+arg)
240 if arg_dict[arg] != True:
211 if not arg_dict[arg]:
241212 params.append(arg_dict[arg])
242213 logger.info('Faraday Server is running as a daemon')
243214 subprocess.Popen(params, stdout=devnull, stderr=devnull)
244
245215 elif not args.start:
246216 run_server(args)
247217
248
249 if __name__ == '__main__':
218 if __name__ == '__main__': # TODO Borrar???
250219 main()
220 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
6 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 import hashlib
77 import uuid
88 import socket
6262 return res.ok
6363 except Exception:
6464 return False
65
66
67 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66
77
88 def simple_decorator(decorator):
9 '''this decorator can be used to turn simple functions
9 """this decorator can be used to turn simple functions
1010 into well-behaved decorators, so long as the decorators
1111 are fairly simple. If a decorator expects a function and
1212 returns a function (no descriptors), and if it doesn't
1414 eligible to use this. Simply apply @simple_decorator to
1515 your decorator and it will automatically preserve the
1616 docstring and function attributes of functions to which
17 it is applied.'''
17 it is applied."""
1818 def new_decorator(f):
1919 g = decorator(f)
2020 g.__name__ = f.__name__
3434 return func(self, *args, **kwargs)
3535 return wrapper
3636
37 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2014 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 import sys
77 try:
88 from pip import main
4141 if not hasattr(sys, 'real_prefix'):
4242 pip_cmd.append('--user')
4343 main(pip_cmd)
44
45
46 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
5 """
66 """
77 This module will help us to retrieve information
88 about the app state and system information and
1212 import sys
1313 import traceback
1414 import threading
15 import requests
16 import hashlib
17 import platform
1518 import faraday.client.model.guiapi
16 from cStringIO import StringIO
19 from io import StringIO
1720 from faraday.client.gui.customevents import ShowExceptionCustomEvent
1821 from faraday.config.configuration import getInstanceConfiguration
1922 import json
2023 import time
2124
25 try:
26 import pip
27 except ImportError:
28 pass
29
2230 CONF = getInstanceConfiguration()
23
2431
2532
2633 def get_crash_log():
3946 Since this handler may be called from threads, the dialog must be created
4047 using gtk idle_add or signals to avoid issues.
4148 """
42 import hashlib
43 import platform
4449
4550 text = StringIO()
4651 traceback.print_exception(type, value, tb, file=text)
5762
5863 modules_info = ""
5964 try:
60 import pip
6165 modules_info = ",".join([ "%s=%s" % (x.key, x.version)
6266 for x in pip.get_installed_distributions()])
6367 except (ImportError, AttributeError):
8589
8690 def reportToDevelopers(name=None, *description):
8791 try:
88 import requests
89 import hashlib
90 import platform
9192
9293 uri = CONF.getTktPostUri()
9394 headers = json.loads(CONF.getApiParams())
126127 def run_with_except_hook(*args, **kw):
127128 try:
128129 run_old(*args, **kw)
129 except (KeyboardInterrupt, SystemExit):
130 raise
131 except Exception:
130 except Exception as e:
131 if isinstance(e, (KeyboardInterrupt, SystemExit)):
132 raise
132133 sys.excepthook(*sys.exc_info())
133134 self.run = run_with_except_hook
134135 threading.Thread.__init__ = init
136
137
138 # I'm Py3
0 '''
0 """
11 Faraday Penetration Test IDE
22 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44
5 '''
6
5 """
76 import sys
87
98 def query_yes_no(question, default="yes"):
2928
3029 while True:
3130 sys.stdout.write(question + prompt)
32 try:
33 choice = raw_input().lower()
34 except NameError:
35 choice = input().lower()
31 choice = input().lower()
3632 if default is not None and choice == '':
3733 return valid[default]
3834 elif choice in valid:
4137 sys.stdout.write("Please respond with 'yes' or 'no' "
4238 "(or 'y' or 'n').\n")
4339
40
41
42 # I'm Py3
44 # See the file 'doc/LICENSE' for the license information
55
66 # Developers: the code for faraday-server is located in faraday/start_server.py
7 from __future__ import absolute_import
78
89 from __future__ import print_function
910 import sys
2021 )
2122
2223 sys.exit(1)
24
25
26 # I'm Py3
44 # See the file 'doc/LICENSE' for the license information
55
66 # Developers: the code for faraday-manage is located in faraday/manage.py
7 from __future__ import absolute_import
78
89 from __future__ import print_function
910 import sys
2122
2223 sys.exit(1)
2324
25
26
27 # I'm Py3
88 our propiertary code. Not useful if you don't have access to
99 the code of Faraday Professional or Faraday Corporate
1010 '''
11 from __future__ import absolute_import
12 from __future__ import print_function
1113
1214 import os
1315 import re
114116 if target_version != version and \
115117 branch_exists(overriden_branch):
116118 branches_to_test.append(overriden_branch)
117 break # Don't test merge to black if has overriden pink branch
119 # break # Uncomment if want to cut the checker on merging to black if has overridden pink branch
118120 else:
119121 branches_to_test.append(BRANCH_FORMAT.format(target_version))
120122
142144 parser.add_argument('-l', '--log-level', default='debug')
143145 args = parser.parse_args()
144146 main(args.branch)
147
148
149 # I'm Py3
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 from __future__ import absolute_import
12 from __future__ import print_function
13
14 import os
15 import re
16 import sys
17 import subprocess
18 import logging
19 import argparse
20 from contextlib import contextmanager
21 from tempfile import mkdtemp
22 from shutil import rmtree
23
24 VERSIONS = ['white', 'pink', 'black']
25 BRANCH_FORMAT = 'origin/{}3/dev'
26
27 @contextmanager
28 def chdir(directory):
29 """Context manager to work in the specified directory"""
30 current = os.getcwd()
31 os.chdir(directory)
32 yield
33 os.chdir(current)
34
35 @contextmanager
36 def temp_worktree(branch=None):
37 """Context manager that creates a temporal worktree and
38 changes the current working directory, and when finished
39 removes the dir and runs a git worktree prune"""
40 directory = mkdtemp()
41 cmd = ["git", "worktree", "add", directory]
42 if branch is not None:
43 cmd.append(branch)
44 subprocess.check_output(cmd)
45 with chdir(directory):
46 yield
47 rmtree(directory)
48 subprocess.check_output(['git', 'worktree', 'prune'])
49
50 def check_merge(dst_branch, cur_branch='HEAD'):
51 """Return a boolean indicating if the merge from cur_branch
52 to dst_branch will merge without causing conflicts that need
53 manual resolution"""
54 # https://stackoverflow.com/questions/501407/is-there-a-git-merge-dry-run-option
55 with temp_worktree(dst_branch):
56 exit_code = subprocess.call(
57 ['git', 'merge', '--no-commit', '--no-ff', cur_branch])
58 # Use call because it will have exit code 128 when there is nothing to
59 # abort
60 subprocess.call(['git', 'merge', '--abort'])
61 return exit_code == 0
62
63
64 def get_current_branch():
65 """Return the current branch of the current workspace"""
66 # https://stackoverflow.com/questions/6245570/how-to-get-the-current-branch-name-in-git
67 branch = subprocess.check_output(
68 ['git', 'rev-parse', '--abbrev-ref', 'HEAD']).decode().strip()
69 if branch == 'HEAD':
70 # Probably in a detached state inside gitlab CI
71 # Fallback to the branch name defined in an env var
72 branch = 'origin/' + os.environ['CI_COMMIT_REF_NAME']
73 return branch
74
75
76 def branch_exists(branch_name):
77 exit_code = subprocess.call(
78 ['git', 'rev-parse', '--verify', '--quiet', branch_name])
79 if exit_code == 0:
80 return True
81 elif exit_code == 1:
82 return False
83 else:
84 raise ValueError('Error when checking for branch existence')
85
86
87 def version_of_branch(branch_name):
88 """
89 >>> version_of_branch('tkt_white_this_is_not_a_pink_branch')
90 'white'
91 """
92 positions = {version: branch_name.find(version)
93 for version in VERSIONS}
94 if all((pos < 0) for pos in positions.values()):
95 # The branch name doesn't contain white, pink or black
96 return
97 positions = {version: pos
98 for (version, pos) in positions.items()
99 if pos >= 0}
100 return min(positions.keys(), key=positions.get)
101
102
103 def main(branch):
104 logging.getLogger().setLevel(getattr(logging, args.log_level.upper()))
105 logger = logging # TODO FIXME
106 logger.info('Checking merge conflicts for branch %s', branch)
107 version = version_of_branch(branch)
108 if version is None:
109 logger.error('Unknown version name. Exiting')
110 sys.exit(-1)
111
112 versions_to_test = VERSIONS[VERSIONS.index(version):]
113 branches_to_test = []
114 for target_version in versions_to_test:
115 overriden_branch = branch.replace(version, target_version)
116 if target_version != version and \
117 branch_exists(overriden_branch):
118 branches_to_test.append(overriden_branch)
119 # break # Uncomment if want to cut the checker on merging to black if has overridden pink branch
120 else:
121 branches_to_test.append(BRANCH_FORMAT.format(target_version))
122
123 logging.info('Testing merges in branches %s' % branches_to_test)
124
125 success = True
126 cur_branch = branch
127 for dst_branch in branches_to_test:
128 result = check_merge(dst_branch, cur_branch)
129 if result:
130 logger.info("Merge into %s succeeded!", dst_branch)
131 else:
132 success = False
133 logger.error("Merge into %s failed :(", dst_branch)
134 print()
135 print()
136
137 if not success:
138 sys.exit(1)
139
140
141 if __name__ == "__main__":
142 parser = argparse.ArgumentParser()
143 parser.add_argument('-b', '--branch', default=get_current_branch())
144 parser.add_argument('-l', '--log-level', default='debug')
145 args = parser.parse_args()
146 main(args.branch)
147
148
149 # I'm Py3
6565 self.logger.error("The auto-claimed python file {path} as python is not python3".format(path=path))
6666 error_list.append(path)
6767 if not find_py3_ok_result and lint_ok_result:
68 self.logger.info("The file {path} is python3".format(path=path))
69 return 0 if lint_ok_result else 1, 1, [], error_list
68 self.logger.info("The file {path} is python3, adding the signature comment in the last line".format(path=path))
69 with open(path,"a+") as py3_file:
70 py3_file.writelines(["\n\n", PY3_MSG, "\n"])
71
72 if not lint_ok_result:
73 self.logger.info("The file {path} is python2".format(path=path))
74 return 1 if lint_ok_result else 0, 1, [], error_list
7075
7176 def analyse_folder(self, parent_path):
7277 are3, total, strs, error_files = 0, 0, [], []
8186 total += s_total
8287 strs.extend(s_strs)
8388 error_files.extend(s_error_files)
84 if total > 0:
89 if are3 != total and total > 0:
90 prtg = 100.0*are3/total
8591 strs.append('Analysed {path}, {are3}/{total} {prtg}%'
86 .format(path=parent_path, are3=are3, total=total, prtg=100.0*are3/total))
92 .format(path=parent_path, are3=are3, total=total, prtg=prtg))
8793 return are3, total, strs, error_files
8894
8995 def run(self):
90 _, _, strs, error_files = self.analyse_folder(os.getcwd())
96 are3, total, strs, error_files = self.analyse_folder(os.getcwd())
9197 for s in strs:
9298 self.logger.info(s)
9399 if len(error_files) > 0:
94100 for error_file in error_files:
95101 self.logger.error("The auto-claimed python file {path} as python is not python3".format(path=error_file))
96102 raise Exception("One or more auto-claimed python file(s) as python is(are) not python3")
103 if are3 == total:
104 print("100% coverage")
97105
98106
99107 def main(filename):
108 PYTLINT = ".pylintrc"
109 RENAMED = ".to_be_renamed"
110 os.rename(PYTLINT, RENAMED)
111
100112 if filename:
101113 import sys
102114 sys.stdout = open(filename, 'w')
103 Analyser().run()
104
115 try:
116 Analyser().run()
117 finally:
118 os.rename(RENAMED, PYTLINT)
105119
106120 if __name__ == "__main__":
107121 parser = argparse.ArgumentParser()
66 mockito>=1.0.12
77 pgcli>=1.8.2
88 requests>=2.18.4
9 tornado==4.5.1
9 tornado>=5.0.0
1010 tqdm>=4.15.0
1111 whoosh>=2.7.4
1212 cairocffi==0.9.0
1111 IPy>=0.83
1212 marshmallow<3.0.0
1313 Pillow>=4.2.1
14 pgcli
15 psycopg2==2.7.7
14 pgcli==2.1.1
15 psycopg2-binary==2.8.4
1616 pyasn1-modules>=0.0.11
1717 pyopenssl>=17.2.0
1818 python-dateutil>=2.6.0
3333 Flask-Restless==0.17.0
3434 simplejson>=3.16.0
3535 syslog-rfc5424-formatter==1.1.1
36 typing==3.6.6
3736 beautifulsoup4==4.7.1
3837 Flask-KVSession==0.6.2
3938 simplekv==0.13.0
4039 pypcapfile==0.12.0
4140 html2text==2019.8.11
41 distro==1.4.0
42 faraday-plugins==1.0rc1
44 ## See the file 'doc/LICENSE' for the license information
55 ###
66
7 from __future__ import absolute_import
8 from __future__ import print_function
79 import subprocess
810 import os
911 import argparse
1012 import time
1113 import shutil
1214 from pprint import pprint
13 try:
14 # py2.7
15 from faraday.client.configparser import ConfigParser, NoSectionError, NoOptionError
16 except ImportError:
17 # py3
18 from ConfigParser import ConfigParser, NoSectionError, NoOptionError
15 from configparser import ConfigParser, NoSectionError, NoOptionError
1916 #from config import config
2017
2118 def setup_config_path():
6562 def main():
6663 lockf = ".lock.pod"
6764 if not lockFile(lockf):
68 print "You can run only one instance of cscan (%s)" % lockf
65 print("You can run only one instance of cscan (%s)" % lockf)
6966 exit(0)
7067
7168 config = init_config()
113110 targets = target_list(script, categories)
114111
115112 cmd = "%s %s %s %s" % (script, targets, output, logdir)
116 print "\n\nRunning: %s" % cmd
113 print("\n\nRunning: %s" % cmd)
117114 proc = subprocess.call(cmd, shell=True, stdin=None, env=dict(env))
118115
119116 #Remove lockfile
121118
122119 if __name__ == "__main__":
123120 main()
121 # I'm Py3
55 #
66 # For more information contact us at carbonator at integrissecurity dot com
77 # Or visit us at https://www.integrissecurity.com/
8 from __future__ import absolute_import
9 from __future__ import print_function
10
811 from burp import IBurpExtender
912 from burp import IHttpListener
1013 from burp import IScannerListener
3134 else:
3235 self.clivars = True
3336
34 print "Initiating Carbonator Against: ", str(self.url)
37 print("Initiating Carbonator Against: ", str(self.url))
3538 #add to scope if not already in there.
3639 if self._callbacks.isInScope(self.url) == 0:
3740 self._callbacks.includeInScope(self.url)
3942 #added to ensure that the root directory is scanned
4043 base_request = str.encode(str("GET "+self.path+" HTTP/1.1\nHost: "+self.fqdn+"\n\n"))
4144 if(self.scheme == 'HTTPS'):
42 print self._callbacks.doActiveScan(self.fqdn,self.port,1,base_request)
45 print(self._callbacks.doActiveScan(self.fqdn,self.port,1,base_request))
4346 else:
44 print self._callbacks.doActiveScan(self.fqdn,self.port,0,base_request)
47 print(self._callbacks.doActiveScan(self.fqdn,self.port,0,base_request))
4548
4649 self._callbacks.sendToSpider(self.url)
4750 self._callbacks.registerHttpListener(self)
4952
5053 while int(time.time())-self.last_packet_seen <= self.packet_timeout:
5154 time.sleep(1)
52 print "No packets seen in the last", self.packet_timeout, "seconds."
53 print "Removing Listeners"
55 print("No packets seen in the last", self.packet_timeout, "seconds.")
56 print("Removing Listeners")
5457 self._callbacks.removeHttpListener(self)
5558 self._callbacks.removeScannerListener(self)
5659 self._callbacks.excludeFromScope(self.url)
5760
58 print "Generating Report"
61 print("Generating Report")
5962 self.generateReport(self.rtype)
60 print "Report Generated"
61 print "Closing Burp in", self.packet_timeout, "seconds."
63 print("Report Generated")
64 print("Closing Burp in", self.packet_timeout, "seconds.")
6265 time.sleep(self.packet_timeout)
6366
6467 if self.clivars:
7073 self.last_packet_seen = int(time.time())
7174 if tool_flag == self._callbacks.TOOL_SPIDER and isRequest: #if is a spider request then send to scanner
7275 self.spider_results.append(current)
73 print "Sending new URL to Vulnerability Scanner: URL #",len(self.spider_results)
76 print("Sending new URL to Vulnerability Scanner: URL #",len(self.spider_results))
7477 if self.scheme == 'https':
7578 self._callbacks.doActiveScan(self.fqdn,self.port,1,current.getRequest()) #returns scan queue, push to array
7679 else:
7982
8083 def newScanIssue(self, issue):
8184 self.scanner_results.append(issue)
82 print "New issue identified: Issue #",len(self.scanner_results);
85 print("New issue identified: Issue #",len(self.scanner_results))
8386 return
8487
8588 def generateReport(self, format):
9598 def processCLI(self):
9699 cli = self._callbacks.getCommandLineArguments()
97100 if len(cli) < 0:
98 print "Incomplete target information provided."
101 print("Incomplete target information provided.")
99102 return False
100103 elif not cli:
101 print "Integris Security Carbonator is now loaded."
102 print "If Carbonator was loaded through the BApp store then you can run in headless mode simply adding the `-Djava.awt.headless=true` flag from within your shell. Note: If burp doesn't close at the conclusion of a scan then disable Automatic Backup on Exit."
103 print "For questions or feature requests contact us at carbonator at integris security dot com."
104 print "Visit carbonator at https://www.integrissecurity.com/Carbonator"
104 print("Integris Security Carbonator is now loaded.")
105 print("If Carbonator was loaded through the BApp store then you can run in headless mode simply adding the `-Djava.awt.headless=true` flag from within your shell. Note: If burp doesn't close at the conclusion of a scan then disable Automatic Backup on Exit.")
106 print("For questions or feature requests contact us at carbonator at integris security dot com.")
107 print("Visit carbonator at https://www.integrissecurity.com/Carbonator")
105108 return False
106109 else:
107110 self.url = URL(cli[0])
118121 else:
119122 self.port = self.port1
120123 self.path = self.url.getFile()
121 print "self.url: " + str(self.url) + "\n"
122 print "Scheme: " + self.scheme + "\n"
123 print "FQDN: " + self.fqdn + "\n"
124 print "Port: " + str(self.port1) + "\n"
125 print "Path: " + self.path + "\n"
124 print("self.url: " + str(self.url) + "\n")
125 print("Scheme: " + self.scheme + "\n")
126 print("FQDN: " + self.fqdn + "\n")
127 print("Port: " + str(self.port1) + "\n")
128 print("Path: " + self.path + "\n")
126129 return True
130 # I'm Py3
0 #!/usr/bin/env python2
0 #!/usr/bin/env python3
11 '''
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
55
66 '''
77
8 from __future__ import absolute_import
9 from __future__ import print_function
810 import os
911 import time
1012 import string
1113 import random
1214 import argparse
1315 import msgpack
14 import httplib
16 import http.client
1517 import ssl
1618
1719
3840 self.token = False
3941 self.headers = {"Content-type": "binary/message-pack"}
4042 if self.ssl:
41 self.client = httplib.HTTPSConnection(self.host, self.port, context=ssl._create_unverified_context())
43 self.client = http.client.HTTPSConnection(self.host, self.port, context=ssl._create_unverified_context())
4244 else:
43 self.client = httplib.HTTPConnection(self.host, self.port)
45 self.client = http.client.HTTPConnection(self.host, self.port)
4446
4547 def encode(self, data):
4648 return msgpack.packb(data)
9092 logfile.write("%s\n" % msg)
9193 logfile.close()
9294 if not self.quiet or critical:
93 print msg
95 print(msg)
9496
9597 def rpc_call(self, meth, opts, key=""):
9698 if self.check_auth():
233235 client.login(args.msfrpc_user if args.msfrpc_user else os.environ.get("MSFRPC_USER"),
234236 args.msfrpc_pass if args.msfrpc_pass else os.environ.get("MSFRPC_PASS"))
235237 except:
236 print "ERROR: Cannot connect to server.."
238 print("ERROR: Cannot connect to server..")
237239 exit(1)
238240
239241 cscan = CscanMsf(client, args.log, args.quiet)
241243 tmp_ws = None
242244 current_ws = cscan.rpc_call("db.current_workspace", [], "workspace")
243245
244 print banner(args, current_ws)
246 print(banner(args, current_ws))
245247 cscan.create_console()
246248
247249 if not args.disable_tmp_ws and os.environ.get("CS_MSF_TMP_WS") == "enabled":
248 tmp_ws = "cscan_" + "".join(random.sample(string.lowercase,6))
250 tmp_ws = "cscan_" + "".join(random.sample(string.ascii_lowercase,6))
249251 cscan.create_ws(tmp_ws, True)
250252 if args.xml:
251253 cscan.import_xml_data(tmp_ws, args.xml)
276278
277279 if __name__ == "__main__":
278280 main()
281 # I'm Py3
0 #!/usr/bin/env python2
0 #!/usr/bin/env python3
11 ###
22 ## Faraday Penetration Test IDE
33 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
44 ## See the file 'doc/LICENSE' for the license information
55 ###
6 from __future__ import absolute_import
7 from __future__ import print_function
68 import requests
79 import json
810 import time
5557 # Exit if there is an error.
5658 if r.status_code != 200:
5759 e = r.json()
58 print e['error']
60 print(e['error'])
5961 sys.exit()
6062
6163 # When downloading a scan we need the raw contents not the JSON data.
316318 args = parser.parse_args()
317319 # Review de Command input
318320 if args.target is None or args.output is None:
319 print "Argument errors check -h"
321 print("Argument errors check -h")
320322 exit(0)
321323
322324 print('Login')
323325 try:
324326 token = login(username, password)
325327 except:
326 print "Unexpected error:", sys.exc_info()[0]
328 print("Unexpected error:", sys.exc_info()[0])
327329 raise
328330
329331 version = get_version()
330332 if version < 7 :
331333 #For Nessus <7
332334 print('Adding new scan.' + token)
333 print args.target
335 print(args.target)
334336
335337 policies = get_policies()
336338 policy_id = policies[profile]
367369 print('Scan up to date. Id: {0}'.format(scan['id']))
368370
369371 print('Logout')
370 logout()
372 logout()# I'm Py3
0 #!/usr/bin/env python2
0 #!/usr/bin/env python3
11
22 # Faraday Penetration Test IDE
33 # Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
44 # See the file 'doc/LICENSE' for the license information
5
6 from __future__ import absolute_import
7 from __future__ import print_function
58
69 from w3af_api_client import Connection, Scan
710 import subprocess
3639 args = parser.parse_args()
3740
3841 if args.target is None or args.output is None:
39 print "Argument errors check -h"
42 print("Argument errors check -h")
4043 exit(0)
4144
42 print 'Starting w3af api ...'
45 print('Starting w3af api ...')
4346 global child_pid
4447 proc = subprocess.Popen([cmd])
4548 child_pid = proc.pid
4649
47 print 'Waiting for W3af to load, 5 seconds ...'
50 print('Waiting for W3af to load, 5 seconds ...')
4851 time.sleep(5)
4952
5053 # Connect to the REST API and get it's version
5154 conn = Connection('http://127.0.0.1:5000/')
52 print conn.get_version()
55 print(conn.get_version())
5356
5457 # Define the target and configuration
5558 # scan_profile = file('/root/tools/w3af/profiles/fast_scan_xml.pw3af').read()
56 scan_profile = file(profile).read()
57 scan_profile = "[output.xml_file]\noutput_file = %s\n%s\n" % (args.output, scan_profile )
59 with open(profile, "r") as scan_profile_file:
60 scan_profile = "[output.xml_file]\noutput_file = %s\n%s\n" % (args.output, scan_profile_file.read())
5861 # scan_profile = file('/root/tools/w3af/profiles/fast_scan.pw3af').read()
5962
6063 target_urls = [args.target]
6972 scan.get_findings()
7073
7174 while(scan.get_status()['status'] == "Running"):
72 print 'Scan progress: %s' + str(scan.get_status()['rpm'])
75 print('Scan progress: %s' + str(scan.get_status()['rpm']))
7376 time.sleep(2)
7477
7578 if __name__ == "__main__":
7679 main()
80 # I'm Py3
0 #!/usr/bin/env python2
0 #!/usr/bin/env python3
11 ###
22 ## Faraday Penetration Test IDE
33 ## Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/)
77 '''
88 By tartamar
99 '''
10
11 from __future__ import absolute_import
12 from __future__ import print_function
13 from builtins import input
14
1015 import argparse
1116 import time
1217 import re
4247 def exportfile(filename,zap):
4348
4449 #Output for XML Report
45 print 'Generating XML Report...'
50 print('Generating XML Report...')
4651 filex = open(filename, 'w')
4752 filex.write(zap.core.xmlreport)
4853 filex.close()
6570 if args.target is None:
6671 # Do nothing
6772 # Input data for test
68 target = raw_input('[+] Enter your target: ')
73 target = input('[+] Enter your target: ')
6974 if is_http_url(target) is True:
70 print '[-] Target selected: ', target
75 print('[-] Target selected: ', target)
7176 else:
72 print '[w] Please type a correct URL address'
77 print('[w] Please type a correct URL address')
7378 quit()
7479 else:
7580 # Check for valid URL addres
7681 if is_http_url(args.target) is True:
7782 target = args.target
78 print '[-] Target selected: ', target
83 print('[-] Target selected: ', target)
7984 else:
80 print '[w] Please type a correct URL Address'
85 print('[w] Please type a correct URL Address')
8186 quit()
82 print 'Starting ZAP ...'
87 print('Starting ZAP ...')
8388
8489 global child_pid
8590 proc = subprocess.Popen([cmd, '-daemon'])
8691 child_pid = proc.pid
8792
88 print 'Waiting for ZAP to load, 10 seconds ...'
93 print('Waiting for ZAP to load, 10 seconds ...')
8994 time.sleep(10)
9095 zap = ZAPv2()
9196 # Use the line below if ZAP is not listening on 8090
9297 zap = ZAPv2(proxies={'http': 'http://127.0.0.1:8080', 'https': 'http://127.0.0.1:8080'})
9398
9499 # do stuff
95 print 'Accessing target %s' % target
100 print('Accessing target %s' % target)
96101 # try have a unique enough session...
97102 zap.urlopen(target)
98103 # Give the sites tree a chance to get updated
99104 time.sleep(2)
100105
101 print 'Spidering target %s' % target
102 print target
106 print('Spidering target %s' % target)
107 print(target)
103108 zap.spider.scan(target)
104109 # Give the Spider a chance to start
105110 time.sleep(2)
106111 # print 'Status %s' % zap.spider.status
107112 while(int(zap.spider.status) < 100):
108 print 'Spider progress %: ' + zap.spider.status
113 print('Spider progress %: ' + zap.spider.status)
109114 time.sleep(2)
110115
111 print 'Spider completed'
116 print('Spider completed')
112117 # Give the passive scanner a chance to finish
113118 time.sleep(5)
114119
115 print 'Scanning target %s' % target
120 print('Scanning target %s' % target)
116121 zap.ascan.scan(target)
117122 while(int(zap.ascan.status) < 100):
118 print 'Scan progress %: ' + zap.ascan.status
123 print('Scan progress %: ' + zap.ascan.status)
119124 time.sleep(5)
120125
121 print 'Scan completed'
126 print('Scan completed')
122127
123128 # Report the results
124129
125 print 'Hosts: ' + ', '.join(zap.core.hosts)
130 print('Hosts: ' + ', '.join(zap.core.hosts))
126131 # print 'Alerts: '
127132 # pprint (zap.core.alerts())
128133 # pprint (zap.core.xmlreport())
129134 exportfile(args.output,zap)
130135
131 print 'Shutting down ZAP ...'
136 print('Shutting down ZAP ...')
132137 zap.core.shutdown
133138 #EOF
134139
135140 if __name__ == "__main__":
136141 main()
142 # I'm Py3
0 from __future__ import absolute_import
1 from __future__ import print_function
02 import faraday.server.config
13 from faraday.server.models import (
24 Host,
2224 vulns_id.append(vuln.id)
2325 session.commit()
2426
25 print ("[+] {vulns_length} vulnerabilities changed in workspace named {ws_name}"
27 print("[+] {vulns_length} vulnerabilities changed in workspace named {ws_name}"
2628 .format(vulns_length=len(affected_vulns),
2729 ws_name=workspace.name))
28 print " Vulnerabilities ID: {ids}".format(ids=vulns_id)
30 print(" Vulnerabilities ID: {ids}".format(ids=vulns_id))
2931
3032
3133 conn_string = faraday.server.config.database.connection_string
4951 if vulns_web.all():
5052 change_vulns(vulns_web.all(), ws)
5153 else:
52 print "[-] No vulnerabilities to change in workspace named {ws_name}".format(ws_name=ws.name)
54 print("[-] No vulnerabilities to change in workspace named {ws_name}".format(ws_name=ws.name))
5355
5456 sys.stdout = orig_stdout
5557
5658 # Print log file
5759 with open(log_file, 'r') as log:
58 print log.read()
60 print(log.read())
5961
60 print "Logs saved in file named {}".format(log_file)
62 print("Logs saved in file named {}".format(log_file))
63 # I'm Py3
44 See the file 'doc/LICENSE' for the license information
55
66 '''
7
7 from __future__ import absolute_import
8 from __future__ import print_function
89 import sys
9 import xmlrpclib
10 import xmlrpc.client
1011 import argparse
1112 import base64
1213
2930
3031
3132 def send_faraday_device(result):
32 print 'IP: %s' % result['ip_address']
33 print('IP: %s' % result['ip_address'])
3334
3435 if args.debug == "1":
35 print '==============='
36 print('===============')
3637 for key in result.keys():
37 print "kname:" + key + ", value:" + str(result[key])
38 print("kname:" + key + ", value:" + str(result[key]))
3839
3940 h_id = api.createAndAddHost(str(result['ip_address']))
4041 i_id = api.createAndAddInterface(h_id,str(result['ip_address']),"00:00:00:00:00:00", str(result['ip_address']), "0.0.0.0", "0.0.0.0",[],
4849 service['banner'] = base64.b64encode(strip_non_ascii(str(service['banner']))) #fix: to avoid non ascii caracters
4950
5051 if service['banner'] is not None:
51 n_id = api.createAndAddNoteToService(h_id,s_id,"banner",str(service['banner']))
52 n_id = api.createAndAddNoteToService(h_id,s_id,"banner",str(service['banner']))
5253
5354 #Notes - Information geo/shadon
5455 n_id = api.createAndAddNoteToHost(h_id,"geo_country",result['location']['country_name'] if result['location']['country_name'] is not None else "" )
7475
7576 try:
7677 # Setup the apis
77 api = xmlrpclib.ServerProxy(args.faradayapi)
78 api = xmlrpc.client.ServerProxy(args.faradayapi)
7879
7980 results = reposify_search(args.skey, args.reposify_banner, args.reposify_filters, 1)
80 print 'Results found: %s, banner "%s", filters "%s' % (results['total_count'], args.reposify_banner, args.reposify_filters)
81 print('Results found: %s, banner "%s", filters "%s' % (results['total_count'], args.reposify_banner, args.reposify_filters))
8182 send_faraday(results)
8283
8384 if results['pagination']['has_more'] == True:
8788 if results['pagination']['has_more'] != True:
8889 break;
8990
90 except xmlrpclib.ProtocolError as e:
91 except xmlrpc.client.ProtocolError as e:
9192 if e.errcode == 500:
92 print "[ERROR] Faraday Api error:", sys.exc_info()[0]
93 print("[ERROR] Faraday Api error:", sys.exc_info()[0])
9394 pass
9495 else:
95 print "[ERROR] Unexpected error:", sys.exc_info()[0]
96 print e.__dict__
96 print("[ERROR] Unexpected error:", sys.exc_info()[0])
97 print(e.__dict__)
9798 raise
9899
99100 except Exception as e:
100 print "Unexpected error:", sys.exc_info()[0]
101 print e.__dict__
101 print("Unexpected error:", sys.exc_info()[0])
102 print(e.__dict__)
102103 raise
103104
104105
105106
107 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import requests
79 import simplejson
810
7072 params['filters'] = filters
7173 res = api_request(key, '/v1/insights/search', params, None, 'https://api.reposify.com', 'get', 1)
7274 return res
75 # I'm Py3
44 See the file 'doc/LICENSE' for the license information
55
66 '''
7 from __future__ import absolute_import
8 from __future__ import print_function
9
710 import shodan
811 import sys
9 import xmlrpclib
12 import xmlrpc.client
1013 import argparse
1114 import base64
1215
2831 return ''.join(stripped)
2932
3033 def send_faraday(result):
31 print 'IP: %s' % result['ip_str']
34 print('IP: %s' % result['ip_str'])
3235
3336 if result['data'] is not None:
3437 result['data'] = base64.b64encode(str(strip_non_ascii(result['data']))) #fix: to avoid non ascii caracters
3538 if args.debug == "1":
36 print '==============='
39 print('===============')
3740 for key in result.keys():
38 print "kname:" + key + ", value:" + str(result[key])
41 print("kname:" + key + ", value:" + str(result[key]))
3942
4043 h_id = api.createAndAddHost(str(result['ip_str']),str(result['os']) if result['os'] is not None else "")
4144 #i_id = api.createAndAddInterface(h_id,str(result['ip_str']),"00:00:00:00:00:00", str(result['ip_str']), "0.0.0.0", "0.0.0.0",[],
4548 "tcp",[str(result['port'])],"open",str(result['version']) if result.has_key('version') else "")
4649
4750 if result['data'] is not None:
48 n_id = api.createAndAddNoteToService(h_id, s_id, "shadon_response", str(result['data']))
51 n_id = api.createAndAddNoteToService(h_id, s_id, "shadon_response", str(result['data']))
4952
5053 #Notes - Information geo/shadon
5154 n_id = api.createAndAddNoteToHost(h_id, "geo_country", result['location']['country_name'] if result['location']['country_name'] is not None else "" )
6972
7073 try:
7174 # Setup the apis
72 api = xmlrpclib.ServerProxy(args.faradayapi)
75 api = xmlrpc.client.ServerProxy(args.faradayapi)
7376 shodan_api = shodan.Shodan(args.skey)
7477 c_page=1
7578
7679 results = shodan_api.search(args.shodan_query)
77 print 'Results found: %s, query "%s"' % (results['total'], args.shodan_query)
80 print('Results found: %s, query "%s"' % (results['total'], args.shodan_query))
7881
7982 for r in shodan_api.search_cursor(args.shodan_query, minify=True, retries=5):
8083 if args.count != "all" and c_page >= int(args.count):
81 break
84 break
8285
8386 send_faraday(r)
8487 c_page+=1
8588
8689
87 except xmlrpclib.ProtocolError as e:
90 except xmlrpc.client.ProtocolError as e:
8891 if e.errcode == 500:
89 print "[ERROR] Faraday Api error:", sys.exc_info()[0]
92 print("[ERROR] Faraday Api error:", sys.exc_info()[0])
9093 pass
9194 else:
92 print "[ERROR] Unexpected error:", sys.exc_info()[0]
93 print e.__dict__
95 print("[ERROR] Unexpected error:", sys.exc_info()[0])
96 print(e.__dict__)
9497 raise
9598 except shodan.client.APIError as e:
96 print "[ERROR] :", sys.exc_info()[0]
99 print("[ERROR] :", sys.exc_info()[0])
97100 raise
98101
99102 except Exception as e:
100 print "Unexpected error:", sys.exc_info()[0]
101 print e.__dict__
103 print("Unexpected error:", sys.exc_info()[0])
104 print(e.__dict__)
102105 raise
103106
104107
105108
109 # I'm Py3
0 #!/usr/bin/env python2
0 #!/usr/bin/env python3
11 '''
22 Faraday Penetration Test IDE
33 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
66 '''
77
88 #libraries
9 from __future__ import absolute_import
10 from __future__ import print_function
911 import subprocess
1012 import argparse
1113 import re
4042 subprocess.check_output("openssl s_client -connect {}:{} < /dev/null".format(host,port), shell=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
4143 return
4244 except subprocess.CalledProcessError:
43 print "[+]Connection failed... [ {} / 5 ]".format(i)
44 print "[+]Connection to the host impossible"
45 print("[+]Connection failed... [ {} / 5 ]".format(i))
46 print("[+]Connection to the host impossible")
4547 exit(1)
4648
4749 def basic_connect(host,port):
5052
5153 def complex_connect(host,port):
5254 result = ""
53 for sprotocol, protocol in openssl_protocols.iteritems():
55 for sprotocol, protocol in openssl_protocols.items():
5456 try:
5557 result += subprocess.check_output("openssl s_client -{} -connect {}:{} < /dev/null".format(sprotocol,host,port),shell=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
5658 except subprocess.CalledProcessError:
6062 protocolx.text="no"
6163 #-----------------------------------XML_Export-----------------------------------#
6264 if protocol == "TLSv1.1" or protocol == "TLSv1.2":
63 print " \033[0;41m{} not supported\033[0m".format(protocol)
65 print(" \033[0;41m{} not supported\033[0m".format(protocol))
6466 else:
65 print " {} not supported".format(protocol)
67 print(" {} not supported".format(protocol))
6668 return result.split("\n")
6769 def tlsdebug_connect(host,port):
6870 result = subprocess.check_output("openssl s_client -tlsextdebug -connect {}:{} < /dev/null".format(host,port),shell=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
98100 forward_secrecy = ET.SubElement(scan, "forward_secrecy")
99101 forward_secrecy.text="no"
100102 #-----------------------------------XML_Export-----------------------------------#
101 print "\033[0;41m[+]Forward Secrecy not supported\033[0m"
103 print("\033[0;41m[+]Forward Secrecy not supported\033[0m")
102104 return result.split("\n")
103105
104106 def key_check(host,port):
109111 key_size = ET.SubElement(scan, "key")
110112 key_size.text = str(re.findall(r'\d+', line)[0])
111113 #-----------------------------------XML_Export-----------------------------------#
112 print "[+]{}\n".format(line),
114 print("[+]{}\n".format(line))
113115 if int(re.findall(r'\d+', line)[0]) < 2048:
114116 print ("\033[0;41m[+]Insecure key size, it must be higher than 2048 bits\033[0m")
115117 return
123125 renegotiation = ET.SubElement(scan, "renegotiation")
124126 renegotiation.text="no"
125127 #-----------------------------------XML_Export-----------------------------------#
126 print "\033[0;41m[+]Secure renegotiation not supported\033[0m"
128 print("\033[0;41m[+]Secure renegotiation not supported\033[0m")
127129 else:
128130 #-----------------------------------XML_Export-----------------------------------#
129131 if args.xmloutput:
130132 renegotiation = ET.SubElement(scan, "renegotiation")
131133 renegotiation.text="yes"
132134 #-----------------------------------XML_Export-----------------------------------#
133 print "[+]"+line
135 print("[+]"+line)
134136 return
135137
136138
141143 for insecure_cypher in openssl_insecure_cyphers:
142144 if insecure_cypher in line or insecure_cypher.upper() in line or insecure_cypher.lower() in line:
143145 insecure = True
144 print "\033[0;41m[+]{}\033[0m\n".format(line.replace(" ","")),
146 print("\033[0;41m[+]{}\033[0m\n".format(line.replace(" ","")))
145147 #-----------------------------------XML_Export-----------------------------------#
146148 if args.xmloutput:
147149 signature_cipher = ET.SubElement(scan, line.replace(" Signature Algorithm: ",""))
154156 signature_cipher = ET.SubElement(scan, line.replace(" Signature Algorithm: ",""))
155157 signature_cipher.text = "signature secure"
156158 #-----------------------------------XML_Export-----------------------------------#
157 print "[+]"+line.replace(" ","")
159 print("[+]"+line.replace(" ",""))
158160
159161 def serv_check(host,port):
160 print "[+]Protocols supported by the server:"
162 print("[+]Protocols supported by the server:")
161163 for line in complex_connect(host,port):
162164 for i,protocol in enumerate(openssl_protocols):
163165 if line.endswith(openssl_protocols[protocol]):
164166 if openssl_protocols[protocol] == "SSLv3" or openssl_protocols[protocol] == "TLSv1":
165 print " \033[0;41m"+openssl_protocols[protocol],
167 print(" \033[0;41m"+openssl_protocols[protocol])
166168 else:
167 print " "+openssl_protocols[protocol],
169 print(" "+openssl_protocols[protocol])
168170 #-----------------------------------XML_Export-----------------------------------#
169171 if args.xmloutput:
170172 protocolx = ET.SubElement(scan, openssl_protocols[protocol])
171173 protocolx.text="yes"
172174 #-----------------------------------XML_Export-----------------------------------#
173175 if "Cipher " in line:
174 print "|| Default cipher:{}\033[0m".format(line.replace(" Cipher : ","").replace("0000",""))
176 print("|| Default cipher:{}\033[0m".format(line.replace(" Cipher : ","").replace("0000","")))
175177
176178 def cyph_check(host,port):
177 print "[+]Ciphers supported by the server (it may takes a minute):"
179 print("[+]Ciphers supported by the server (it may takes a minute):")
178180 for line in cypher_connect(host,port):
179181 if "Cipher : " in line:
180182 insecure = False
185187 cypher = ET.SubElement(scan, line.replace(" Cipher : ",""))
186188 cypher.text = "insecure"
187189 #-----------------------------------XML_Export-----------------------------------#
188 print " \033[0;41m{}\033[0m\n".format(line.replace(" Cipher : ","Supported cipher suite: [INSECURE] ")),
190 print(" \033[0;41m{}\033[0m\n".format(line.replace(" Cipher : ","Supported cipher suite: [INSECURE] ")))
189191 insecure = True
190192 break
191193 if not insecure:
194196 cypher = ET.SubElement(scan, line.replace(" Cipher : ",""))
195197 cypher.text = "secure"
196198 #-----------------------------------XML_Export-----------------------------------#
197 print line.replace(" Cipher : "," Supported cipher suite: [SECURE] ")
199 print(line.replace(" Cipher : "," Supported cipher suite: [SECURE] "))
198200
199201 def forw_check(host,port):
200202 for line in forward_connect(host,port):
204206 forward_secrecy = ET.SubElement(scan, "forward_secrecy")
205207 forward_secrecy.text="yes"
206208 #-----------------------------------XML_Export-----------------------------------#
207 print "[+]{} (prefered)".format(line.replace(" Cipher : ","Forward Secrecy supported: "))
209 print("[+]{} (prefered)".format(line.replace(" Cipher : ","Forward Secrecy supported: ")))
208210 return
209211
210212 def heart_check(host,port):
215217 heartbeat = ET.SubElement(scan, "heartbeat")
216218 heartbeat.text = "yes"
217219 #-----------------------------------XML_Export-----------------------------------#
218 print "\033[0;41m[+]Heartbeat extension vulnerable\033[0m"
220 print("\033[0;41m[+]Heartbeat extension vulnerable\033[0m")
219221 return;
220222 #-----------------------------------XML_Export-----------------------------------#
221223 if args.xmloutput:
222224 heartbeat = ET.SubElement(scan, "heartbeat")
223225 heartbeat.text = "no"
224226 #-----------------------------------XML_Export-----------------------------------#
225 print "[+]Heartbeat extension disabled"
227 print("[+]Heartbeat extension disabled")
226228
227229 def crime_check(host,port):
228230 for line in basic_connect(host,port):
232234 crime = ET.SubElement(scan, "crime")
233235 crime.text = "no"
234236 #-----------------------------------XML_Export-----------------------------------#
235 print "[+]Compression disabled, CRIME is prevented"
237 print("[+]Compression disabled, CRIME is prevented")
236238 return
237239 elif "Compression: " in line:
238240 #-----------------------------------XML_Export-----------------------------------#
240242 crime = ET.SubElement(scan, "crime")
241243 crime.text = "yes"
242244 #-----------------------------------XML_Export-----------------------------------#
243 print ("\033[0;41m[+]Potentially vulnerable to CRIME:{}\033[0m").format(line)
245 print("\033[0;41m[+]Potentially vulnerable to CRIME:{}\033[0m").format(line)
244246 return
245247
246 print """\n\033[0;33m
248 print("""\n\033[0;33m
247249 ___ ___ _ ___ _ _
248250 / __/ __| | / __| |_ ___ __| |_
249251 \__ \__ \ |_| (__| ' \/ -_) _| / /
250252 |___/___/____\___|_||_\___\__|_\_\ \033[0m
251253
252254 Version v1.0: November 2014
253 Morgan Lemarechal\n"""
255 Morgan Lemarechal\n""")
254256
255257 if args.xmloutput:
256258 root = ET.Element("sslcheck")
261263 host_ip = host
262264 host_name = ""
263265 #host_name = subprocess.check_output("dig -x {} +short|sed 's/\.$//g'".format(host),shell=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE).rstrip()
264 print "[+]Perfoming the scan of {}".format(host_ip)
266 print("[+]Perfoming the scan of {}".format(host_ip))
265267 else:
266268 host_name = host
267269 host_ip = subprocess.check_output("nslookup "+host+" | tail -2 | head -1|awk '{print $2}'",shell=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE).rstrip()
268 print "[+]Perfoming the scan of {}|{}".format(host_name,host_ip)
270 print("[+]Perfoming the scan of {}|{}".format(host_name,host_ip))
269271
270272 try:
271273 #-----------------------------------XML_Export-----------------------------------#
283285 globals()[action](host,args.port)
284286
285287 except KeyboardInterrupt:
286 print "[+]Interrupting SSLcheck..."
288 print("[+]Interrupting SSLcheck...")
287289 exit(1)
288290
289291 #-----------------------------------XML_Export-----------------------------------#
296298 except IOError:
297299 sys.exit('\033[0;41m[+]XML export failed.\033[0m')
298300 #-----------------------------------XML_Export-----------------------------------#
301 # I'm Py3
66
77 '''
88
9 from __future__ import absolute_import
10 from __future__ import print_function
911 import subprocess
1012 import argparse
1113 from lxml import etree as ET
1214 import os.path
1315 from os.path import basename
14 import __builtin__
1516 import re
1617
1718 from wcscans import phpini, webconfig
7172 version='%(prog)s v1.0 by Morgan Lemarechal')
7273 args = parser.parse_args()
7374
74 print basename("/a/b/c.txt")
75 print """\033[0;33m
75 print (basename("/a/b/c.txt"))
76 print ("""\033[0;33m
7677 __ __
7778 \ \ /\ / /__ ___ ___ __ _ _ __
7879 \ \/ \/ / __/ __|/ __/ _` | '_ \
7980 \ /\ / (__\__ \ (_| (_| | | | |
8081 \/ \/ \___|___/\___\__,_|_| |_|
8182 Version v1.0: November 2014
82 Morgan Lemarechal\033[0m"""
83 Morgan Lemarechal\033[0m""")
8384
8485 if args.xmloutput:
8586 root = ET.Element("wcscan")
8889
8990 for file in args.files:
9091 try:
91 print "\n[+]Perfoming the scan of \033[1;30m{}\033[0m...".format(file)
92 print("\n[+]Perfoming the scan of \033[1;30m{}\033[0m...".format(file))
9293
9394 #------------------------------XML_Export------------------------------#
9495 if args.xmloutput:
127128 #------------------------------XML_Export------------------------------#
128129
129130 except KeyboardInterrupt:
130 print "\n[+]Interrupting the checking of \033[1;30m{}\033[0m...".format(file)
131 print("\n[+]Interrupting the checking of \033[1;30m{}\033[0m...".format(file))
132 # I'm Py3
66
77 #!/usr/bin/env python
88 # -*- coding: utf-8 -*-
9
9 from __future__ import absolute_import
10 from __future__ import print_function
1011 import re
1112 from lxml import etree as ET
1213
8990 newElement.text = directive[1]
9091
9192 def global_check(self):
92 print "[+]\033[0;41mVulnerabilites/Informations\033[0m:"
93 print("[+]\033[0;41mVulnerabilites/Informations\033[0m:")
9394 for line in self.config.split('\n'):
9495 directive = ''.join(line.split()).split('=')
9596
9697 if ( rules.has_key(directive[0]) and not equals(rules[directive[0]][1],directive[1])) or directive[0] in bad_default_config:
97 print " \033[1;30m({})\033[0m {}".format(directive[0],rules[directive[0]][0])
98 print(" \033[1;30m({})\033[0m {}".format(directive[0],rules[directive[0]][0]))
9899 self.recommended += " {} = {}\n".format(directive[0],rules[directive[0]][1])
99100 self.xml_export(directive,rules[directive[0]][0])
100101 continue
101102
102103 if rules.has_key(directive[0]) and directive[1] == "":
103 print " \033[1;30m({})\033[0m {}".format(directive[0],rules[directive[0]][0])
104 print(" \033[1;30m({})\033[0m {}".format(directive[0],rules[directive[0]][0]))
104105 self.recommended += " {} = {}\n".format(directive[0],rules[directive[0]][1])
105106 self.xml_export(directive,rules[directive[0]][0])
106107 continue
107108
108109 if directive[0] == "session.hash_function" and directive[1] in weak_functions:
109 print " \033[1;30m({})\033[0m {}".format(directive[0],rules[directive[0]][0])
110 print(" \033[1;30m({})\033[0m {}".format(directive[0],rules[directive[0]][0]))
110111 self.recommended += " {} = sha256\n".format(directive[0])
111112 self.xml_export(directive,rules[directive[0]][0])
112113 continue
114115 if directive[0] == "disable_functions":
115116 for option in weak_php_functions:
116117 if not option in directive[1]:
117 print " \033[1;30m(disable_functions)\033[0m {} not listed".format(option)
118 print(" \033[1;30m(disable_functions)\033[0m {} not listed".format(option))
118119 self.recommended += " disable_functions = ... , {} , ...\n".format(option)
119120 self.xml_export(directive,"")
120121 continue
121122
122123 for element in is_set_config:
123124 if not element in self.config:
124 print " \033[1;30m({})\033[0m {}".format(element,rules[element][0])
125 print(" \033[1;30m({})\033[0m {}".format(element,rules[element][0]))
125126 self.recommended += " {} is not set\n".format(element)
126127 directive = [element,'isNotSet']
127128 self.xml_export(directive,rules[directive[0]][0])
128129
129130 for element in bad_default_config:
130131 if not element in self.config:
131 print " \033[1;30m({})\033[0m {}".format(element,rules[element][0])
132 print(" \033[1;30m({})\033[0m {}".format(element,rules[element][0]))
132133 self.recommended += " {} = {}\n".format(element,rules[element][1])
133134 directive = [element,'defaultValue']
134135 self.xml_export(directive,rules[directive[0]][0])
145146 filetoscan = PhpIniScan(file,xml)
146147 filetoscan.global_check()
147148 if recmode:
148 print filetoscan.recommended
149 print(filetoscan.recommended)
150 # I'm Py3
77
88 '''
99
10 from __future__ import absolute_import
11 from __future__ import print_function
1012 from lxml import etree as ET
1113
1214 rules = {
7375
7476
7577 def global_check(self):
76 print "[+]\033[0;41mVulnerabilites/Informations\033[0m:"
78 print("[+]\033[0;41mVulnerabilites/Informations\033[0m:")
7779 countforms = 0
7880 nameforms = []
7981 for element in rules:
8284 countforms += 1
8385 nameforms.append(tag.attrib['name'])
8486 if element == "user":
85 print " \033[1;30m{}\033[0m {}: {} \033[1;30m({})\033[0m".format(element,
87 print(" \033[1;30m{}\033[0m {}: {} \033[1;30m({})\033[0m".format(element,
8688 tag.attrib['name'],
8789 rules[element][''][0],
88 option)
90 option))
8991 self.recommended += " Not to store passwords or hashes in web.config\n"
9092 self.xml_export(directive=[element, tag.attrib['name'],'hardcoded'],
9193 rec=rules[element][''][0])
9395
9496 for option in tag.attrib:
9597 if element == "customErrors" and rules[element].has_key(option) and not tag.attrib[option].lower() in rules[element][option][1]:
96 print " \033[1;30m{}\033[0m: {} \033[1;30m({})\033[0m".format(element,
98 print(" \033[1;30m{}\033[0m: {} \033[1;30m({})\033[0m".format(element,
9799 rules[element][option][0],
98 option)
100 option))
99101 self.recommended += " <{} {}=\"{}\"/>\n".format(element,option,rules[element][option][1])
100102 self.xml_export(directive=[element,option,'disabled'],
101103 rec=rules[element][option][0])
102104 continue
103105
104106 elif element == "roleManager" and option == "cookiePath":
105 print " \033[1;30mroleManager\033[0m: Liberal path defined ('{}') (\033[1;30mcookiePath\033[0m)".format(tag.attrib[option].lower())
107 print(" \033[1;30mroleManager\033[0m: Liberal path defined ('{}') (\033[1;30mcookiePath\033[0m)".format(tag.attrib[option].lower()))
106108 self.recommended += " <roleManager cookiePath=\"{abcd1234…}\">\n"
107109 self.xml_export(directive=[element,option,'liberal'],
108110 rec=rules[element][option][0])
110112
111113 if rules[element].has_key(option) and tag.attrib[option].lower() != rules[element][option][1]:
112114 if element == "forms":
113 print " \033[1;30m{}\033[0m {}: {} \033[1;30m({})\033[0m".format(element,
115 print(" \033[1;30m{}\033[0m {}: {} \033[1;30m({})\033[0m".format(element,
114116 tag.attrib['name'],
115117 rules[element][option][0],
116 option)
118 option))
117119 self.xml_export(directive=[element,option,tag.attrib[option],tag.attrib['name']],
118120 rec=rules[element][option][0])
119121
120122 else:
121 print " \033[1;30m{}\033[0m: {} \033[1;30m({})\033[0m".format(element,
123 print(" \033[1;30m{}\033[0m: {} \033[1;30m({})\033[0m".format(element,
122124 rules[element][option][0],
123 option)
125 option))
124126
125127 self.xml_export(directive=[element,option,tag.attrib[option]],
126128 rec=rules[element][option][0])
128130 continue
129131
130132 if countforms > 1 and (nameforms.index('.ASPXAUTH') != "-1" or nameforms.index('ASPXAUTH') != "-1"):
131 print " \033[1;30mforms\033[0m: Non-Unique authentication cookie used\033[1;30m (name)\033[0m"
133 print(" \033[1;30mforms\033[0m: Non-Unique authentication cookie used\033[1;30m (name)\033[0m")
132134 self.recommended += " <forms name=\"{abcd1234…}\">\n"
133135 self.xml_export(directive=['nameforms','name','false'],
134136 rec="Non-Unique authentication cookie used")
137139 filetoscan = WebConfigScan(file,xml)
138140 filetoscan.global_check()
139141 if recmode:
140 print filetoscan.recommended
142 print(filetoscan.recommended)
141143
144 # I'm Py3
33 https://packaging.python.org/en/latest/distributing.html
44 https://github.com/pypa/sampleproject
55 """
6 from __future__ import absolute_import
67
78 # Always prefer setuptools over distutils
89 from setuptools import setup, find_packages
136137 'Topic :: System :: Networking :: Monitoring',
137138
138139 # Pick your license as you wish
139 'License :: OSI Approved :: MIT License',
140 'License :: OSI Approved :: GNU General Public License version 3 (GPL-3.0)',
140141
141142 # Specify the Python versions you support here. In particular, ensure
142143 # that you indicate whether you support Python 2, Python 3 or both.
143144 # These classifiers are *not* checked by 'pip install'. See instead
144145 # 'python_requires' below.
145 'Programming Language :: Python :: 2',
146 'Programming Language :: Python :: 2.7',
147 #'Programming Language :: Python :: 3',
148 #'Programming Language :: Python :: 3.4',
149 #'Programming Language :: Python :: 3.5',
150 #'Programming Language :: Python :: 3.6',
151 #'Programming Language :: Python :: 3.7',
146 'Programming Language :: Python :: 3',
147 'Programming Language :: Python :: 3.6',
148 'Programming Language :: Python :: 3.7',
152149 ],
153150
154151 # This field adds keywords for your project which will appear on the
183180 # and refuse to install the project if the version does not match. If you
184181 # do not support Python 2, you can simplify this to '>=3.5' or similar, see
185182 # https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires
186 python_requires='>=2.7.*',
183 python_requires='>=3.6.*',
187184
188185 # This field lists other packages that your project depends on to run.
189186 # Any package you put here will be installed by pip when your project is
260257 setup_requires=['pytest-runner'],
261258 tests_require=['pytest', 'flask'] + dev_required,
262259 )
260
261
262 # I'm Py3
00 with (import <nixpkgs> {});
11 mkShell {
2 buildInputs = [pandoc] ++ (with python27Packages;
2 buildInputs = [pandoc] ++ (with python3Packages;
33 [virtualenv pyopenssl psycopg2 pillow pygobject3 pynacl matplotlib lxml ldap autobahn
4 gobjectIntrospection gtk3 gnome3.vte gssapi
4 gobjectIntrospection gtk3 gnome3.vte gssapi pykerberos
55 ]);
66 shellHook = ''
77 unset SOURCE_DATE_EPOCH # Required to make pip work
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 from tempfile import NamedTemporaryFile
79
810 import os
2022 from faraday.server.app import create_app
2123 from faraday.server.models import db
2224 from tests import factories
25
26 from faraday.server.utils.py3 import BytesJSONEncoder
2327
2428 TEST_BASE = os.path.abspath(os.path.dirname(__file__))
2529 TEST_DATA = os.path.join(TEST_BASE, 'data')
5054 def open(self, *args, **kwargs):
5155 if kwargs.pop('use_json_data', True) and 'data' in kwargs:
5256 # JSON-encode data by default
53 kwargs['data'] = json.dumps(kwargs['data'])
57 kwargs['data'] = json.dumps(kwargs['data'], cls=BytesJSONEncoder)
5458 kwargs['headers'] = kwargs.get('headers', []) + [
5559 ('Content-Type', 'application/json'),
5660 ]
297301 def csrf_token(logged_user, test_client):
298302 session_response = test_client.get('/session')
299303 return session_response.json.get('csrf_token')
304
305
306 # I'm Py3
+0
-10
tests/data/docker_images/faraday_testing_base/Dockerfile less more
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
-950
tests/dont_run_but_update_model_controller.py less more
0 #!/usr/bin/python
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 import unittest
8 import sys
9 sys.path.append('.')
10 import faraday.client.model.controller as controller
11 from mockito import mock, verify, when, any
12 from faraday.client.model.hosts import Host, Interface, Service
13 from faraday.client.model.common import ModelObjectVuln, ModelObjectVulnWeb, ModelObjectNote, ModelObjectCred
14
15
16 class ModelObjectControllerUnitTest(unittest.TestCase):
17 # TODO: Notifier goes into mapper?
18
19 def testAddHostGetsMapperDispatchSaveSYNC(self):
20 host = Host('coco')
21
22 mappersManager = self.createMapperMock()
23 objectMapper = mock()
24 when(mappersManager).getMapper(host.class_signature).thenReturn(objectMapper)
25 when(objectMapper).save(host).thenReturn(True)
26
27 model_controller = controller.ModelController(mock(), mappersManager)
28
29 model_controller.addHostSYNC(host)
30 verify(mappersManager).getMapper(host.class_signature)
31 verify(objectMapper).save(host)
32
33 def testAddHostGetsMapperDispatchSaveASYNC(self):
34 host = Host('coco')
35
36 mappersManager = self.createMapperMock()
37 objectMapper = mock()
38 when(mappersManager).getMapper(host.class_signature).thenReturn(objectMapper)
39 when(objectMapper).save(host).thenReturn(True)
40
41 model_controller = controller.ModelController(mock(), mappersManager)
42
43 model_controller.addHostASYNC(host)
44 model_controller.processAllPendingActions()
45
46 verify(mappersManager).getMapper(host.class_signature)
47 verify(objectMapper).save(host)
48
49 def testAddInterfaceGetsMapperDispatchSaveSYNC(self):
50 host = Host('coco')
51 interface = Interface("int_mock0")
52
53 mappersManager = self.createMapperMock()
54 objectMapper = mock()
55 when(mappersManager).getMapper(interface.class_signature).thenReturn(objectMapper)
56 when(objectMapper).save(interface).thenReturn(True)
57
58 model_controller = controller.ModelController(mock(), mappersManager)
59
60 model_controller.addInterfaceSYNC(host.getID(), interface)
61 verify(mappersManager).getMapper(interface.class_signature)
62 verify(objectMapper).save(interface)
63
64 def testAddInterfaceGetsMapperDispatchSaveASYNC(self):
65 host = Host('coco')
66 interface = Interface("int_mock0")
67
68 mappersManager = self.createMapperMock()
69 objectMapper = mock()
70 when(mappersManager).getMapper(interface.class_signature).thenReturn(objectMapper)
71 when(objectMapper).save(interface).thenReturn(True)
72
73 model_controller = controller.ModelController(mock(), mappersManager)
74
75 model_controller.addInterfaceASYNC(host.getID(), interface)
76 model_controller.processAllPendingActions()
77
78 verify(mappersManager).getMapper(interface.class_signature)
79 verify(objectMapper).save(interface)
80
81 def testAddObjectSavesChildInParent(self):
82 host = Host('coco')
83 interface = Interface("int_mock0")
84
85 mappersManager = self.createMapperMock()
86 objectMapper = mock()
87
88 when(mappersManager).getMapper(interface.class_signature).thenReturn(objectMapper)
89 when(objectMapper).save(interface).thenReturn(True)
90 when(mappersManager).find(host.getID()).thenReturn(host)
91
92 model_controller = controller.ModelController(mock(), mappersManager)
93
94 model_controller.addInterfaceSYNC(host.getID(), interface)
95 verify(mappersManager).getMapper(interface.class_signature)
96 verify(objectMapper).save(interface)
97
98 self.assertEquals(interface, host.findChild(interface.getID()),
99 "Orphan child, what happen papi?")
100
101 def testAddServiceGetsMapperDispatchSaveSYNC(self):
102 interface = Interface("int_mock0")
103 service = Service("servi")
104
105 mappersManager = self.createMapperMock()
106 objectMapper = mock()
107 when(mappersManager).getMapper(service.class_signature).thenReturn(objectMapper)
108 when(objectMapper).save(service).thenReturn(True)
109
110 model_controller = controller.ModelController(mock(), mappersManager)
111
112 model_controller.addServiceToInterfaceSYNC(None, interface.getID(), service)
113
114 verify(mappersManager).getMapper(service.class_signature)
115 verify(objectMapper).save(service)
116
117 def testAddServiceGetsMapperDispatchSaveASYNC(self):
118 interface = Interface("int_mock0")
119 service = Service("servi")
120
121 mappersManager = self.createMapperMock()
122 objectMapper = mock()
123 when(mappersManager).getMapper(service.class_signature).thenReturn(objectMapper)
124 when(objectMapper).save(service).thenReturn(True)
125
126 model_controller = controller.ModelController(mock(), mappersManager)
127
128 model_controller.addServiceToInterfaceASYNC(None, interface.getID(), service)
129 model_controller.processAllPendingActions()
130
131 verify(mappersManager).getMapper(service.class_signature)
132 verify(objectMapper).save(service)
133
134 def testAddVulnToServiceGetsMapperDispatchSaveSYNC(self):
135 service = Service("servi")
136 vuln = ModelObjectVuln("a_vuln")
137
138 mappersManager = self.createMapperMock()
139 objectMapper = mock()
140 when(mappersManager).getMapper(vuln.class_signature).thenReturn(objectMapper)
141 when(objectMapper).save(vuln).thenReturn(True)
142
143 model_controller = controller.ModelController(mock(), mappersManager)
144
145 model_controller.addVulnToServiceSYNC(None, service.getID(), vuln)
146
147 verify(mappersManager).getMapper(vuln.class_signature)
148 verify(objectMapper).save(vuln)
149
150 def testAddVulnToServiceGetsMapperDispatchSaveASYNC(self):
151 service = Service("servi")
152 vuln = ModelObjectVuln("a_vuln")
153
154 mappersManager = self.createMapperMock()
155 objectMapper = mock()
156 when(mappersManager).getMapper(vuln.class_signature).thenReturn(objectMapper)
157 when(objectMapper).save(vuln).thenReturn(True)
158
159 model_controller = controller.ModelController(mock(), mappersManager)
160
161 model_controller.addVulnToServiceASYNC(None, service.getID(), vuln)
162 model_controller.processAllPendingActions()
163
164 verify(mappersManager).getMapper(vuln.class_signature)
165 verify(objectMapper).save(vuln)
166
167 def testAddVulnToInterfaceGetsMapperDispatchSaveSYNC(self):
168 interface = Interface("int0")
169 vuln = ModelObjectVuln("a_vuln")
170
171 mappersManager = self.createMapperMock()
172 objectMapper = mock()
173 when(mappersManager).getMapper(vuln.class_signature).thenReturn(objectMapper)
174 when(objectMapper).save(vuln).thenReturn(True)
175
176 model_controller = controller.ModelController(mock(), mappersManager)
177
178 model_controller.addVulnToInterfaceSYNC(None, interface.getID(), vuln)
179
180 verify(mappersManager).getMapper(vuln.class_signature)
181 verify(objectMapper).save(vuln)
182
183 def testAddVulnToInterfaceGetsMapperDispatchSaveASYNC(self):
184 interface = Interface("int0")
185 vuln = ModelObjectVuln("a_vuln")
186
187 mappersManager = self.createMapperMock()
188 objectMapper = mock()
189 when(mappersManager).getMapper(vuln.class_signature).thenReturn(objectMapper)
190 when(objectMapper).save(vuln).thenReturn(True)
191
192 model_controller = controller.ModelController(mock(), mappersManager)
193
194 model_controller.addVulnToInterfaceASYNC(None, interface.getID(), vuln)
195 model_controller.processAllPendingActions()
196
197 verify(mappersManager).getMapper(vuln.class_signature)
198 verify(objectMapper).save(vuln)
199
200 def testAddVulnToHostGetsMapperDispatchSaveSYNC(self):
201 host = Host("pepito")
202 vuln = ModelObjectVuln("a_vuln")
203
204 mappersManager = self.createMapperMock()
205 objectMapper = mock()
206 when(mappersManager).getMapper(vuln.class_signature).thenReturn(objectMapper)
207 when(objectMapper).save(vuln).thenReturn(True)
208
209 model_controller = controller.ModelController(mock(), mappersManager)
210
211 model_controller.addVulnToHostSYNC(host.getID(), vuln)
212
213 verify(mappersManager).getMapper(vuln.class_signature)
214 verify(objectMapper).save(vuln)
215
216 def testAddVulnToHostGetsMapperDispatchSaveASYNC(self):
217 host = Host("pepito")
218 vuln = ModelObjectVuln("a_vuln")
219
220 mappersManager = self.createMapperMock()
221 objectMapper = mock()
222 when(mappersManager).getMapper(vuln.class_signature).thenReturn(objectMapper)
223 when(objectMapper).save(vuln).thenReturn(True)
224
225 model_controller = controller.ModelController(mock(), mappersManager)
226
227 model_controller.addVulnToHostASYNC(host.getID(), vuln)
228 model_controller.processAllPendingActions()
229
230 verify(mappersManager).getMapper(vuln.class_signature)
231 verify(objectMapper).save(vuln)
232
233 def testAddNoteToServiceGetsMapperDispatchSaveSYNC(self):
234 service = Service("servi")
235 note = ModelObjectNote("a_note")
236
237 mappersManager = self.createMapperMock()
238 objectMapper = mock()
239 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
240 when(objectMapper).save(note).thenReturn(True)
241
242 model_controller = controller.ModelController(mock(), mappersManager)
243
244 model_controller.addNoteToServiceSYNC(None, service.getID(), note)
245
246 verify(mappersManager).getMapper(note.class_signature)
247 verify(objectMapper).save(note)
248
249 def testAddNoteToServiceGetsMapperDispatchSaveASYNC(self):
250 service = Service("servi")
251 note = ModelObjectNote("a_note")
252
253 mappersManager = self.createMapperMock()
254 objectMapper = mock()
255 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
256 when(objectMapper).save(note).thenReturn(True)
257
258 model_controller = controller.ModelController(mock(), mappersManager)
259
260 model_controller.addNoteToServiceASYNC(None, service.getID(), note)
261 model_controller.processAllPendingActions()
262
263 verify(mappersManager).getMapper(note.class_signature)
264 verify(objectMapper).save(note)
265
266 def testAddNoteToVulnGetsMapperDispatchSave(self):
267 vuln = ModelObjectVuln('a vuln')
268 note = ModelObjectNote("a_note")
269
270 mappersManager = self.createMapperMock()
271 objectMapper = mock()
272 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
273 when(objectMapper).save(note).thenReturn(True)
274
275 model_controller = controller.ModelController(mock(), mappersManager)
276
277 model_controller.addNoteToServiceSYNC(None, vuln.getID(), note)
278
279 verify(mappersManager).getMapper(note.class_signature)
280 verify(objectMapper).save(note)
281
282 def testAddNoteToServiceGetsMapperDispatchSaveSYNC(self):
283 service = Service("servi")
284 note = ModelObjectNote("a_note")
285
286 mappersManager = self.createMapperMock()
287 objectMapper = mock()
288 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
289 when(objectMapper).save(note).thenReturn(True)
290
291 model_controller = controller.ModelController(mock(), mappersManager)
292
293 model_controller.addNoteToServiceSYNC(None, service.getID(), note)
294
295 verify(mappersManager).getMapper(note.class_signature)
296 verify(objectMapper).save(note)
297
298 def testAddNoteToServiceGetsMapperDispatchSaveASYNC(self):
299 service = Service("servi")
300 note = ModelObjectNote("a_note")
301
302 mappersManager = self.createMapperMock()
303 objectMapper = mock()
304 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
305 when(objectMapper).save(note).thenReturn(True)
306
307 model_controller = controller.ModelController(mock(), mappersManager)
308
309 model_controller.addNoteToServiceASYNC(None, service.getID(), note)
310 model_controller.processAllPendingActions()
311
312 verify(mappersManager).getMapper(note.class_signature)
313 verify(objectMapper).save(note)
314
315 def testAddNoteToInterfaceGetsMapperDispatchSaveSYNC(self):
316 interface = Interface("int0")
317 note = ModelObjectNote("a_note")
318
319 mappersManager = self.createMapperMock()
320 objectMapper = mock()
321 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
322 when(objectMapper).save(note).thenReturn(True)
323
324 model_controller = controller.ModelController(mock(), mappersManager)
325
326 model_controller.addNoteToServiceSYNC(None, interface.getID(), note)
327
328 verify(mappersManager).getMapper(note.class_signature)
329 verify(objectMapper).save(note)
330
331 def testAddNoteToInterfaceGetsMapperDispatchSaveASYNC(self):
332 interface = Interface("int0")
333 note = ModelObjectNote("a_note")
334
335 mappersManager = self.createMapperMock()
336 objectMapper = mock()
337 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
338 when(objectMapper).save(note).thenReturn(True)
339
340 model_controller = controller.ModelController(mock(), mappersManager)
341
342 model_controller.addNoteToServiceASYNC(None, interface.getID(), note)
343 model_controller.processAllPendingActions()
344
345 verify(mappersManager).getMapper(note.class_signature)
346 verify(objectMapper).save(note)
347
348 def testAddNoteToHostGetsMapperDispatchSaveSYNC(self):
349 host = Host("pepito")
350 note = ModelObjectNote("a_note")
351
352 mappersManager = self.createMapperMock()
353 objectMapper = mock()
354 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
355 when(objectMapper).save(note).thenReturn(True)
356
357 model_controller = controller.ModelController(mock(), mappersManager)
358
359 model_controller.addNoteToHostSYNC(host.getID(), note)
360
361 verify(mappersManager).getMapper(note.class_signature)
362 verify(objectMapper).save(note)
363
364 def testAddNoteToHostGetsMapperDispatchSaveASYNC(self):
365 host = Host("pepito")
366 note = ModelObjectNote("a_note")
367
368 mappersManager = self.createMapperMock()
369 objectMapper = mock()
370 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
371 when(objectMapper).save(note).thenReturn(True)
372
373 model_controller = controller.ModelController(mock(), mappersManager)
374
375 model_controller.addNoteToHostASYNC(host.getID(), note)
376 model_controller.processAllPendingActions()
377
378 verify(mappersManager).getMapper(note.class_signature)
379 verify(objectMapper).save(note)
380
381 def testAddNoteToInterfaceGetsMapperDispatchSaveSYNC(self):
382 interface = Interface("pepito")
383 note = ModelObjectNote("a_note")
384
385 mappersManager = self.createMapperMock()
386 objectMapper = mock()
387 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
388 when(objectMapper).save(note).thenReturn(True)
389
390 model_controller = controller.ModelController(mock(), mappersManager)
391
392 model_controller.addNoteToInterfaceSYNC(None, interface.getID(), note)
393
394 verify(mappersManager).getMapper(note.class_signature)
395 verify(objectMapper).save(note)
396
397 def testAddNoteToInterfaceGetsMapperDispatchSaveASYNC(self):
398 interface = Interface("pepito")
399 note = ModelObjectNote("a_note")
400
401 mappersManager = self.createMapperMock()
402 objectMapper = mock()
403 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
404 when(objectMapper).save(note).thenReturn(True)
405
406 model_controller = controller.ModelController(mock(), mappersManager)
407
408 model_controller.addNoteToInterfaceASYNC(None, interface.getID(), note)
409 model_controller.processAllPendingActions()
410
411 verify(mappersManager).getMapper(note.class_signature)
412 verify(objectMapper).save(note)
413
414 def testAddNoteToNoteGetsMapperDispatchSaveSYNC(self):
415 host = Host("pepito")
416 note = ModelObjectNote("a_note")
417
418 mappersManager = self.createMapperMock()
419 objectMapper = mock()
420 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
421 when(objectMapper).save(note).thenReturn(True)
422
423 model_controller = controller.ModelController(mock(), mappersManager)
424
425 model_controller.addNoteToNoteSYNC(note.getID(), note)
426
427 verify(mappersManager).getMapper(note.class_signature)
428 verify(objectMapper).save(note)
429
430 def testAddNoteToNoteGetsMapperDispatchSaveASYNC(self):
431 host = Host("pepito")
432 note = ModelObjectNote("a_note")
433
434 mappersManager = self.createMapperMock()
435 objectMapper = mock()
436 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
437 when(objectMapper).save(note).thenReturn(True)
438
439 model_controller = controller.ModelController(mock(), mappersManager)
440
441 model_controller.addNoteToNoteASYNC(None, None, note.getID(), note)
442 model_controller.processAllPendingActions()
443
444 verify(mappersManager).getMapper(note.class_signature)
445 verify(objectMapper).save(note)
446
447 def testAddSavesObjectNameInTrie(self):
448 host = Host('coco')
449
450 mappersManager = self.createMapperMock()
451 objectMapper = mock()
452 triemock = mock()
453
454 when(mappersManager).getMapper(host.class_signature).thenReturn(objectMapper)
455 when(objectMapper).save(host).thenReturn(True)
456 when(triemock).addWord(host.getName()).thenReturn(True)
457
458 model_controller = controller.ModelController(mock(), mappersManager)
459 model_controller.treeWordsTries = triemock
460
461 model_controller.addHostSYNC(host)
462
463 verify(mappersManager).getMapper(host.class_signature)
464 verify(objectMapper).save(host)
465 verify(triemock).addWord(host.getName())
466
467 def testAddNoteToModelObjectSYNC(self):
468 host = Host("pepito")
469 note = ModelObjectNote("a_note")
470
471 mappersManager = self.createMapperMock()
472 objectMapper = mock()
473 when(mappersManager).getMapper(note.class_signature).thenReturn(objectMapper)
474 when(objectMapper).save(note).thenReturn(True)
475
476 model_controller = controller.ModelController(mock(), mappersManager)
477
478 model_controller.addNoteSYNC(host.getID(), note)
479
480 verify(mappersManager).getMapper(note.class_signature)
481 verify(objectMapper).save(note)
482
483 def createMapperMock(self):
484 map_mock = mock()
485 when(map_mock).find(any()).thenReturn(mock())
486 when(map_mock).find(None).thenReturn(None)
487 return map_mock
488
489 def testAddCredGetsMapperDispatchSaveSYNC(self):
490 host = Host("pepito")
491 cred = ModelObjectCred("usr", "pass")
492
493 mappersManager = self.createMapperMock()
494 objectMapper = mock()
495 when(mappersManager).getMapper(cred.class_signature).thenReturn(objectMapper)
496 when(objectMapper).save(cred).thenReturn(True)
497
498 model_controller = controller.ModelController(mock(), mappersManager)
499
500 model_controller.addCredSYNC(cred.getID(), cred)
501
502 verify(mappersManager).getMapper(cred.class_signature)
503 verify(objectMapper).save(cred)
504
505
506 def testAddCredToServiceGetsMapperDispatchSaveSYNC(self):
507 service = Service("pepito")
508 cred = ModelObjectCred("usr", "pass")
509
510 mappersManager = self.createMapperMock()
511 objectMapper = mock()
512 when(mappersManager).getMapper(cred.class_signature).thenReturn(objectMapper)
513 when(objectMapper).save(cred).thenReturn(True)
514
515 model_controller = controller.ModelController(mock(), mappersManager)
516
517 model_controller.addCredToServiceSYNC(None, cred.getID(), cred)
518
519 verify(mappersManager).getMapper(cred.class_signature)
520 verify(objectMapper).save(cred)
521
522 def testAddCredToServiceGetsMapperDispatchSaveASYNC(self):
523 service = Service("pepito")
524 cred = ModelObjectCred("usr", "pass")
525
526 mappersManager = self.createMapperMock()
527 objectMapper = mock()
528 when(mappersManager).getMapper(cred.class_signature).thenReturn(objectMapper)
529 when(objectMapper).save(cred).thenReturn(True)
530
531 model_controller = controller.ModelController(mock(), mappersManager)
532
533 model_controller.addCredToServiceASYNC(None, cred.getID(), cred)
534 model_controller.processAllPendingActions()
535
536 verify(mappersManager).getMapper(cred.class_signature)
537 verify(objectMapper).save(cred)
538
539 def testDeleteHostObjectDispatchRemoveSYNC(self):
540 host = Host("coquito")
541
542 mappersManager = self.createMapperMock()
543 objectMapper = mock()
544 when(mappersManager).find(host.getID()).thenReturn(host)
545 when(mappersManager).remove(host.getID()).thenReturn(True)
546
547 model_controller = controller.ModelController(mock(), mappersManager)
548 model_controller.delHostSYNC(host.getID())
549 verify(mappersManager).remove(host.getID())
550 verify(mappersManager).find(host.getID())
551
552 def testDeleteHostObjectDispatchRemoveASYNC(self):
553 host = Host("coquito")
554
555 mappersManager = self.createMapperMock()
556 objectMapper = mock()
557 when(mappersManager).find(host.getID()).thenReturn(host)
558 when(mappersManager).remove(host.getID()).thenReturn(True)
559
560 model_controller = controller.ModelController(mock(), mappersManager)
561 model_controller.delHostASYNC(host.getID())
562 model_controller.processAllPendingActions()
563
564 verify(mappersManager).remove(host.getID())
565
566 def testDeleteModelObjectRemovesChildFromParentSYNC(self):
567 host = Host('coco')
568 interface = Interface("int_mock0")
569 self.genericDelTest(host, interface, controller.ModelController.delInterfaceSYNC)
570
571 def testDeleteModelObjectRemovesChildFromParentASYNC(self):
572 host = Host('coco')
573 interface = Interface("int_mock0")
574 self.genericDelTest(host, interface, controller.ModelController.delInterfaceASYNC, process_pending=True)
575
576 def testInterfaceFromHostRemovedSYNC(self):
577 host = Host('coco')
578 interface = Interface("int_mock0")
579 self.genericDelTest(host, interface,
580 controller.ModelController.delInterfaceSYNC)
581
582 def testInterfaceFromHostRemovedSYNC(self):
583 service = Service('coco')
584 interface = Interface("int_mock0")
585 interface.addChild(service)
586 self.genericDelTest(interface, service,
587 controller.ModelController.delServiceFromInterfaceSYNC)
588
589 def testInterfaceFromHostRemovedASYNC(self):
590 service = Service('coco')
591 interface = Interface("int_mock0")
592 interface.addChild(service)
593 self.genericDelTest(interface, service,
594 controller.ModelController.delServiceFromInterfaceASYNC, process_pending=True)
595
596 def testDelVulnFromHostSYNC(self):
597 host = Host('coco')
598 vuln = ModelObjectVuln("int_mock0")
599 host.addChild(vuln)
600 self.genericDelTest(host, vuln,
601 controller.ModelController.delVulnFromHostSYNC)
602
603 def testDelVulnFromHostASYNC(self):
604 host = Host('coco')
605 vuln = ModelObjectVuln("int_mock0")
606 host.addChild(vuln)
607 self.genericDelTest(host, vuln,
608 controller.ModelController.delVulnFromHostASYNC, process_pending=True)
609
610 def testDelVulnFromObjectSYNC(self):
611 host = Host('coco')
612 vuln = ModelObjectVuln("int_mock0")
613 host.addChild(vuln)
614 self.genericDelTest(host, vuln,
615 controller.ModelController.delVulnSYNC)
616
617 def testDelVulnFromServiceSYNC(self):
618 service = Service('coco')
619 vuln = ModelObjectVuln("int_mock0")
620 service.addChild(vuln)
621 self.genericDelTest(service, vuln,
622 controller.ModelController.delVulnFromServiceSYNC)
623
624 def testDelVulnFromServiceASYNC(self):
625 service = Service('coco')
626 vuln = ModelObjectVuln("int_mock0")
627 service.addChild(vuln)
628 self.genericDelTest(service, vuln,
629 controller.ModelController.delVulnFromServiceASYNC, process_pending=True)
630
631 # def delNoteFromInterfaceSYNC(self, hostname, intname, noteId):
632
633 def testDelNoteFromInterfaceSYNC(self):
634 interface = Interface('coco')
635 note = ModelObjectNote("int_mock0")
636 interface.addChild(note)
637 self.genericDelTest(interface, note,
638 controller.ModelController.delNoteFromInterfaceSYNC)
639
640 def testDelNoteFromInterfaceASYNC(self):
641 interface = Interface('coco')
642 note = ModelObjectNote("int_mock0")
643 interface.addChild(note)
644 self.genericDelTest(interface, note,
645 controller.ModelController.delNoteFromInterfaceASYNC, process_pending=True)
646
647
648 def testDelNoteFromServiceSYNC(self):
649 service = Service('coco')
650 note = ModelObjectNote("int_mock0")
651 service.addChild(note)
652 self.genericDelTest(service, note,
653 controller.ModelController.delNoteFromServiceSYNC)
654
655 def testDelNoteFromServiceASYNC(self):
656 service = Service('coco')
657 note = ModelObjectNote("int_mock0")
658 service.addChild(note)
659 self.genericDelTest(service, note,
660 controller.ModelController.delNoteFromServiceASYNC, process_pending=True)
661
662 def testDelNoteFromHostSYNC(self):
663 host = Host('coco')
664 note = ModelObjectNote("int_mock0")
665 host.addChild(note)
666 self.genericDelTest(host, note,
667 controller.ModelController.delNoteFromHostSYNC)
668
669 def testDelNoteFromHostSYNC(self):
670 host = Host('coco')
671 note = ModelObjectNote("int_mock0")
672 host.addChild(note)
673 self.genericDelTest(host, note,
674 controller.ModelController.delNoteFromHostASYNC, process_pending=True)
675
676 def testDelNoteFromModelObjectSYNC(self):
677 host = Host('coco')
678 note = ModelObjectNote("int_mock0")
679 host.addChild(note)
680 self.genericDelTest(host, note,
681 controller.ModelController.delNoteSYNC)
682
683 def testDelCredentialFromServiceSYNC(self):
684 service = Service('coco')
685 cred = ModelObjectCred("int_mock0")
686 service.addChild(cred)
687 self.genericDelTest(service, cred,
688 controller.ModelController.delCredFromServiceSYNC)
689
690 def testDelCredentialFromServiceASYNC(self):
691 service = Service('coco')
692 cred = ModelObjectCred("int_mock0")
693 service.addChild(cred)
694 self.genericDelTest(service, cred,
695 controller.ModelController.delCredFromServiceASYNC, process_pending=True)
696
697 def testDelCredentialFromModelObjectSYNC(self):
698 service = Service('coco')
699 cred = ModelObjectCred("int_mock0")
700 service.addChild(cred)
701 self.genericDelTest(service, cred,
702 controller.ModelController.delCredSYNC)
703
704 def testDelRemovesObjectFromTrie(self):
705 host = Host("coquito")
706
707 mappersManager = self.createMapperMock()
708 objectMapper = mock()
709 triemock = mock()
710 when(mappersManager).getMapper(host.class_signature).thenReturn(objectMapper)
711 when(mappersManager).find(host.getID()).thenReturn(host)
712 when(triemock).addWord(host.getName()).thenReturn(True)
713
714 model_controller = controller.ModelController(mock(), mappersManager)
715 model_controller.treeWordsTries = triemock
716 model_controller.delHostSYNC(host.getID())
717 verify(mappersManager).remove(host.getID())
718
719 verify(triemock).removeWord(host.getName())
720
721 def genericDelTest(self, obj1, obj2, test_method, process_pending=False):
722 mappersManager = self.createMapperMock()
723 objectMapper = mock()
724 triemock = mock()
725 when(mappersManager).find(obj2.getID()).thenReturn(obj2)
726 when(objectMapper).delObject(obj2.getID()).thenReturn(True)
727
728 model_controller = controller.ModelController(mock(), mappersManager)
729 model_controller.treeWordsTries = triemock
730
731 try:
732 test_method(model_controller, None, obj2.getID())
733 except:
734 test_method(model_controller, None, None, obj2.getID())
735
736 if process_pending:
737 model_controller.processAllPendingActions()
738
739 verify(mappersManager).find(obj2.getID())
740 verify(mappersManager).remove(obj2.getID())
741
742 def testEditHostSyncGetsMapperDispatchedSYNC(self):
743 host = Host("coquito")
744
745 mappersManager = self.createMapperMock()
746 dataMapper = mock()
747 objectMapper = mock()
748 triemock = mock()
749 when(mappersManager).getMapper(host.class_signature).thenReturn(dataMapper)
750 when(dataMapper).save(host).thenReturn(True)
751
752 model_controller = controller.ModelController(mock(), mappersManager)
753
754 model_controller.editHostSYNC(host, 'new_name', 'new_desc', 'new_os', True)
755
756 verify(dataMapper).save(host)
757
758 self.assertEquals(host.getName(), 'new_name', "Name not updated")
759 self.assertEquals(host.getDescription(), 'new_desc', "Description not updated")
760 self.assertEquals(host.getOS(), 'new_os', "OS not updated")
761 self.assertEquals(host.isOwned(), True, "Owned status not updated")
762
763 def testEditServiceSyncGetsMapperDispatchedSYNC(self):
764 service = Service("coquito")
765
766 params = ('new_name', 'new_desc', 'upd', 9000, 'closed', '2.1', True)
767 self.genericEdit(service, params, controller.ModelController.editServiceSYNC)
768
769 self.assertEquals(service.getName(), 'new_name', "Name not updated")
770 self.assertEquals(service.getDescription(), 'new_desc', "Description not updated")
771 self.assertEquals(service.getProtocol(), 'upd', "Protocol not updated")
772 self.assertEquals(service.isOwned(), True, "Owned status not updated")
773
774 def testEditServiceSyncGetsMapperDispatchedASYNC(self):
775 service = Service("coquito")
776
777 params = ('new_name', 'new_desc', 'upd', 9000, 'closed', '2.1', True)
778 self.genericEdit(service, params, controller.ModelController.editServiceASYNC,
779 process_pending=True)
780
781 self.assertEquals(service.getName(), 'new_name', "Name not updated")
782 self.assertEquals(service.getDescription(), 'new_desc', "Description not updated")
783 self.assertEquals(service.getProtocol(), 'upd', "Protocol not updated")
784 self.assertEquals(service.isOwned(), True, "Owned status not updated")
785
786 def testEditServiceSyncGetsMapperDispatchedSYNC(self):
787 service = Service("coquito")
788
789 params = ('new_name', 'new_desc', 'upd', 9000, 'closed', '2.1', True)
790 self.genericEdit(service, params, controller.ModelController.editServiceSYNC)
791
792 self.assertEquals(service.getName(), 'new_name', "Name not updated")
793 self.assertEquals(service.getDescription(), 'new_desc', "Description not updated")
794 self.assertEquals(service.getProtocol(), 'upd', "Protocol not updated")
795 self.assertEquals(service.isOwned(), True, "Owned status not updated")
796
797 def testEditServiceSyncGetsMapperDispatchedASYNC(self):
798 service = Service("coquito")
799
800 params = ('new_name', 'new_desc', 'upd', 9000, 'closed', '2.1', True)
801 self.genericEdit(service, params, controller.ModelController.editServiceASYNC, process_pending=True)
802
803 self.assertEquals(service.getName(), 'new_name', "Name not updated")
804 self.assertEquals(service.getDescription(), 'new_desc', "Description not updated")
805 self.assertEquals(service.getProtocol(), 'upd', "Protocol not updated")
806 self.assertEquals(service.isOwned(), True, "Owned status not updated")
807
808 def testEditInterfaceSyncGetsMapperDispatchedSYNC(self):
809 inter = Interface("coquito")
810
811 params = ('new_name', 'new_desc', 'hostname1', "FF:AA:EE:11:00", None,
812 None, None, None, None, None, True)
813
814 self.genericEdit(inter, params, controller.ModelController.editInterfaceSYNC)
815
816 self.assertEquals(inter.getName(), 'new_name', "Name not updated")
817 self.assertEquals(inter.getDescription(), 'new_desc', "Description not updated")
818 self.assertEquals(inter.isOwned(), True, "Owned status not updated")
819
820
821 def testEditVulnSyncGetsMapperDispatchedSYNC(self):
822 vuln = ModelObjectVuln("coquito")
823
824 params = ('new_name', 'new_desc', 'high', "ref1")
825
826 self.genericEdit(vuln, params, controller.ModelController.editVulnSYNC)
827
828 self.assertEquals(vuln.getName(), 'new_name', "Name not updated")
829 self.assertEquals(vuln.getDescription(), 'new_desc', "Description not updated")
830 self.assertEquals(vuln.getSeverity(), 'high', "Severity not updated")
831
832 def testEditVulnSyncGetsMapperDispatchedASYNC(self):
833 vuln = ModelObjectVuln("coquito")
834
835 params = ('new_name', 'new_desc', 'high', "ref1")
836
837 self.genericEdit(vuln, params, controller.ModelController.editVulnASYNC, process_pending=True)
838
839 self.assertEquals(vuln.getName(), 'new_name', "Name not updated")
840 self.assertEquals(vuln.getDescription(), 'new_desc', "Description not updated")
841 self.assertEquals(vuln.getSeverity(), 'high', "Severity not updated")
842
843 def testEditVulnWebSyncGetsMapperDispatchedSYNC(self):
844 vuln = ModelObjectVulnWeb("coquito")
845
846 params = ('new_name', 'new_desc', 'www.goole.com', 'index.html',
847 "ref1", 'high', None, None, 'GET', 'pepe', 'coco' , 'caca',
848 None)
849
850 self.genericEdit(vuln, params, controller.ModelController.editVulnWebSYNC)
851
852 self.assertEquals(vuln.getName(), 'new_name', "Name not updated")
853 self.assertEquals(vuln.getDescription(), 'new_desc', "Description not updated")
854 self.assertEquals(vuln.getSeverity(), 'high', "Severity not updated")
855
856 def testEditVulnWebSyncGetsMapperDispatchedASYNC(self):
857 vuln = ModelObjectVulnWeb("coquito")
858
859 params = ('new_name', 'new_desc', 'www.goole.com', 'index.html',
860 "ref1", 'high', None, None, 'GET', 'pepe', 'coco' , 'caca',
861 None)
862
863 self.genericEdit(vuln, params, controller.ModelController.editVulnWebASYNC, process_pending=True)
864
865 self.assertEquals(vuln.getName(), 'new_name', "Name not updated")
866 self.assertEquals(vuln.getDescription(), 'new_desc', "Description not updated")
867 self.assertEquals(vuln.getSeverity(), 'high', "Severity not updated")
868
869 def testEditNoteSyncGetsMapperDispatchedSYNC(self):
870 note = ModelObjectNote("coquito")
871
872 params = ('new_name', 'new_desc')
873 self.genericEdit(note, params, controller.ModelController.editNoteSYNC)
874 self.assertEquals(note.getName(), 'new_name', "Name not updated")
875 self.assertEquals(note.text, 'new_desc', "Description not updated")
876
877 def testEditNoteSyncGetsMapperDispatchedASYNC(self):
878 note = ModelObjectNote("coquito")
879
880 params = ('new_name', 'new_desc')
881 self.genericEdit(note, params, controller.ModelController.editNoteASYNC, process_pending=True)
882 self.assertEquals(note.getName(), 'new_name', "Name not updated")
883 self.assertEquals(note.text, 'new_desc', "Description not updated")
884
885 def testEditCredSyncGetsMapperDispatchedSYNC(self):
886 cred = ModelObjectCred("coquito")
887
888 params = ('new_user', 'new_pass')
889 self.genericEdit(cred, params, controller.ModelController.editCredSYNC)
890 self.assertEquals(cred.getUsername(), 'new_user', "Username not updated")
891 self.assertEquals(cred.getPassword(), 'new_pass', "Password not updated")
892
893 def testEditCredSyncGetsMapperDispatchedASYNC(self):
894 cred = ModelObjectCred("coquito")
895
896 params = ('new_user', 'new_pass')
897 self.genericEdit(cred, params, controller.ModelController.editCredASYNC, process_pending=True)
898 self.assertEquals(cred.getUsername(), 'new_user', "Username not updated")
899 self.assertEquals(cred.getPassword(), 'new_pass', "Password not updated")
900
901 def testGetAllHosts(self):
902 hosts = [ Host("coquito%i" % i ) for i in range(10)]
903
904 mappersManager = self.createMapperMock()
905 objectMapper = mock()
906 when(mappersManager).getMapper(Host.__name__).thenReturn(objectMapper)
907 when(objectMapper).getAll().thenReturn(hosts)
908
909 model_controller = controller.ModelController(mock(), mappersManager)
910 hosts_obt = model_controller.getAllHosts()
911 verify(objectMapper).getAll()
912 verify(mappersManager).getMapper(Host.__name__)
913
914 self.assertListEqual(hosts, hosts_obt)
915
916 def testGetHost(self):
917 host = Host("coquito")
918
919 mappersManager = self.createMapperMock()
920 objectMapper = mock()
921 when(mappersManager).getMapper(host.__class__.__name__).thenReturn(objectMapper)
922 when(objectMapper).find(host.getName()).thenReturn(host)
923
924 model_controller = controller.ModelController(mock(), mappersManager)
925
926 host_obt = model_controller.getHost('coquito')
927
928 verify(objectMapper).find(host.getName())
929 verify(mappersManager).getMapper(host.__class__.__name__)
930
931 self.assertEqual(host, host_obt)
932
933 def genericEdit(self, obj, params, callback, process_pending=False):
934 mappersManager = self.createMapperMock()
935 dataMapper = mock()
936 objId = obj.getID()
937 when(mappersManager).getMapper(obj.class_signature).thenReturn(dataMapper)
938 when(dataMapper).save(obj).thenReturn(True)
939 when(mappersManager).find(objId).thenReturn(obj)
940 when(mappersManager).save(obj).thenReturn(True)
941 model_controller = controller.ModelController(mock(), mappersManager)
942 callback(model_controller, obj, *params)
943 if process_pending:
944 model_controller.processAllPendingActions()
945
946
947 if __name__ == '__main__':
948 unittest.main()
949
+0
-254
tests/dont_run_but_update_pluginbase.py less more
0 #!/usr/bin/python
1
2 '''
3 Faraday Penetration Test IDE
4 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
5 See the file 'doc/LICENSE' for the license information
6
7 '''
8
9 from unittest import TestCase
10 import unittest
11 import sys
12 sys.path.append('.')
13 import faraday.client.model.controller as controller
14 from mockito import mock, when
15 from faraday.client.model import api
16 from faraday.client.plugins.core import PluginBase, PluginController
17 from faraday.client.model.workspace import Workspace
18 from faraday.client.model.container import ModelObjectContainer
19 from faraday.client.managers.all import CommandManager
20 from time import time
21 from faraday.client.model.commands_history import CommandRunInformation
22
23
24 class TestPluginCreateModelObject(TestCase):
25 """docstring for TestModelObjectCRUD"""
26 def setUp(self):
27 self._model_controller = controller.ModelController(mock())
28 self.cm = mock(CommandManager)
29 when(self.cm).saveCommand().thenReturn(True)
30 self._plugin_controller = PluginController("test", {}, self.cm)
31
32 class PluginTest(PluginBase):
33 def __init__(self):
34 PluginBase.__init__(self)
35 self.id = "Test"
36 self.name = "Test"
37
38 def parseOutputString(self, output, debug=False):
39 pass
40
41 self.workspace = mock(Workspace)
42 when(self.workspace).getContainee().thenReturn(ModelObjectContainer())
43 self._model_controller.setWorkspace(self.workspace)
44
45 self.plugin = PluginTest()
46 api.setUpAPIs(self._model_controller)
47
48 self._plugin_controller.setActivePlugin(self.plugin)
49 self.cmdinfo = CommandRunInformation(
50 **{'workspace': 'test',
51 'itime': time(),
52 'command': 'test',
53 'params': 'test'})
54
55 def test_create_host(self):
56 """
57 Testing the creation of one host
58 """
59 h = self.plugin.createAndAddHost("pepito", "linux")
60 self._plugin_controller.last_command_information = self.cmdinfo
61 self._plugin_controller.onCommandFinished()
62 self._model_controller.processAllPendingActions()
63
64 self.assertTrue(h is not None, "host should have an ID")
65 self.assertTrue(len(self._model_controller.getAllHosts()) == 1, "The controller should have one host")
66 self.assertTrue(self._model_controller.getHost(h) is not None, "The host should be in the controller")
67
68 def test_create_same_host_two_times(self):
69 """
70 Testing the creation of the same host, two times.
71 This simulates two plugins creating the host with the same name
72 We should end up with just one host in the controller
73 """
74 h1 = self.plugin.createAndAddHost("pepito", "linux")
75 h2 = self.plugin.createAndAddHost("pepito", "linux")
76 self._plugin_controller.last_command_information = self.cmdinfo
77 self._plugin_controller.onCommandFinished()
78 self._model_controller.processAllPendingActions()
79
80 self.assertTrue(len(self._model_controller.getAllHosts()) == 1, "The controller should have just one host")
81 self.assertTrue(self._model_controller.getHost(h1) == self._model_controller.getHost(h2), "The host should be the same")
82
83 def test_create_host_with_interface(self):
84 """
85 Testing the creation of one host, with one interface
86 """
87 h = self.plugin.createAndAddHost("pepito", "linux")
88 i = self.plugin.createAndAddInterface(h, "1.2.3.4")
89 self._plugin_controller.last_command_information = self.cmdinfo
90 self._plugin_controller.onCommandFinished()
91 self._model_controller.processAllPendingActions()
92
93 self.assertTrue(i is not None, "interface should have an ID")
94 host = self._model_controller.getHost(h)
95 self.assertTrue(len(host.getAllInterfaces()) == 1, "Host should have one interface")
96 self.assertTrue(host.getInterface(i) is not None, "The interface should be the one we've just create")
97
98 def test_create_interface_two_times(self):
99 """
100 Testing the creation of the same interface, two times.
101 This simulates two plugins creating the host with the same interface
102 We should end up with just one interface in that host
103 """
104 h1 = self.plugin.createAndAddHost("pepito", "linux")
105 i1 = self.plugin.createAndAddInterface(h1, "1.2.3.4")
106
107 h2 = self.plugin.createAndAddHost("pepito", "linux")
108 i2 = self.plugin.createAndAddInterface(h2, "1.2.3.4")
109
110 self._plugin_controller.last_command_information = self.cmdinfo
111 self._plugin_controller.onCommandFinished()
112 self._model_controller.processAllPendingActions()
113
114 self.assertTrue(len(self._model_controller.getAllHosts()) == 1, "The controller should have just one host")
115 self.assertTrue(len(self._model_controller.getHost(h1).getAllInterfaces()) == 1, "The host should have just one interface")
116
117 def test_create_host_with_interface_with_service(self):
118 """
119 Testing the creation of one host, with one interface and one service on that interface
120 """
121 h = self.plugin.createAndAddHost("pepito", "linux")
122 i = self.plugin.createAndAddInterface(h, "1.2.3.4")
123 s = self.plugin.createAndAddServiceToInterface(h, i, "unknown", protocol="tcp", ports=['80'])
124 self._plugin_controller.last_command_information = self.cmdinfo
125 self._plugin_controller.onCommandFinished()
126 self._model_controller.processAllPendingActions()
127
128 host = self._model_controller.getHost(h)
129 interface = host.getInterface(i)
130 self.assertTrue(len(interface.getAllServices()) == 1, "The interface should have just one service")
131 self.assertTrue(interface.getService(s) is not None, "The service should be the one we've just create")
132
133 def test_create_two_services_different_names_equal_port(self):
134 """
135 Testing the creation of two services with different names but same protocol and port
136 The result should only one services being created, since both have the same id
137 """
138 h = self.plugin.createAndAddHost("pepito", "linux")
139 i = self.plugin.createAndAddInterface(h, "1.2.3.4")
140 s1 = self.plugin.createAndAddServiceToInterface(h, i, "unknown", protocol="tcp", ports=['80'])
141 s2 = self.plugin.createAndAddServiceToInterface(h, i, "test", protocol="tcp", ports=['80'])
142 self._plugin_controller.last_command_information = self.cmdinfo
143 self._plugin_controller.onCommandFinished()
144 self._model_controller.processAllPendingActions()
145
146 host = self._model_controller.getHost(h)
147 interface = host.getInterface(i)
148 self.assertEqual(s1, s2, "Both services should have the same id")
149 self.assertTrue(len(interface.getAllServices()) == 1, "The interface should have just one service")
150
151 def test_create_two_services_same_names_different_port(self):
152 """
153 Testing the creation of two services with same names but different port
154 The result should only two services being created, since both have the different ids
155 """
156 h = self.plugin.createAndAddHost("pepito", "linux")
157 i = self.plugin.createAndAddInterface(h, "1.2.3.4")
158 s1 = self.plugin.createAndAddServiceToInterface(h, i, "unknown", protocol="tcp", ports=['80'])
159 s2 = self.plugin.createAndAddServiceToInterface(h, i, "unknown", protocol="tcp", ports=['443'])
160 self._plugin_controller.last_command_information = self.cmdinfo
161 self._plugin_controller.onCommandFinished()
162 self._model_controller.processAllPendingActions()
163
164 host = self._model_controller.getHost(h)
165 interface = host.getInterface(i)
166 self.assertNotEqual(s1, s2, "Both services should have the same id")
167 self.assertTrue(len(interface.getAllServices()) == 2, "The interface should have two services")
168
169 def test_create_vuln_to_service(self):
170 """
171 Testing the creation of a vuln to a service
172 """
173 h = self.plugin.createAndAddHost("pepito", "linux")
174 i = self.plugin.createAndAddInterface(h, "1.2.3.4")
175 s1 = self.plugin.createAndAddServiceToInterface(h, i, "unknown", protocol="tcp", ports=['80'])
176 s2 = self.plugin.createAndAddServiceToInterface(h, i, "unknown", protocol="tcp", ports=['443'])
177 v = self.plugin.createAndAddVulnToService(h, s1, "vuln1", "descripcion")
178 self._plugin_controller.last_command_information = self.cmdinfo
179 self._plugin_controller.onCommandFinished()
180 self._model_controller.processAllPendingActions()
181
182 host = self._model_controller.getHost(h)
183 interface = host.getInterface(i)
184 service1 = interface.getService(s1)
185 service2 = interface.getService(s2)
186 self.assertTrue(len(service1.getVulns()) == 1, "The service should have one vuln")
187 self.assertTrue(service1.getVuln(v) is not None, "The vuln should be the one we've just create")
188 self.assertTrue(len(service2.getVulns()) == 0, "The service should't have any vuln")
189
190 def test_create_note_to_service(self):
191 """
192 Testing the creation of a vuln to a service
193 """
194 h = self.plugin.createAndAddHost("pepito", "linux")
195 i = self.plugin.createAndAddInterface(h, "1.2.3.4")
196 s1 = self.plugin.createAndAddServiceToInterface(h, i, "unknown", protocol="tcp", ports=['80'])
197 s2 = self.plugin.createAndAddServiceToInterface(h, i, "unknown", protocol="tcp", ports=['443'])
198 n = self.plugin.createAndAddNoteToService(h, s1, "note1", "desc1")
199 self._plugin_controller.last_command_information = self.cmdinfo
200 self._plugin_controller.onCommandFinished()
201 self._model_controller.processAllPendingActions()
202
203 host = self._model_controller.getHost(h)
204 interface = host.getInterface(i)
205 service1 = interface.getService(s1)
206 service2 = interface.getService(s2)
207 self.assertTrue(len(service1.getNotes()) == 1, "The service should have one vuln")
208 self.assertTrue(service1.getNote(n) is not None, "The vuln should be the one we've just create")
209 self.assertTrue(len(service2.getNotes()) == 0, "The service should't have any vuln")
210
211 def test_create_note_to_note_service(self):
212 """
213 Testing the creation of a vuln to a service
214 """
215 h = self.plugin.createAndAddHost("pepito", "linux")
216 i = self.plugin.createAndAddInterface(h, "1.2.3.4")
217 s1 = self.plugin.createAndAddServiceToInterface(h, i, "unknown", protocol="tcp", ports=['80'])
218 s2 = self.plugin.createAndAddServiceToInterface(h, i, "unknown", protocol="tcp", ports=['443'])
219 n = self.plugin.createAndAddNoteToService(h, s1, "note1", "desc1")
220 n2 = self.plugin.createAndAddNoteToNote(h, s1, n, "note2", "desc2")
221 self._plugin_controller.last_command_information = self.cmdinfo
222 self._plugin_controller.onCommandFinished()
223 self._model_controller.processAllPendingActions()
224
225 host = self._model_controller.getHost(h)
226 interface = host.getInterface(i)
227 service1 = interface.getService(s1)
228 service2 = interface.getService(s2)
229 note1 = service1.getNote(n)
230 self.assertTrue(service1.getNote(n) is not None, "The note should be the one we've just create")
231 self.assertTrue(len(note1.getNotes()) == 1, "The note should have a nested note")
232
233 def test_create_cred_to_service(self):
234 """
235 Testing the creation of a vuln to a service
236 """
237 h = self.plugin.createAndAddHost("pepito", "linux")
238 i = self.plugin.createAndAddInterface(h, "1.2.3.4")
239 s1 = self.plugin.createAndAddServiceToInterface(h, i, "unknown", protocol="tcp", ports=['80'])
240 c = self.plugin.createAndAddCredToService(h, s1, "user", "pass")
241 self._plugin_controller.last_command_information = self.cmdinfo
242 self._plugin_controller.onCommandFinished()
243 self._model_controller.processAllPendingActions()
244
245 host = self._model_controller.getHost(h)
246 interface = host.getInterface(i)
247 service1 = interface.getService(s1)
248 cred = service1.getCred(c)
249 self.assertTrue(service1.getCred(c) is not None, "The cred should be the one we've just create")
250 self.assertTrue(len(service1.getCreds()) == 1, "The service should have a nested note")
251
252 if __name__ == '__main__':
253 unittest.main()
44 See the file 'doc/LICENSE' for the license information
55
66 '''
7 from __future__ import absolute_import
78
89 import unittest
910 import sys
197198
198199 if __name__ == '__main__':
199200 unittest.main()
201
202
203 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7 from builtins import chr, range
8
69 import random
710 import string
811 import factory
4043 CommandObject,
4144 Comment,
4245 CustomFieldsSchema,
43 Agent)
46 Agent,
47 SearchFilter, Executor)
4448
4549 # Make partials for start and end date. End date must be after start date
4650 FuzzyStartTime = lambda: (
5660 )
5761 )
5862
59 all_unicode = ''.join(unichr(i) for i in xrange(65536))
63 all_unicode = ''.join(chr(i) for i in range(65536))
6064 UNICODE_LETTERS = ''.join(c for c in all_unicode if unicodedata.category(c) == 'Lu' or unicodedata.category(c) == 'Ll')
6165
6266
8488
8589 class WorkspaceFactory(FaradayFactory):
8690
87 name = FuzzyText(chars=string.lowercase+string.digits)
91 name = FuzzyText(chars=string.ascii_lowercase+string.digits)
8892 creator = factory.SubFactory(UserFactory)
8993
9094 class Meta:
150154 class ServiceFactory(WorkspaceObjectFactory):
151155 name = FuzzyText()
152156 description = FuzzyText()
153 port = FuzzyInteger(1, 2**31) # Using 2**16 it generates many collisions
157 port = FuzzyInteger(1, 65535)
154158 protocol = FuzzyChoice(['TCP', 'UDP'])
155159 host = factory.SubFactory(HostFactory, workspace=factory.SelfAttribute('..workspace'))
156160 status = FuzzyChoice(Service.STATUSES)
183187 severity = FuzzyChoice(['critical', 'high'])
184188
185189
186 class HasParentHostOrService(object):
190 class HasParentHostOrService:
187191 """
188192 Mixins for objects that must have either a host or a service,
189193 but ont both, as a parent.
268272
269273
270274 class VulnerabilityWebFactory(VulnerabilityGenericFactory):
271 method = FuzzyChoice(['GET', 'POST', 'PUT', 'PATCH' 'DELETE'])
275 method = FuzzyChoice(['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
272276 parameter_name = FuzzyText()
273277 service = factory.SubFactory(ServiceFactory, workspace=factory.SelfAttribute('..workspace'))
274278
435439 sqlalchemy_session = db.session
436440
437441
442 class ExecutorFactory(FaradayFactory):
443 name = FuzzyText()
444 agent = factory.SubFactory(AgentFactory)
445
446 class Meta:
447 model = Executor
448 sqlalchemy_session = db.session
449
450
451 class SearchFilterFactory(FaradayFactory):
452
453 name = FuzzyText()
454 user_query = FuzzyText()
455 json_query = FuzzyText()
456
457 creator = factory.SubFactory(UserFactory)
458
459 class Meta:
460 model = SearchFilter
461 sqlalchemy_session = db.session
462
463
464 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
67 import pytest
78 from contextlib import contextmanager
89 from faraday.server.models import (
8081 self.attachment = File(
8182 name='test.png',
8283 filename='test.png',
83 content='test',
84 content=b'test',
8485 object_type='vulnerability',
8586 object_id=self.service_vuln.id,
8687 creator=user,
9091 self.host_attachment = File(
9192 name='test.png',
9293 filename='test.png',
93 content='test',
94 content=b'test',
9495 object_type='host',
9596 object_id=self.host.id,
9697 creator=user,
243244 def test_delete_user_deletes_assignations(self):
244245 with self.assert_deletes(self.methodology_task_assigned):
245246 self.session.delete(self.user)
247 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
67 import os
78 import pytest
89 from faraday.server.models import File
1516 def depotfile():
1617 depot = DepotManager.get('default')
1718 path = os.path.join(CURRENT_PATH, '../data', 'faraday.png')
18 with open(path) as fp:
19 with open(path, 'rb') as fp:
1920 fileid = depot.create(fp, 'faraday.png', 'image/png')
2021 return fileid
2122
5556 assert len(vulnerability_web.evidence) == 1
5657 assert vulnerability_web.evidence[0].object_type == 'vulnerability'
5758 assert vulnerability_web.evidence[0].object_id == vulnerability_web.id
59 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
67 import random
78 import pytest
89 from functools import partial
241242
242243 for host in hosts_not_to_query_w2:
243244 assert str(host.id) not in res.json['hosts']
245 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 import pytest
6 from __future__ import absolute_import
77 from faraday.server.models import TagObject
88
99
2929
3030 session.commit()
3131 assert vuln.tags == set(correct_tags)
32 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
67 import pytest
78 from faraday.server.models import (
89 CommandObject,
251252 session.commit()
252253 assert vulnerability.creator_command_id == command.id
253254 assert vulnerability.creator_command_tool == command.tool
255 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
67 from faraday.server.models import db, Workspace
78 from tests.factories import (
89 HostFactory,
118119 assert workspace.vulnerability_code_count is None
119120 assert workspace.vulnerability_standard_count is None
120121 assert workspace.vulnerability_total_count is None
122 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22
33 '''
66 See the file 'doc/LICENSE' for the license information
77
88 '''
9
9 from __future__ import absolute_import
1010 import unittest
1111 import sys
12 from Queue import Queue
12 from queue import Queue
1313 from collections import defaultdict
1414
1515 import os
5656 action = self.plugin._pending_actions.get(block=True)
5757 actions[action[0]].append(action[1])
5858
59 assert actions.keys() == [2000, 20008, 2038]
59 assert list(actions.keys()) == [2000, 20008, 2038]
6060 assert len(actions[2000]) == 1
6161 assert actions[2000][0].name == "5.175.17.140"
6262 assert len(actions[20008]) == 1
7171
7272 if __name__ == '__main__':
7373 unittest.main()
74 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22
33 '''
77
88 '''
99
10 from __future__ import absolute_import
1011 import unittest
1112 import sys
12 from Queue import Queue
13 from queue import Queue
1314 from collections import defaultdict
1415
1516 import os
5455 actions[action[0]].append(action[1])
5556
5657 assert actions[2000][0].name == "200.20.20.201"
57 assert actions.keys() == [2000, 20008, 2038]
58 assert list(actions.keys()) == [2000, 20008, 2038]
5859 assert len(actions[20008]) == 14
5960 assert len(actions[2038]) == 14
6061
6768
6869 if __name__ == '__main__':
6970 unittest.main()
71 # I'm Py3
66 def skip(self, n):
77 for x in range(n):
88 action = self.plugin._pending_actions.get(block=True)
9 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22
33 '''
66 See the file 'doc/LICENSE' for the license information
77
88 '''
9 from __future__ import absolute_import
910 import sys
1011 import unittest
11 from Queue import Queue
12 from queue import Queue
1213 from collections import defaultdict
1314
1415 import os
4748 actions[action[0]].append(action[1])
4849
4950 assert actions[2000][0].name == "12.233.108.201"
50 assert actions.keys() == [2000, 2017, 2038, 20008]
51 assert set(actions.keys()) == {2000, 2017, 2038, 20008}
5152 assert len(actions[20008]) == 1
5253 assert len(actions[2038]) == 1
5354
6162
6263 if __name__ == '__main__':
6364 unittest.main()
65 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22
33 '''
77
88 '''
99
10 from __future__ import absolute_import
1011 import unittest
1112 import sys
12 from Queue import Queue
13 from queue import Queue
1314 from collections import defaultdict
1415
1516 import os
5152 actions[action[0]].append(action[1])
5253
5354 assert actions[2000][0].name == "192.168.1.1"
54 assert actions.keys() == [2000, 2017, 2019, 2038, 20008]
55 assert set(actions.keys()) == {2000, 2017, 2019, 2038, 20008}
5556
5657 assert len(actions[2000]) == 8
5758 assert len(actions[20008]) == 20
6364
6465 if __name__ == '__main__':
6566 unittest.main()
67 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22
33 '''
66 See the file 'doc/LICENSE' for the license information
77
88 '''
9 from __future__ import absolute_import
10
911 import os
1012 import sys
11 from Queue import Queue
13 from queue import Queue
1214 from collections import defaultdict
1315
1416 sys.path.append(os.path.abspath(os.getcwd()))
7375 actions[action[0]].append(action[1])
7476
7577 assert actions[2000][0].name == "198.38.82.159"
76 assert actions.keys() == [2000, 20008]
78 assert list(actions.keys()) == [2000, 20008]
7779
7880 assert len(actions[2000]) == 1
7981 assert len(actions[20008]) == 13
8082
81 assert map(lambda service: service.name, actions[20008]) == [
83 assert list(map(lambda service: service.name, actions[20008])) == [
8284 'ftp',
8385 'smtp',
8486 'domain',
9496 'mysql'
9597 ]
9698
99 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22
33 '''
77
88 '''
99
10 from __future__ import absolute_import
1011 import unittest
1112 import sys
12 from Queue import Queue
13 from queue import Queue
1314 from collections import defaultdict
1415
1516 import os
5051 actions[action[0]].append(action[1])
5152
5253 assert actions[2000][0].name == "216.58.222.142"
53 assert actions.keys() == [2000]
54 assert list(actions.keys()) == [2000]
5455
5556 assert len(actions[2000]) == 1
5657
5859
5960 if __name__ == '__main__':
6061 unittest.main()
62 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22
33 '''
66 See the file 'doc/LICENSE' for the license information
77
88 '''
9 from Queue import Queue
9 from __future__ import absolute_import
10 from queue import Queue
1011 from collections import defaultdict
1112
1213 import os
5960 actions[action[0]].append(action[1])
6061
6162 assert actions[2000][0].name == "127.0.0.1"
62 assert actions.keys() == [2000, 20008]
63 assert list(actions.keys()) == [2000, 20008]
6364
6465 assert len(actions[2000]) == 1
6566 assert len(actions[20008]) == 1
6768
6869 if __name__ == '__main__':
6970 unittest.main()
71 # I'm Py3
0 #!/usr/bin/env python2.7
0 #!/usr/bin/env python3
11 # -*- coding: utf-8 -*-
22
33 '''
77
88 '''
99
10 from __future__ import absolute_import
1011 import unittest
1112 import sys
12 from Queue import Queue
13 from queue import Queue
1314 from collections import defaultdict
1415
1516 import os
5657 actions[action[0]].append(action[1])
5758
5859 assert actions[2000][0].name == "205.251.196.172"
59 assert actions.keys() == [2000]
60 assert list(actions.keys()) == [2000]
6061
6162 assert len(actions[2000]) == 8
63 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7 from __future__ import division
8
69 import os
710 import pytest
811 from io import BytesIO
5659 )
5760
5861 # Changing res.json['itime'] to timestamp format of itime
59 res_itime = res.json['itime'] / 1000
62 res_itime = res.json['itime'] / 1000.0
6063 assert res.status_code == 200
6164 assert res_itime == itime
65
66
67 # I'm Py3
22 Copyright (C) 2019 Infobyte LLC (http://www.infobytesec.com/)
33 See the file 'doc/LICENSE' for the license information
44 """
5 import mock
5 from __future__ import absolute_import
6
7 from unittest import mock
68 import pytest
79
810 from faraday.server.api.modules.agent import AgentView
173175 res = test_client.delete(self.url(agent.id))
174176 assert res.status_code == 204
175177 assert len(session.query(Agent).all()) == initial_agent_count
178
179 def test_run_agent_invalid_missing_executorData(self, csrf_token, session, test_client):
180 agent = AgentFactory.create(workspace=self.workspace)
181 session.add(agent)
182 session.commit()
183 payload = {
184 'csrf_token': csrf_token
185 }
186 res = test_client.post(self.url() + f'{agent.id}/run/', json=payload)
187 assert res.status_code == 400
188
189 def test_invalid_body(self, test_client, session):
190 agent = AgentFactory.create(workspace=self.workspace)
191 session.add(agent)
192 session.commit()
193 res = test_client.post(self.url() + f'{agent.id}/run/', data='[" broken]"{')
194 assert res.status_code == 400
195
196 def test_invalid_content_type(self, test_client, session, csrf_token):
197 agent = AgentFactory.create(workspace=self.workspace)
198 session.add(agent)
199 session.commit()
200 payload = {
201 'csrf_token': csrf_token,
202 'executorData': {
203 "args": {
204 "param1": True
205 },
206 "executor": "executor_name"
207 },
208 }
209 headers = [
210 ('content-type', 'text/html'),
211 ]
212 res = test_client.post(
213 self.url() + f'{agent.id}/run/',
214 data=payload,
215 headers=headers)
216 assert res.status_code == 400
217
218 def test_happy_path_valid_json(self, test_client, session, csrf_token):
219 agent = AgentFactory.create(workspace=self.workspace)
220 session.add(agent)
221 session.commit()
222 payload = {
223 'csrf_token': csrf_token,
224 'executorData': {
225 "args": {
226 "param1": True
227 },
228 "executor": "executor_name"
229 },
230 }
231 res = test_client.post(self.url() + f'{agent.id}/run/', json=payload)
232 assert res.status_code == 200
233
234 def test_invalid_json_on_executorData_breaks_the_api(self, csrf_token, session, test_client):
235 agent = AgentFactory.create(workspace=self.workspace)
236 session.add(agent)
237 session.commit()
238 payload = {
239 'csrf_token': csrf_token,
240 'executorData': '[][dassa',
241 }
242 res = test_client.post(self.url() + f'{agent.id}/run/', json=payload)
243 assert res.status_code == 400
244
245 def test_run_agent(self, session, csrf_token, test_client):
246 agent = AgentFactory.create(workspace=self.workspace)
247 session.add(agent)
248 session.commit()
249 payload = {
250 'csrf_token': csrf_token,
251 'executorData': '',
252 }
253 res = test_client.post(self.url() + f'{agent.id}/run/', json=payload)
254 assert res.status_code == 400
0 from __future__ import absolute_import
1 from builtins import bytes
2
03 import pytest
14 from marshmallow import ValidationError
25 from faraday.server.models import (
314317 command = workspace.commands[0]
315318 assert command.tool == 'pytest'
316319 assert command.user == 'root'
317 assert (command.end_date - command.start_date).seconds == 30
320 assert (command.end_date - command.start_date).microseconds == 30
318321
319322
320323 def test_creates_command_object(session, workspace):
493496
494497
495498 @pytest.mark.usefixtures('logged_user')
499 def test_bulk_create_endpoint_run_over_closed_vuln(session, workspace, test_client):
500 assert count(Host, workspace) == 0
501 assert count(VulnerabilityGeneric, workspace) == 0
502 url = f'v2/ws/{workspace.name}/bulk_create/'
503 host_data_ = host_data.copy()
504 host_data_['vulnerabilities'] = [vuln_data]
505 res = test_client.post(url, data=dict(hosts=[host_data_]))
506 assert res.status_code == 201, res.json
507 assert count(Host, workspace) == 1
508 assert count(Vulnerability, workspace) == 1
509 host = Host.query.filter(Host.workspace == workspace).one()
510 vuln = Vulnerability.query.filter(Vulnerability.workspace == workspace).one()
511 assert host.ip == "127.0.0.1"
512 assert set({hn.name for hn in host.hostnames}) == {"test.com", "test2.org"}
513 assert vuln.status == "open"
514 close_url = f"v2/ws/{workspace.name}/vulns/{vuln.id}/"
515 res = test_client.get(close_url)
516 vuln_data_del = res.json
517 vuln_data_del["status"] = "closed"
518 res = test_client.put(close_url, data=dict(vuln_data_del))
519 assert res.status_code == 200, res.json
520 assert count(Host, workspace) == 1
521 assert count(Vulnerability, workspace) == 1
522 assert vuln.status == "closed"
523 res = test_client.post(url, data=dict(hosts=[host_data_]))
524 assert res.status_code == 201, res.json
525 assert count(Host, workspace) == 1
526 assert count(Vulnerability, workspace) == 1
527 vuln = Vulnerability.query.filter(Vulnerability.workspace == workspace).one()
528 assert vuln.status == "re-opened"
529
530
531 @pytest.mark.usefixtures('logged_user')
496532 def test_bulk_create_endpoint_without_host_ip(session, workspace, test_client):
497533 url = 'v2/ws/{}/bulk_create/'.format(workspace.name)
498534 host_data_ = host_data.copy()
540576 headers=[("authorization", "agent {}".format(agent.token))]
541577 )
542578 assert res.status_code == 404
543 assert 'No such workspace' in res.data
579 assert b'No such workspace' in res.data
544580 assert count(Host, second_workspace) == 0
545581
546582
586622 headers=[("authorization", "agent {}".format(agent.token))]
587623 )
588624 assert res.status_code == 403
625
626 @pytest.mark.usefixtures('logged_user')
627 def test_bulk_create_endpoint_raises_400_with_no_data(
628 session, test_client, workspace):
629 url = 'v2/ws/{}/bulk_create/'.format(workspace.name)
630 res = test_client.post(
631 url,
632 data="",
633 use_json_data=False,
634 headers=[("Content-Type", "application/json")]
635 )
636 assert res.status_code == 400
637
638
639 # I'm Py3
44 See the file 'doc/LICENSE' for the license information
55
66 '''
7 from __future__ import absolute_import
8
79 """Tests for many API endpoints that do not depend on workspace_name"""
810 import datetime
911 import pytest
1012 import time
1113
1214 from tests import factories
13 from test_api_workspaced_base import API_PREFIX, ReadOnlyAPITests
15 from tests.test_api_workspaced_base import API_PREFIX, ReadOnlyAPITests
1416 from faraday.server.models import (
1517 Command,
1618 Workspace,
2830 # and https://github.com/pytest-dev/pytest/issues/568 for more information
2931
3032 @pytest.mark.usefixtures('logged_user')
31 class TestListCommandView(object, ReadOnlyAPITests):
33 class TestListCommandView(ReadOnlyAPITests):
3234 model = Command
3335 factory = factories.CommandFactory
3436 api_endpoint = 'commands'
9294 res = test_client.get(self.url(workspace=command.workspace) + 'activity_feed/')
9395 assert res.status_code == 200
9496
95 assert filter(lambda stats: stats['_id'] == command.id, res.json) == [
97 assert list(filter(lambda stats: stats['_id'] == command.id, res.json)) == [
9698 {u'_id': command.id,
9799 u'command': command.command,
98100 u'import_source': u'shell',
105107 u'vulnerabilities_count': 1,
106108 u'criticalIssue': 0}]
107109
108 assert filter(lambda stats: stats['_id'] == another_command.id,
109 res.json) == [{
110 assert list(filter(lambda stats: stats['_id'] == another_command.id,
111 res.json)) == [{
110112 u'_id': another_command.id,
111113 u'command': another_command.command,
112114 u'import_source': u'shell',
263265 session.commit()
264266 res = test_client.get(self.url(workspace=command.workspace) + 'activity_feed/')
265267 assert res.status_code == 200
266 raw_first_command = filter(lambda comm: comm['_id'] == commands[0].id, res.json)
268 raw_first_command = list(filter(lambda comm: comm['_id'] == commands[0].id, res.json))
267269
268270 assert raw_first_command.pop() == {
269271 u'_id': first_command.id,
280282 }
281283
282284 for in_the_middle_command in in_the_middle_commands:
283 raw_in_the_middle_command = filter(lambda comm: comm['_id'] == in_the_middle_command.id, res.json)
285 raw_in_the_middle_command = list(filter(lambda comm: comm['_id'] == in_the_middle_command.id, res.json))
284286 assert raw_in_the_middle_command.pop() == {u'_id': in_the_middle_command.id,
285287 u'command': in_the_middle_command.command,
286288 u'import_source': u'shell',
294296 u'criticalIssue': 0}
295297
296298 # new command must create new service and vuln
297 raw_last_command = filter(lambda comm: comm['_id'] == last_command.id, res.json)
299 raw_last_command = list(filter(lambda comm: comm['_id'] == last_command.id, res.json))
298300 assert raw_last_command.pop() == {u'_id': last_command.id,
299301 u'command': last_command.command,
300302 u'import_source': u'shell',
408410
409411 res = test_client.get(self.url(workspace=command.workspace) + 'activity_feed/')
410412 assert res.status_code == 200
411 command_history = filter(lambda hist: hist['_id'] == command.id, res.json)
413 command_history = list(filter(lambda hist: hist['_id'] == command.id, res.json))
412414 assert len(command_history)
413415 command_history = command_history[0]
414416 assert command_history['hosts_count'] == 1
415417 assert command_history['tool'] == 'test'
418
419 def test_year_is_out_range(self, test_client):
420 raw_data ={
421 'command': 'Import Nessus:',
422 'tool': 'nessus',
423 'duration': None,
424 'hostname': 'mandarina',
425 'ip': '192.168.20.53',
426 'itime': 1511387720000.048548,
427 'params': u'/home/lcubo/.faraday/report/airbnb/nessus_report_Remote.nessus',
428 'user': 'lcubo'
429 }
430
431 res = test_client.post(self.url(), data=raw_data)
432
433 assert res.status_code == 400
434
435 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 from faraday.server.api.modules.comments import CommentView
79 from faraday.server.models import Comment
810 from tests.factories import ServiceFactory
110112 assert res.status_code == 409
111113 assert 'object' in res.json
112114 assert type(res.json) == dict
115
116
117 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import pytest
79
810 from tests import factories
9 from test_api_workspaced_base import (
11 from tests.test_api_workspaced_base import (
1012 ReadWriteAPITests,
1113 )
1214 from faraday.server.api.modules.credentials import CredentialView
103105 session.commit()
104106 res = test_client.get(self.url(workspace=credential.workspace) + '?host_id={0}'.format(credential.host.id))
105107 assert res.status_code == 200
106 assert map(lambda cred: cred['value']['parent'],res.json['rows']) == [credential.host.id]
107 assert map(lambda cred: cred['value']['parent_type'], res.json['rows']) == [u'Host']
108 assert [cred['value']['parent'] for cred in res.json['rows']] == [credential.host.id]
109 assert [cred['value']['parent_type'] for cred in res.json['rows']] == [u'Host']
108110
109111 def test_get_credentials_for_a_service_backwards_compatibility(self, session, test_client):
110112 service = ServiceFactory.create()
112114 session.commit()
113115 res = test_client.get(self.url(workspace=credential.workspace) + '?service={0}'.format(credential.service.id))
114116 assert res.status_code == 200
115 assert map(lambda cred: cred['value']['parent'],res.json['rows']) == [credential.service.id]
116 assert map(lambda cred: cred['value']['parent_type'], res.json['rows']) == [u'Service']
117 assert [cred['value']['parent'] for cred in res.json['rows']] == [credential.service.id]
118 assert [cred['value']['parent_type'] for cred in res.json['rows']] == [u'Service']
117119
118120 def _generate_raw_update_data(self, name, username, password, parent_id):
119121 return {
199201 }
200202 res = test_client.post(self.url(), data=data)
201203 assert res.status_code == 400
202 assert 'Parent id not found' in res.data
204 assert b'Parent id not found' in res.data
203205
204206 @pytest.mark.parametrize("parent_type, parent_factory", [
205207 ("Host", HostFactory),
230232 }
231233 res = test_client.put(self.url(credential), data=data)
232234 assert res.status_code == 400
233 assert 'Parent id not found' in res.data
235 assert b'Parent id not found' in res.data
236
237
238 # I'm Py3
0 from __future__ import absolute_import
1
02 import pytest
13
24 from tests.factories import CustomFieldsSchemaFactory
2931
3032 res = test_client.get(self.url()) # '/v2/custom_fields_schema/')
3133 assert res.status_code == 200
32 assert {u'table_name': u'vulnerability', u'id': add_text_field.id, u'field_type': u'text', u'field_name': u'cvss', u'field_display_name': u'CVSS', u'field_order': 1} in res.json
34 assert {u'table_name': u'vulnerability', u'id': add_text_field.id, u'field_type': u'text', u'field_name': u'cvss', u'field_display_name': u'CVSS', u'field_metadata': None, u'field_order': 1} in res.json
3335
3436 def test_custom_fields_field_name_cant_be_changed(self, session, test_client):
3537 add_text_field = CustomFieldsSchemaFactory.create(
5759 assert custom_field_obj.table_name == 'vulnerability'
5860 assert custom_field_obj.field_type == 'str'
5961 assert custom_field_obj.field_display_name == 'CVSS new'
62
63 def test_add_custom_fields_with_metadata(self, session, test_client):
64 add_choice_field = CustomFieldsSchemaFactory.create(
65 table_name='vulnerability',
66 field_name='gender',
67 field_type='choice',
68 field_metadata=['Male', 'Female'],
69 field_order=1,
70 field_display_name='Gender',
71 )
72
73 session.add(add_choice_field)
74 session.commit()
75
76 res = test_client.get(self.url()) # '/v2/custom_fields_schema/')
77 assert res.status_code == 200
78 assert {u'table_name': u'vulnerability', u'id': add_choice_field.id, u'field_type': u'choice',
79 u'field_name': u'gender', u'field_display_name': u'Gender', u'field_metadata': "['Male', 'Female']",
80 u'field_order': 1} in res.json
81
82 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
67
78 import pytest
89
2627 assert res.status_code == 200
2728 assert res.json.get('metasploit') != []
2829 assert res.json.get('exploitdb') != []
30
31
32 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
67 import time
78 import operator
89 from io import BytesIO
2122 import pytest
2223
2324 from tests import factories
24 from test_api_workspaced_base import (
25 from tests.test_api_workspaced_base import (
2526 API_PREFIX,
2627 ReadWriteAPITests,
2728 PaginationTestsMixin,
389390
390391 res = test_client.get(self.url())
391392 assert res.status_code == 200
392 json_host = filter(lambda json_host: json_host['value']['id'] == host.id, res.json['rows'])[0]
393 json_host = list(filter(lambda json_host: json_host['value']['id'] == host.id, res.json['rows']))[0]
393394 # the host has one vuln associated. another one via service.
394395 assert json_host['value']['vulns'] == 2
395396
519520 session.add(ws)
520521 session.commit()
521522 expected_created_hosts = 2
522 file_contents = """ip, description, os, hostnames\n
523 10.10.10.10, test_host, linux, \"['localhost', 'test_host']\"\n
524 10.10.10.11, test_host, linux, \"['localhost', 'test_host_1']\"
525 """
523 file_contents = b"""ip,description,os,hostnames\n
524 10.10.10.10,test_host,linux,\"['localhost','test_host']\"\n
525 10.10.10.11,test_host,linux,\"['localhost','test_host_1']"
526 """
526527 data = {
527528 'file': (BytesIO(file_contents), 'hosts.csv'),
528529 'csrf_token': csrf_token
532533 data=data, headers=headers, use_json_data=False)
533534 assert res.status_code == 200
534535 assert res.json['hosts_created'] == expected_created_hosts
536 assert res.json['hosts_with_errors'] == 0
535537 assert session.query(Host).filter_by(description="test_host").count() == expected_created_hosts
536538
537539
771773 assert res.status_code in [201, 400, 409]
772774
773775 send_api_request()
776
777
778 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import os
79 import pytest
810
2931 res = test_client.get('/config')
3032 assert res.status_code == 200
3133 assert res.json['lic_db'] == 'faraday_licenses'
34
35
36 # I'm Py3
44 See the file 'doc/LICENSE' for the license information
55
66 '''
7 from __future__ import absolute_import
78 """Tests for many API endpoints that do not depend on workspace_name"""
89
910 import pytest
1112 from hypothesis import given, strategies as st
1213
1314 from tests import factories
14 from test_api_non_workspaced_base import ReadWriteAPITests, API_PREFIX
15 from tests.test_api_non_workspaced_base import ReadWriteAPITests, API_PREFIX
1516 from faraday.server.models import (
1617 License,
1718 )
8687 assert res.status_code in [201, 400, 409]
8788
8889 send_api_request()
90
91
92 # I'm Py3
0 from __future__ import absolute_import
1
02 import pytest
13 from itsdangerous import TimedJSONWebSignatureSerializer
24
5355 res = test_client.post('/login', data=login_payload)
5456 assert res.status_code == 200
5557 assert 'authentication_token' in res.json['response']['user']
56
58
5759 headers = {'Authentication-Token': res.json['response']['user']['authentication_token']}
5860
5961 ws = test_client.get('/v2/ws/wonderland/', headers=headers)
8183 serializer = TimedJSONWebSignatureSerializer(app.config['SECRET_KEY'], expires_in=500, salt="token")
8284 token = serializer.dumps({ 'user_id': alice.id})
8385
84 headers = {'Authorization': 'Token ' + token}
86 headers = {'Authorization': b'Token ' + token}
8587
8688 ws = test_client.get('/v2/ws/wonderland/', headers=headers)
8789 assert ws.status_code == 401
124126 test_client.cookie_jar.clear()
125127 res = test_client.get('/v2/ws/', headers=headers)
126128 assert res.status_code == 401
129
130 def test_null_caracters(self, test_client, session):
131 """
132 Use of a valid auth token
133 """
134
135 alice = factories.UserFactory.create(
136 active=True,
137 username='asdasd',
138 password='asdasd',
139 role='pentester')
140 session.add(alice)
141 session.commit()
142
143 ws = factories.WorkspaceFactory.create(name='wonderland')
144 session.add(ws)
145 session.commit()
146
147 login_payload = {
148 'email': "\x00asd\00asd\0",
149 'password': "\x00asd\00asd\0",
150 }
151 res = test_client.post('/login', data=login_payload)
152 # import ipdb; ipdb.set_trace()
153 assert res.status_code == 200
154 assert 'authentication_token' in res.json['response']['user']
155
156 headers = {'Authentication-Token': res.json['response']['user']['authentication_token']}
157
158 ws = test_client.get('/v2/ws/wonderland/', headers=headers)
159 assert ws.status_code == 200
44 See the file 'doc/LICENSE' for the license information
55
66 '''
7 from __future__ import absolute_import
8 from builtins import str
79
810 """Generic tests for APIs NOT prefixed with a workspace_name"""
911
1517
1618
1719 @pytest.mark.usefixtures('logged_user')
18 class GenericAPITest(object):
20 class GenericAPITest:
1921
2022 model = None
2123 factory = None
4244 def url(self, obj=None):
4345 url = API_PREFIX + self.api_endpoint + '/'
4446 if obj is not None:
45 id_ = unicode(getattr(obj, self.lookup_field)) if isinstance(
46 obj, self.model) else unicode(obj)
47 id_ = str(getattr(obj, self.lookup_field)) if isinstance(
48 obj, self.model) else str(obj)
4749 url += id_ + u'/'
4850 return url
4951
157159 RetrieveTestsMixin,
158160 GenericAPITest):
159161 pass
162
163
164 # I'm Py3
44 See the file 'doc/LICENSE' for the license information
55
66 '''
7 from __future__ import absolute_import
78
89 """Generic test mixins for APIs with pagination enabled when listing"""
910
1011 import pytest
11 from urllib import urlencode
12
13 try:
14 from urllib import urlencode
15 except ImportError as e:
16 from urllib.parse import urlencode
1217
1318 def with_0_and_n_objects(n=10):
1419 return pytest.mark.parametrize('object_count', [0, n])
103108 res = test_client.get(self.page_url(1, 5))
104109 assert res.status_code == 200
105110 assert len(res.json['data']) == 0
111
112
113 # I'm Py3
0 #-*- coding: utf8 -*-
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 from random import randrange
8
9 import pytest
10
11 from tests.factories import SearchFilterFactory, UserFactory, SubFactory
12 from tests.test_api_non_workspaced_base import ReadOnlyAPITests, OBJECT_COUNT
13 from tests.test_api_agent import logout, http_req
14 from tests.conftest import login_as
15 from faraday.server.models import SearchFilter
16
17
18 from faraday.server.api.modules.search_filter import SearchFilterView
19
20
21 @pytest.mark.usefixtures('logged_user')
22 class TestSearchFilterAPI(ReadOnlyAPITests):
23 model = SearchFilter
24 factory = SearchFilterFactory
25 api_endpoint = 'searchfilter'
26 view_class = SearchFilterView
27
28 pytest.fixture(autouse=True)
29
30 def test_list_retrieves_all_items_from(self, test_client):
31 return
32
33 def test_list_retrieves_all_items_from_logger_user(self, test_client, session, logged_user):
34 user_filter = SearchFilterFactory.create(creator=logged_user)
35 session.add(user_filter)
36 session.commit()
37 res = test_client.get(self.url())
38 assert res.status_code == 200
39 if 'rows' in res.json:
40 assert len(res.json['rows']) == 1
41 else:
42 assert len(res.json) == 1
43
44 def test_retrieve_one_object(self):
45 return
46
47 def test_retrieve_one_object_from_logged_user(self, test_client, session, logged_user):
48
49 filters = []
50 for n in range(5):
51 user_filter = SearchFilterFactory.create(creator=logged_user)
52 session.add(user_filter)
53 filters.append(user_filter)
54
55 session.commit()
56
57 print(self.url(filters[randrange(5)]))
58 res = test_client.get(self.url(filters[randrange(5)]))
59 assert res.status_code == 200
60 assert isinstance(res.json, dict)
61
62 def test_retrieve_filter_from_another_user(self, test_client, session, logged_user):
63 user_filter = SearchFilterFactory.create(creator=logged_user)
64 another_user = UserFactory.create()
65 session.add(user_filter)
66 session.add(another_user)
67 session.commit()
68
69 logout(test_client, [302])
70 login_as(test_client, another_user)
71
72 res = test_client.get(self.url(user_filter))
73 assert res.status_code == 404
74
75 def test_retrieve_filter_list_is_empty_from_another_user(self, test_client, session, logged_user):
76 user_filter = SearchFilterFactory.create(creator=logged_user)
77 another_user = UserFactory.create()
78 session.add(user_filter)
79 session.add(another_user)
80 session.commit()
81
82 logout(test_client, [302])
83 login_as(test_client, another_user)
84
85 res = test_client.get(self.url())
86 assert res.status_code == 200
87 assert res.json == []
88
89 def test_delete_filter_from_another_user(self, test_client, session, logged_user):
90 user_filter = SearchFilterFactory.create(creator=logged_user)
91 another_user = UserFactory.create()
92 session.add(user_filter)
93 session.add(another_user)
94 session.commit()
95
96 logout(test_client, [302])
97 login_as(test_client, another_user)
98
99 res = test_client.delete(self.url(user_filter))
100 assert res.status_code == 404
1515
1616 from faraday.server.api.modules.services import ServiceView
1717 from tests import factories
18 from test_api_workspaced_base import ReadOnlyAPITests
18 from tests.test_api_workspaced_base import ReadOnlyAPITests
1919 from faraday.server.models import (
2020 Service
2121 )
8787 }
8888 res = test_client.post(self.url(), data=data)
8989 assert res.status_code == 400
90 assert 'Not a valid choice' in res.data
90 assert b'Not a valid choice' in res.data
9191
9292 def test_create_fails_with_no_host_id(self, test_client,
9393 host, session):
102102 }
103103 res = test_client.post(self.url(), data=data)
104104 assert res.status_code == 400
105 assert 'Parent id is required' in res.data
105 assert b'Parent id is required' in res.data
106106
107107 def test_create_fails_with_host_of_other_workspace(self, test_client,
108108 host, session,
120120 }
121121 res = test_client.post(self.url(workspace=second_workspace), data=data)
122122 assert res.status_code == 400
123 assert 'Host with id' in res.data
123 assert b'Host with id' in res.data
124124
125125 def test_update_fails_with_host_of_other_workspace(self, test_client,
126126 second_workspace,
140140 }
141141 res = test_client.put(self.url(self.first_object), data=data)
142142 assert res.status_code == 400
143 assert 'Can\'t change service parent.' in res.data
143 assert b'Can\'t change service parent.' in res.data
144144
145145 def test_create_service_returns_conflict_if_already_exists(self, test_client, host, session):
146146 session.commit()
198198 raw_data = self._raw_put_data(service.id, parent=host.id)
199199 res = test_client.put(self.url(service, workspace=service.workspace), data=raw_data)
200200 assert res.status_code == 400
201 assert 'Can\'t change service parent.' in res.data
201 assert b'Can\'t change service parent.' in res.data
202202 updated_service = Service.query.filter_by(id=service.id).first()
203203 assert updated_service.name == service.name
204204
291291 res = test_client.post(self.url(), data=data)
292292 assert res.status_code == 400
293293
294 def test_load_ports_with_negative_value(self, test_client):
294 def test_load_ports_with_negative_value(self, test_client, session):
295 host = HostFactory.create(workspace=self.workspace)
296 session.commit()
295297 data = {
296298 "name": "ports",
297299 "description": "testing ports load",
299301 "ports": [-1],
300302 "protocol": "tcp",
301303 "status": "open",
302 }
303 res = test_client.post(self.url(), data=data)
304 assert res.status_code == 400
304 "parent": host.id
305 }
306 res = test_client.post(self.url(), data=data)
307 assert res.status_code == 400
308
309 def test_load_invalid_port(self, test_client, session):
310 host = HostFactory.create(workspace=self.workspace)
311 session.commit()
312 data = {
313 "name": "ports",
314 "description": "testing ports load",
315 "owned": False,
316 "ports": [65536],
317 "protocol": "tcp",
318 "status": "open",
319 "parent": host.id
320 }
321 res = test_client.post(self.url(), data=data)
322 print(res.data)
323 assert res.status_code == 400
324
325
326 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
67
78 import pytest
89
1617 def test_session_when_user_is_not_logged(self, test_client):
1718 res = test_client.get('/session')
1819 assert res.status_code == 401
20
21
22 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
67
78 import os
89 import pytest
910 from io import BytesIO
10 from mock import Mock, patch
1111
1212 from tests.factories import WorkspaceFactory
13 from faraday.config.configuration import getInstanceConfiguration
1413
1514 @pytest.mark.usefixtures('logged_user')
1615 class TestFileUpload():
2423 'data',
2524 'nmap_plugin_with_api.xml')
2625
27 with open(path,'r') as report:
26 with open(path,'rb') as report:
2827 file_contents = report.read()
2928 data = {
3029 'file' : (BytesIO(file_contents), 'nmap_report.xml'),
6059 'nmap_plugin_with_api.xml')
6160
6261 with open(path,'r') as report:
63 file_contents = report.read()
62 file_contents = report.read().encode('utf-8')
6463
6564 data = {
6665 'file' : (BytesIO(file_contents), 'nmap_report.xml'),
8584 'nmap_plugin_with_api.xml')
8685
8786 with open(path,'r') as report:
88 file_contents = report.read()
87 file_contents = report.read().encode('utf-8')
8988
9089 data = {
9190 'file' : (BytesIO(file_contents), 'nmap_report.xml'),
9796 use_json_data=False)
9897
9998 assert res.status_code == 404
99
100
101 # I'm Py3
44 See the file 'doc/LICENSE' for the license information
55
66 '''
7 from __future__ import absolute_import
8 from builtins import str
9
710 import json
811 from tempfile import NamedTemporaryFile
912
1013 import os
1114 import csv
1215 from base64 import b64encode
13 from io import BytesIO
16 from io import BytesIO, StringIO
1417
1518
1619 import pytz
3437 from faraday.server.schemas import NullToBlankString
3538 from tests import factories
3639 from tests.conftest import TEST_DATA
37 from test_api_workspaced_base import (
40 from tests.test_api_workspaced_base import (
3841 ReadOnlyAPITests
3942 )
4043 from faraday.server.models import (
129132 if attachments:
130133 data['_attachments'] = {}
131134 for attachment in attachments:
135 attachment_data = attachment.read()
136 if isinstance(attachment_data, str):
137 attachment_data = attachment_data.encode('utf-8')
132138 data['_attachments'][attachment.name] = {
133139 "content_type": "application/x-shellscript",
134 "data": b64encode(attachment.read())
140 "data": b64encode(attachment_data).decode('utf-8')
135141 }
136142
137143 if service_id:
313319 )
314320 res = test_client.post(self.url(), data=raw_data)
315321 assert res.status_code == 400
316 assert 'Shorter than minimum length 1' in res.data
322 assert b'Shorter than minimum length 1' in res.data
317323
318324 def test_create_create_vuln_with_empty_desc_success(
319325 self, host, session, test_client):
335341
336342 def test_create_vuln_with_attachments(self, host_with_hostnames, test_client, session):
337343 session.commit() # flush host_with_hostnames
338 attachment = NamedTemporaryFile()
339 file_content = 'test file'
344 attachment = NamedTemporaryFile(mode="wb+")
345 file_content = b'test file'
340346 attachment.write(file_content)
341347 attachment.seek(0)
342348 raw_data = _create_post_data_vulnerability(
374380 session.add(vuln)
375381 session.commit() # flush host_with_hostnames
376382 attachment = NamedTemporaryFile()
377 file_content = 'test file'
383 file_content = b'test file'
378384 attachment.write(file_content)
379385 attachment.seek(0)
380386 raw_data = self._create_put_data(
395401
396402 new_attachment = NamedTemporaryFile()
397403 new_filename = new_attachment.name.split('/')[-1]
398 file_content = 'new test file'
404 file_content = b'new test file'
399405 new_attachment.write(file_content)
400406 new_attachment.seek(0)
401407 raw_data = self._create_put_data(
424430 session.add(vuln)
425431 session.commit()
426432
427 with open(os.path.join(TEST_DATA,'faraday.png'), 'r') as file_obj:
433 with open(os.path.join(TEST_DATA,'faraday.png'), 'rb') as file_obj:
428434 new_file = FaradayUploadedFile(file_obj.read())
429435
430436 new_attach = File(object_type='vulnerability', object_id=vuln.id, name='Faraday', filename='faraday.png',
471477 vuln_count_previous = session.query(Vulnerability).count()
472478 res = test_client.post('/v2/ws/{0}/vulns/'.format(ws_name), data=raw_data)
473479 assert res.status_code == 201
474 for prop, value in vuln_props.iteritems():
480 for prop, value in vuln_props.items():
475481 if prop not in vuln_props_excluded:
476482 assert res.json[prop] == value, prop
477483
680686 def test_filter_by_invalid_severity_fails(self, test_client):
681687 res = test_client.get(self.url() + '?severity=131231')
682688 assert res.status_code == 400
683 assert 'Invalid severity type' in res.data
689 assert b'Invalid severity type' in res.data
684690
685691 @pytest.mark.usefixtures('mock_envelope_list')
686692 def test_filter_by_invalid_severity(self, test_client):
808814 session):
809815 session.commit() # flush host_with_hostnames
810816 attachments = [
811 open(os.path.join(CURRENT_PATH, 'data', 'faraday.png'), 'r'),
812 open(os.path.join(CURRENT_PATH, 'data', 'test.html'), 'r')
817 open(os.path.join(CURRENT_PATH, 'data', 'faraday.png'), 'rb'),
818 open(os.path.join(CURRENT_PATH, 'data', 'test.html'), 'rb')
813819 ]
814820 raw_data = _create_post_data_vulnerability(
815821 name='New vulns',
827833 assert res.status_code == 201
828834 assert len(res.json['_attachments']) == 2
829835 assert vuln_count_previous + 1 == session.query(Vulnerability).count()
830 map(lambda fileobj: fileobj.close(), attachments)
836 [fileobj.close() for fileobj in attachments]
831837
832838 def test_create_vuln_with_refs(self, host_with_hostnames, test_client, session):
833839 session.commit() # flush host_with_hostnames
980986 res = test_client.post('/v2/ws/{0}/vulns/'.format(ws_name), data=raw_data)
981987 assert res.status_code == 400
982988 assert vuln_count_previous == session.query(Vulnerability).count()
983 assert 'Invalid severity type.' in res.data
989 assert b'Invalid severity type.' in res.data
984990
985991 def test_create_vuln_with_invalid_ease_of_resolution(self,
986992 host_with_hostnames,
10011007 res = test_client.post('/v2/ws/{0}/vulns/'.format(ws_name), data=raw_data)
10021008 assert res.status_code == 400
10031009 assert vuln_count_previous == session.query(Vulnerability).count()
1004 assert res.json['messages'].keys() == ['easeofresolution']
1010 assert list(res.json['messages'].keys()) == ['easeofresolution']
10051011 assert res.json['messages']['easeofresolution'][0] == u'Not a valid choice.'
10061012
10071013 def test_create_vuln_with_null_ease_of_resolution(self,
10421048 'count/?confirmed=1&group_by=severity')
10431049 assert res.status_code == 200
10441050 assert res.json['total_count'] == 3
1045 assert sorted(res.json['groups']) == sorted([
1051 assert sorted(res.json['groups'], key=lambda i: (i['count'],i['name'],i['severity'])) == sorted([
10461052 {"name": "high", "severity": "high", "count": 2},
10471053 {"name": "critical", "severity": "critical", "count": 1},
1048 ])
1054 ], key=lambda i: (i['count'],i['name'],i['severity']))
10491055
10501056 def test_count_severity_map(self, test_client, second_workspace, session):
10511057 vulns = self.factory.create_batch(4, severity='informational',
10611067 'count/?group_by=severity')
10621068 assert res.status_code == 200
10631069 assert res.json['total_count'] == 9
1064 assert sorted(res.json['groups']) == sorted([
1070 assert sorted(res.json['groups'], key=lambda i: (i['count'],i['name'],i['severity'])) == sorted([
10651071 {"name": "med", "severity": "med", "count": 3},
10661072 {"name": "low", "severity": "low", "count": 2},
10671073 {"name": "info", "severity": "info", "count": 4},
1068 ])
1074 ], key=lambda i: (i['count'],i['name'],i['severity']))
10691075
10701076 @pytest.mark.usefixtures('mock_envelope_list')
10711077 def test_target(self, test_client, session, second_workspace,
12281234
12291235 res = test_client.get(self.url())
12301236 assert res.status_code == 200
1231 from_json_vuln = filter(lambda raw_vuln: raw_vuln['id'] == vuln.id,
1232 res.json['vulnerabilities'])
1237 from_json_vuln = list(filter(lambda raw_vuln: raw_vuln['id'] == vuln.id,
1238 res.json['vulnerabilities']))
12331239 assert 'metadata' in from_json_vuln[0]['value']
12341240 expected_metadata = {
12351241 u'command_id': command.id,
12661272 )
12671273 res = test_client.post(self.url(), data=data)
12681274 assert res.status_code == 400
1269 assert 'Parent id not found' in res.data
1275 assert b'Parent id not found' in res.data
12701276
12711277 @pytest.mark.parametrize("parent_type, parent_factory", [
12721278 ("Host", HostFactory),
12901296 )
12911297 res = test_client.put(self.url(self.first_object), data=data)
12921298 assert res.status_code == 400
1293 assert 'Parent id not found' in res.data
1299 assert b'Parent id not found' in res.data
12941300
12951301 def test_create_vuln_multiple_times_returns_conflict(self, host_with_hostnames, test_client, session):
12961302 """
16541660 vuln = VulnerabilityFactory.create(workspace=ws)
16551661 session.add(vuln)
16561662 session.commit()
1657 file_contents = 'my file contents'
1663 file_contents = b'my file contents'
16581664 data = {
16591665 'file' : (BytesIO(file_contents), 'borrar.txt')
16601666 }
16841690 vuln = VulnerabilityFactory.create(workspace=ws)
16851691 session.add(vuln)
16861692 session.commit()
1687 file_contents = 'my file contents'
1693 file_contents = b'my file contents'
16881694 data = {
16891695 'file' : (BytesIO(file_contents), 'borrar.txt')
16901696 }
17041710 session.commit() # flush host_with_hostnames
17051711 ws_name = host_with_hostnames.workspace.name
17061712 attachment = NamedTemporaryFile()
1707 file_content = 'test file'
1713 file_content = b'test file'
17081714 attachment.write(file_content)
17091715 attachment.seek(0)
17101716 vuln = _create_post_data_vulnerability(
17351741 session.commit() # flush host_with_hostnames
17361742 ws_name = host_with_hostnames.workspace.name
17371743 attachment = NamedTemporaryFile()
1738 file_content = 'test file'
1744 file_content = b'test file'
17391745 attachment.write(file_content)
17401746 attachment.seek(0)
17411747 vuln = _create_post_data_vulnerability(
18371843 )
18381844 with pytest.raises(Exception) as err:
18391845 res = test_client.post(self.url(), data=raw_data)
1840 assert err.typename == 'AssertionError'
1846 assert err.typename in ['AssertionError', 'ValueError']
18411847
18421848 def test_add_vuln_with_unknown_parent_type(self, test_client, session, host_with_hostnames):
18431849 session.commit()
18971903 "impact_accountability"
18981904 ]
18991905 assert res.status_code == 200
1900 assert res.data.strip('\r\n').split(',') == expected_headers
1906 assert res.data.decode('utf-8').strip('\r\n').split(',') == expected_headers
19011907
19021908 @pytest.mark.usefixtures('ignore_nplusone')
19031909 def test_export_vuln_csv_filters_confirmed_using_filters_query(self, test_client, session):
19781984 "impact_accountability"
19791985 ]
19801986 final_expected_headers = expected_headers + custom_fields
1981 csv_data = csv.reader(BytesIO(raw_csv_data), delimiter=',')
1987 csv_data = csv.reader(StringIO(raw_csv_data.decode('utf-8')), delimiter=',')
19821988 for index, line in enumerate(csv_data):
19831989
19841990 if index == 0:
21682174 res = test_client.post(self.url(), data=raw_data)
21692175 assert res.status_code == 400
21702176
2177 @pytest.mark.usefixtures('ignore_nplusone')
2178 def test_bulk_delete_vuln_id(self, host_with_hostnames, test_client, session):
2179 """
2180 This one should only check basic vuln properties
2181 :param host_with_hostnames:
2182 :param test_client:
2183 :param session:
2184 :return:
2185 """
2186 session.commit() # flush host_with_hostnames
2187 raw_data_vuln_1 = _create_post_data_vulnerability(
2188 name='New vuln 1',
2189 vuln_type='Vulnerability',
2190 parent_id=host_with_hostnames.id,
2191 parent_type='Host',
2192 refs=[],
2193 policyviolations=[],
2194 description='helloworld 1',
2195 severity='low',
2196 )
2197 raw_data_vuln_2 = _create_post_data_vulnerability(
2198 name='New vuln 2',
2199 vuln_type='Vulnerability',
2200 parent_id=host_with_hostnames.id,
2201 parent_type='Host',
2202 refs=[],
2203 policyviolations=[],
2204 description='helloworld 2',
2205 severity='low',
2206 )
2207 ws_name = host_with_hostnames.workspace.name
2208 vuln_count_previous = session.query(Vulnerability).count()
2209 res_1 = test_client.post('/v2/ws/{0}/vulns/'.format(ws_name), data=raw_data_vuln_1)
2210 res_2 = test_client.post('/v2/ws/{0}/vulns/'.format(ws_name), data=raw_data_vuln_2)
2211 vuln_1_id = res_1.json['obj_id']
2212 vuln_2_id = res_2.json['obj_id']
2213 vulns_to_delete = [vuln_1_id, vuln_2_id]
2214 request_data = {'vulnerability_ids': vulns_to_delete}
2215 delete_response = test_client.delete('/v2/ws/{0}/vulns/bulk_delete/'.format(ws_name), data=request_data)
2216 vuln_count_after = session.query(Vulnerability).count()
2217 deleted_vulns = delete_response.json['deleted_vulns']
2218 assert delete_response.status_code == 200
2219 assert vuln_count_previous == vuln_count_after
2220 assert deleted_vulns == len(vulns_to_delete)
2221
2222 @pytest.mark.usefixtures('ignore_nplusone')
2223 def test_bulk_delete_vuln_severity(self, host_with_hostnames, test_client, session):
2224 """
2225 This one should only check basic vuln properties
2226 :param host_with_hostnames:
2227 :param test_client:
2228 :param session:
2229 :return:
2230 """
2231 session.commit() # flush host_with_hostnames
2232 raw_data_vuln_1 = _create_post_data_vulnerability(
2233 name='New vuln 1',
2234 vuln_type='Vulnerability',
2235 parent_id=host_with_hostnames.id,
2236 parent_type='Host',
2237 refs=[],
2238 policyviolations=[],
2239 description='helloworld 1',
2240 severity='low',
2241 )
2242 raw_data_vuln_2 = _create_post_data_vulnerability(
2243 name='New vuln 2',
2244 vuln_type='Vulnerability',
2245 parent_id=host_with_hostnames.id,
2246 parent_type='Host',
2247 refs=[],
2248 policyviolations=[],
2249 description='helloworld 2',
2250 severity='low',
2251 )
2252 ws_name = host_with_hostnames.workspace.name
2253 vuln_count_previous = session.query(Vulnerability).count()
2254 res_1 = test_client.post('/v2/ws/{0}/vulns/'.format(ws_name), data=raw_data_vuln_1)
2255 res_2 = test_client.post('/v2/ws/{0}/vulns/'.format(ws_name), data=raw_data_vuln_2)
2256 vuln_1_id = res_1.json['obj_id']
2257 vuln_2_id = res_2.json['obj_id']
2258 vulns_to_delete = [vuln_1_id, vuln_2_id]
2259 request_data = {'severities': ['low']}
2260 delete_response = test_client.delete('/v2/ws/{0}/vulns/bulk_delete/'.format(ws_name), data=request_data)
2261 vuln_count_after = session.query(Vulnerability).count()
2262 deleted_vulns = delete_response.json['deleted_vulns']
2263 assert delete_response.status_code == 200
2264 assert vuln_count_previous == vuln_count_after
2265 assert deleted_vulns == len(vulns_to_delete)
21712266
21722267 @pytest.mark.usefixtures('logged_user')
21732268 class TestVulnerabilityCustomFields(ReadOnlyAPITests):
24952590 field = VulnerabilitySchema().fields['data']
24962591 assert isinstance(field, NullToBlankString)
24972592 assert field.allow_none
2593
2594
2595 # I'm Py3
44 See the file 'doc/LICENSE' for the license information
55
66 '''
7 from __future__ import absolute_import
8
79 import os
810 from io import BytesIO
911 import pytest
1012
1113 from faraday.server.api.modules.vulnerability_template import VulnerabilityTemplateView
1214 from tests import factories
13 from test_api_non_workspaced_base import (
15 from tests.test_api_non_workspaced_base import (
1416 ReadOnlyAPITests
1517 )
1618 from faraday.server.models import (
246248
247249 def test_add_vuln_template_from_csv(self, session, test_client, csrf_token):
248250 expected_created_vuln_template = 1
249 file_contents = """cwe,name,description,resolution,exploitation,references\n
251 file_contents = b"""cwe,name,description,resolution,exploitation,references\n
250252 CWE-119,EN-Improper Restriction of Operations within the Bounds of a Memory Buffer (Type: Class),"The software performs operations on a memory buffer, but it can read from or write to a memory location that is outside of the intended boundary of the buffer.\n
251253 Certain languages allow direct addressing of memory locations and do not automatically ensure that these locations are valid for the memory buffer that is being referenced. This can cause read or write operations to be performed on memory locations that may be associated with other variables, data structures, or internal program data.\n
252254 As a result, an attacker may be able to execute arbitrary code, alter the intended control flow, read sensitive information, or cause the system to crash.",,high,"Writing Secure Code: Chapter 5, ""Public Enemy #1: The Buffer Overrun"" Page 127; Chapter 14, ""Prevent I18N Buffer Overruns"" Page 441\n
267269 res = test_client.post('/v2/vulnerability_template/bulk_create/',
268270 data=data, headers=headers, use_json_data=False)
269271 assert res.status_code == 200
270 assert res.json['vulns_created'] == expected_created_vuln_template
272 assert res.json['vulns_created'] == expected_created_vuln_template
273
274 def test_add_unicode_vuln_template_from_csv(self, session, test_client, csrf_token):
275 expected_created_vuln_template = 1
276 file_contents = """cwe,name,description,resolution,exploitation,references
277 ,ES-Exposición de información a través del listado de directorios,"Estos directorios no deberian estar publicos, pues exponen información sensible del tipo de tecnología utilizada, código de programación, información sobre rutas de acceso a distintos lugares, particularmente en este caso podemos listar toda la información del servidor sin ningun tipo de restricción
278 ",Siempre evitar que se puedan listar directorios de manera externa y sin permisos,high,
279 """
280 data = {
281 'file': (BytesIO(file_contents.encode()), 'vulns.csv'),
282 'csrf_token': csrf_token
283 }
284 headers = {'Content-type': 'multipart/form-data'}
285 res = test_client.post('/v2/vulnerability_template/bulk_create/',
286 data=data, headers=headers, use_json_data=False)
287 assert res.status_code == 200
288 assert res.json['vulns_created'] == expected_created_vuln_template
289
290 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7 from builtins import str
8
69 import pytest
710 from faraday.server.api.modules.websocket_auth import decode_agent_websocket_token
811
8487 assert res.status_code == 200
8588 decoded_agent = decode_agent_websocket_token(res.json['token'])
8689 assert decoded_agent == agent
90
91
92 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import time
79 import pytest
810
232234
233235 active_query = session.query(Workspace).filter_by(id=workspace.id).first().active
234236 assert active_query == False
237
238 def test_create_fails_with_start_date_greater_than_end_date(self,
239 session,
240 test_client):
241 workspace_count_previous = session.query(Workspace).count()
242 duration = {'start_date': 1563638577, 'end_date': 1563538577}
243 raw_data = {'name': 'somethingdarkside', 'duration': duration}
244 res = test_client.post('/v2/ws/', data=raw_data)
245 assert res.status_code == 400
246 assert workspace_count_previous == session.query(Workspace).count()
247
248 # I'm Py3
44 See the file 'doc/LICENSE' for the license information
55
66 '''
7 from __future__ import absolute_import
8 from builtins import str
79
810 """Generic tests for APIs prefixed with a workspace_name"""
911
1012 import pytest
1113 from sqlalchemy.orm.util import was_deleted
1214 from faraday.server.models import db, Workspace, Credential
13 from test_api_pagination import PaginationTestsMixin as \
15 from tests.test_api_pagination import PaginationTestsMixin as \
1416 OriginalPaginationTestsMixin
1517
1618 API_PREFIX = '/v2/ws/'
5052 workspace = workspace or self.workspace
5153 url = API_PREFIX + workspace.name + '/' + self.api_endpoint + '/'
5254 if obj is not None:
53 id_ = unicode(obj.id) if isinstance(
54 obj, self.model) else unicode(obj)
55 id_ = str(obj.id) if isinstance(
56 obj, self.model) else str(obj)
5557 url += id_ + u'/'
5658 return url
5759
286288 RetrieveTestsMixin,
287289 GenericAPITest):
288290 pass
291
292
293 # I'm Py3
44 See the file 'doc/LICENSE' for the license information
55
66 '''
7 from __future__ import absolute_import
8
79 from faraday.server.models import Host, Service, Vulnerability
810 import random
911 def new_random_workspace_name():
4143 self.model_controller.addVulnToServiceSYNC(host.getID(), service.getID(), vuln)
4244
4345 return vuln
46
47
48 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
67
78 try:
89 from lxml import etree as ET
2829 default_element = tree.find(tag_name).text
2930
3031 return default_element
32
33
34 # I'm Py3
0 from __future__ import absolute_import
1 from __future__ import print_function
2
03 import os
14 import subprocess
2 from faraday.server.config import FARADAY_BASE
35
4 try:
5 import ConfigParser
6 except ImportError:
7 import faraday.client.configparser as ConfigParser
6 from configparser import SafeConfigParser, DuplicateSectionError
87
98
109 def test_manage_migrate():
2019 password=os.environ['POSTGRES_PASSWORD'],
2120 database=os.environ['POSTGRES_DB'],
2221 )
23 faraday_config = ConfigParser.SafeConfigParser()
22 faraday_config = SafeConfigParser()
2423 config_path = os.path.expanduser('~/.faraday/config/server.ini')
2524 faraday_config.read(config_path)
2625 try:
2726 faraday_config.add_section('database')
28 except ConfigParser.DuplicateSectionError:
27 except DuplicateSectionError:
2928 pass
3029 faraday_config.set('database', 'connection_string', connection_string)
3130 with open(config_path, 'w') as faraday_config_file:
4140 subproc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
4241 subproc.wait()
4342 std, err = subproc.communicate()
44 print std
45 print err
43 print(std)
44 print(err)
4645 assert subproc.returncode == 0, ('manage migrate failed!', std, err)
46
47
48 # I'm Py3
0 from __future__ import absolute_import
1 from __future__ import print_function
2
03 import os
14 import time
25 import signal
47 from datetime import datetime
58 from faraday.server.utils import daemonize
69
7 try:
8 import ConfigParser
9 except ImportError:
10 import faraday.client.configparser as ConfigParser
10 from configparser import SafeConfigParser, DuplicateSectionError
1111
1212
1313 def test_start_and_kill_faraday_server():
3333 password=os.environ['POSTGRES_PASSWORD'],
3434 database=os.environ['POSTGRES_DB'],
3535 )
36 faraday_config = ConfigParser.SafeConfigParser()
36 faraday_config = SafeConfigParser()
3737 config_path = os.path.expanduser('~/.faraday/config/server.ini')
3838 faraday_config.read(config_path)
3939 try:
4040 faraday_config.add_section('database')
41 except ConfigParser.DuplicateSectionError:
41 except DuplicateSectionError:
4242 pass
4343 faraday_config.set('database', 'connection_string', connection_string)
4444 with open(config_path, 'w') as faraday_config_file:
7171 with open(log_path, 'r') as log_file:
7272 print(log_file.read())
7373 assert subproc.returncode == 0, (out, err, command)
74
75
76 # I'm Py3
+0
-70
tests/test_import_users_from_couch.py less more
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 '''
6 import pytest
7 from passlib.hash import pbkdf2_sha1
8 from faraday.server.importer import ImportCouchDBUsers
9
10 NON_ADMIN_DOC = {
11 "_id": "org.couchdb.user:removeme2",
12 "_rev": "0-00000000000000000000000000000000",
13 "password_scheme": "pbkdf2",
14 "iterations": 10,
15 "name": "removeme2",
16 "roles": [
17 "pentester"
18 ],
19 "type": "user",
20 "derived_key": "d2061cb98f85b5e14eda97da556c1625906e5c2b",
21 "salt": "60604061640065389e9e90ca12c83b8d"
22 }
23
24 ADMIN_DOC = {
25 "_id": "org.couchdb.user:removeme",
26 "_rev": "1-00000000000000000000000000000000",
27 "name": "removeme",
28 "password": None,
29 "roles": [],
30 "type": "user"
31 }
32
33 import_users_from_couch = ImportCouchDBUsers()
34
35 def test_import_encrypted_password_from_admin_user():
36 original_hash = ('-pbkdf2-eeea435c505e74d33a8c1b55c39d8dd355db4c2d,'
37 'aedeef5a01f96a84360d2719fc521b9f,10')
38 new_hash = import_users_from_couch.convert_couchdb_hash(original_hash)
39 assert pbkdf2_sha1.verify('12345', new_hash)
40
41
42 def test_import_plaintext_password_from_admin_user():
43 assert import_users_from_couch.convert_couchdb_hash('12345') == '12345'
44
45
46 def test_import_non_admin_from_document():
47 new_hash = import_users_from_couch.get_hash_from_document(NON_ADMIN_DOC)
48 assert pbkdf2_sha1.verify('12345', new_hash)
49
50
51 @pytest.mark.skip(reason="Now it use default password 'changeme'")
52 def test_import_admin_from_document_fails():
53 with pytest.raises(ValueError):
54 import_users_from_couch.get_hash_from_document(ADMIN_DOC)
55
56
57 def test_parse_all_docs_response_succeeds():
58 doc_with_metadata = {
59 "id": "org.couchdb.user:removeme",
60 "key": "org.couchdb.user:removeme",
61 "value": {"rev": "1-00000000000000000000000000000000"},
62 "doc": ADMIN_DOC
63 }
64 data = {
65 "total_rows": 15,
66 "offset": 0,
67 "rows": [doc_with_metadata] * 15
68 }
69 assert import_users_from_couch.parse_all_docs(data) == [ADMIN_DOC] * 15
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 from functools import partial
79
810 import pytest
896898 if obj_class not in [Command]:
897899 metadata = serialized_obj.pop('metadata')
898900 assert serialized_obj == test_data['serialized_expected_results']
901
902
903 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import time
79 import datetime
810 import pytest
187189 "name": "test",
188190 "x": 5,
189191 }
192
193
194 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from Queue import Queue
6 from __future__ import absolute_import
7 from __future__ import print_function
8
9 from queue import Queue
710
811 import time
912
10 import mock
13 from unittest import mock
1114 import pytest
1215
1316 from faraday.client.managers.mapper_manager import MapperManager
7780 pending_actions = Queue()
7881 controller = ModelController(mappers_manager, pending_actions)
7982 assert controller.processing is False
80 assert controller._stop is False
83 assert controller._must_stop is False
8184 controller.start()
82 assert controller.isAlive()
85 assert controller.is_alive()
8386 controller.stop()
84 assert controller._stop is True
8587 controller.join()
86 assert controller.isAlive() is False
88 assert controller._must_stop is True
89 assert controller.is_alive() is False
8790
8891
8992 def test_controller_cant_be_stopped_when_is_processing():
9699 pending_actions = Queue()
97100 controller = ModelController(mappers_manager, pending_actions)
98101 assert controller.processing is False
99 assert controller._stop is False
102 assert controller._must_stop is False
100103 controller.start()
101104 controller.processing = True
102105 controller.active_plugins_count = 1
103 assert controller.isAlive()
106 assert controller.is_alive()
104107 controller.stop()
105 assert controller._stop
108 assert controller._must_stop is True
106109 assert controller.processing
107110 controller.join(timeout=2)
108 assert controller.isAlive()
111 assert controller.is_alive()
109112 controller.processing = False
110113 controller.join()
111 assert controller.isAlive() is False
114 assert controller.is_alive() is False
112115
113116
114117 def test_controller_plugin_start_action_updates_internal_state():
126129 assert controller.processing is False
127130 controller.stop()
128131 controller.join()
129 assert controller.isAlive() is False
132 assert controller.is_alive() is False
130133
131134 def test_only_start_plugin():
132135 mappers_manager = MapperManager()
158161
159162
160163
161 @pytest.mark.parametrize("url_endpoint, test_data", TEST_CASES.items())
164 @pytest.mark.parametrize("url_endpoint, test_data", list(TEST_CASES.items()))
162165 @mock.patch('faraday.client.persistence.server.server._get')
163166 def test_find(get, url_endpoint, test_data, session):
164167 if 'api_result' in test_data:
176179 print(get.mock_calls[0][1][0])
177180 assert get.mock_calls[0][1][0].endswith(
178181 '/_api/v2/ws/{0}/{1}/{2}/'.format(workspace.name, url_endpoint, obj.id))
182
183
184 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import pytest
79 from tests.factories import HostFactory, ServiceFactory
10 from faraday.server.models import Host, Workspace
811
912
1013 def test_child_parent_verification_event_fails(session, workspace,
1417 with pytest.raises(AssertionError):
1518 session.commit()
1619
20 session.rollback()
21
22 assert session.query(Host).filter(
23 Workspace.id == workspace.id
24 ).first() == None
25
1726
1827 def test_child_parent_verification_event_succeeds(session, workspace):
1928 """
2231 host = HostFactory.build(workspace=workspace)
2332 ServiceFactory.build(host=host, workspace=workspace)
2433 session.commit()
34
35
36 def test_child_parent_verification_event_fails_update(session, workspace,
37 second_workspace):
38 host = HostFactory.build(workspace=workspace)
39 service = ServiceFactory.build(host=host, workspace=workspace)
40 session.commit()
41 service.workspace = second_workspace
42 session.add(service)
43 with pytest.raises(AssertionError):
44 session.commit()
45
46
47 def test_child_parent_verification_event_succeds_update(session, workspace):
48 host = HostFactory.build(workspace=workspace)
49 service = ServiceFactory.build(host=host, workspace=workspace)
50 session.commit()
51 service.workspace = workspace
52 session.add(service)
53 session.commit()
54
55
56 def test_child_parent_verification_event_changing_id_fails(session, workspace,
57 second_workspace):
58
59 session.add(workspace)
60 session.add(second_workspace)
61 session.commit()
62 host = HostFactory.build(workspace=workspace)
63 session.add(host)
64 session.commit()
65 service = ServiceFactory.build(host=host, workspace_id=second_workspace.id)
66
67 session.add(service)
68
69 with pytest.raises(AssertionError):
70 session.commit()
71
72
73 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import os
79
810 from faraday.server.fields import FaradayUploadedFile
1113
1214
1315 def test_html_content_type_is_not_html():
14 with open(os.path.join(CURRENT_PATH, 'data', 'test.html'))as image_data:
16 with open(os.path.join(CURRENT_PATH, 'data', 'test.html'), "rb")as image_data:
1517 field = FaradayUploadedFile(image_data.read())
1618 assert field['content_type'] == 'application/octet-stream'
1719 assert len(field['files']) == 1
1921
2022 def test_image_is_detected_correctly():
2123
22 with open(os.path.join(CURRENT_PATH, 'data', 'faraday.png'))as image_data:
24 with open(os.path.join(CURRENT_PATH, 'data', 'faraday.png'), "rb")as image_data:
2325 field = FaradayUploadedFile(image_data.read())
2426 assert field['content_type'] == 'image/png'
2527 assert 'thumb_id' in field.keys()
2830
2931
3032 def test_normal_attach_is_not_detected_as_image():
31 with open(os.path.join(CURRENT_PATH, 'data', 'report_w3af.xml'))as image_data:
33 with open(os.path.join(CURRENT_PATH, 'data', 'report_w3af.xml'), "rb")as image_data:
3234 field = FaradayUploadedFile(image_data.read())
3335 assert field['content_type'] == 'application/octet-stream'
3436 assert len(field['files']) == 1
37
38
39 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import unittest
79 import json
810 from faraday.client.persistence.server import models
9 from faraday.client.persistence.server import server_io_exceptions
10 from mock import MagicMock, patch, create_autospec
11 from unittest.mock import patch
1112
1213 HOST_JSON_STRING = '{"_id":1,"id":"08d3b6545ec70897daf05cd471f4166a8e605c00","key":"08d3b6545ec70897daf05cd471f4166a8e605c00","value":{"_id":"08d3b6545ec70897daf05cd471f4166a8e605c00","_rev":"1-a12368dc03d557c337e833f8090db568","default_gateway":["192.168.20.1","00:1d:aa:c9:83:e8"],"description":"","metadata":{"create_time":1475852074.455225,"creator":"","owner":"","update_action":0,"update_controller_action":"ModelControler._processAction ModelControler.newHost","update_time":1475852074.455226,"update_user":""},"name":"10.31.112.29","os":"Microsoft Windows Server 2008 R2 Standard Service Pack 1","owned":"false","owner":"","services":12,"vulns":43}}'
1314
7879 self.assertTrue(all([isinstance(s, models.Service) for s in services]))
7980 self.assertTrue(all([isinstance(v, models.Vuln) for v in vulns]))
8081 self.assertTrue(all([isinstance(v, models.VulnWeb) for v in vulns_web]))
82
83
84 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import faraday.client.persistence.server.models as models
79 import pytest
810 import responses
911 import requests
10 from mock import Mock, patch
12 from unittest.mock import Mock, patch
1113
12 import faraday.server.config
1314
14 from test_api_workspaced_base import GenericAPITest
15 from tests.test_api_workspaced_base import GenericAPITest
1516
1617 from tests.factories import VulnerabilityWebFactory, VulnerabilityFactory
1718
170171 a = requests.put('http://localhost:{0}/_api/v2/ws/{1}/vulns/{2}/'.format(port,self.workspace.name, v.id))
171172
172173 models.update_vuln_web(self.workspace.name, v)
174
175
176 # I'm Py3
+0
-43
tests/test_persistence_server_server.py less more
0 '''
1 Faraday Penetration Test IDE
2 Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
3 See the file 'doc/LICENSE' for the license information
4
5 '''
6 import os
7
8 import responses
9 import requests
10
11 import pytest
12
13 from faraday.client.persistence.server import server
14
15
16 @pytest.mark.usefixtures('logged_user')
17 class TestServerFuncions:
18
19 @responses.activate
20 def test_test_server_url_server_runnimg(self, test_client):
21 """
22 The name of the method is correct we are testing the function:
23 check_server_url
24
25 """
26 mocked_response = {
27 "Faraday Server": "Running",
28 "Version":"2.7.1"
29 }
30
31 responses.add(responses.GET, 'http://localhost/_api/v2/info',
32 json=mocked_response, status=200)
33 assert server.check_server_url('http://localhost')
34
35 @responses.activate
36 def test_test_server_url_another_http_returns_404(self):
37 responses.add(responses.GET, 'http://localhost/_api/v2/info',
38 status=404)
39 assert not server.check_server_url('http://localhost')
40
41 def test_test_server_url_aserver_down(self, test_client):
42 assert not server.check_server_url('http://localhost')
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import sys
79 sys.path.append('.')
810 import unittest
9 from Queue import Queue
10 from mock import MagicMock as mock
11 from queue import Queue
12 from unittest.mock import MagicMock as mock
1113
1214 import faraday.client.plugins.controller
1315
9597 new_settings = {}
9698 self.controller.updatePluginSettings(plugin_id, new_settings)
9799 self.plugin1.updateSettings.assert_called_once_with(new_settings)
100
101
102 # I'm Py3
0 from __future__ import absolute_import
1
02 import unittest
13
24
4547 from faraday.server.config import storage
4648 self.path = storage.path
4749
50
51
52 # I'm Py3
0 from __future__ import absolute_import
1
02 import pytest
13
24 from faraday.searcher.api import Api
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import os
79 import sys
810 import unittest
5355 self.assertEqual(res.status_code, 401)
5456
5557 def test_401_when_posting_an_existent_view_and_not_logged(self):
56 res = self.app.post('/', 'data')
58 res = self.app.post('/', data={'data':'data'})
5759 self.assertEqual(res.status_code, 401)
5860
5961 def test_401_when_accessing_a_non_existent_view_and_not_logged(self):
60 res = self.app.post('/dfsdfsdd', 'data')
62 res = self.app.post('/dfsdfsdd', data={'data':'data'})
6163 self.assertEqual(res.status_code, 401)
6264
6365 def test_200_when_not_logged_but_endpoint_is_public(self):
9395
9496 if __name__ == '__main__':
9597 unittest.main()
98
99
100 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7 import re
68 import random
79 import string
8 import mock
9 import os
10 import re
10 from unittest import mock
1111
1212 from faraday import __version__
13 from faraday.server.config import FARADAY_BASE
1413
1514 from faraday.server.config import (
1615 copy_default_config_to_local,
17 gen_web_config
1816 )
1917
2018
7876
7977 def test_exists_and_content():
8078 assert isPEP440(__version__)
79
80
81 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import os
79 import sys
810 import unittest
1214 sys.path.append(os.path.abspath(os.getcwd()))
1315 from faraday.client.persistence.server import server
1416 from faraday.client.persistence.server import server_io_exceptions
15 from mock import MagicMock, patch
17 from unittest.mock import MagicMock, patch
1618
1719 server.FARADAY_UP = False
1820 server.SERVER_URL = "http://localhost:5985"
164166 obj_sign_to_mock[obj_sign].assert_called_once_with('a')
165167 with self.assertRaises(server_io_exceptions.WrongObjectSignature):
166168 server.get_objects('a', 'not a signature')
169
170
171 # I'm Py3
33 See the file 'doc/LICENSE' for the license information
44
55 '''
6 from __future__ import absolute_import
7
68 import pytest
79
810 from faraday.server.utils.database import get_unique_fields
4446 statement = vulnerability_uniqueness.statement
4547 column_part = statement.split('%(fullname)s')[1]
4648 statements_clean = column_part.strip().strip(')').strip('(').split(',')
47 statements_clean = map(lambda column: column.replace('COALESCE(', '').replace('md5(', '').strip('('), statements_clean)
48 statements_clean = filter(len, map(lambda column: column.strip("'')").strip('-1').strip('-1));').strip(), statements_clean))
49 statements_clean = [column.replace('COALESCE(', '').replace('md5(', '').strip('(') for column in statements_clean]
50 statements_clean = \
51 list(
52 filter(len,
53 map(lambda column: column.strip("'')").strip('-1').strip('-1));').strip(), statements_clean)
54 )
55 )
4956 statements_clean.remove('source_code_id') # we don't support source_code yet
5057 unique_constraints = get_unique_fields(session, Vulnerability())
5158 for unique_constraint in unique_constraints:
5663 raise Exception('Please check server.utils.database.get_unique_fields. Vulnerability DDL changed?')
5764
5865
59 @pytest.mark.parametrize("obj_class, expected_unique_fields", sorted(UNIQUE_FIELDS.items()))
66 @pytest.mark.parametrize("obj_class, expected_unique_fields", list(UNIQUE_FIELDS.items()))
6067 def test_unique_fields_workspace(obj_class, expected_unique_fields, session):
6168 object_ = obj_class()
6269 unique_constraints = get_unique_fields(session, object_)
6370 for unique_constraint in unique_constraints:
6471 assert unique_constraint == expected_unique_fields
72
73
74 # I'm Py3
0 from __future__ import absolute_import
1
02 import pytest
1 from faraday.server.models import Agent
2 from faraday.server.websocket_factories import WorkspaceServerFactory
3 from faraday.server.models import Agent, Executor
4 from faraday.server.websocket_factories import WorkspaceServerFactory, update_executors
35
4 from tests.factories import AgentFactory
6 from tests.factories import AgentFactory, ExecutorFactory
7
58
69 def _join_agent(test_client, session):
710 agent = AgentFactory.create(token='pepito')
3033 message = '{"action": "JOIN_AGENT"}'
3134 assert not proto.onMessage(message, False)
3235
33 def test_join_agent_message_with_valid_token(self, session, proto, test_client):
36 def test_join_agent_message_with_valid_token(self, session, proto, workspace, test_client):
3437 token = _join_agent(test_client, session)
35 message = '{{"action": "JOIN_AGENT", "token": "{}" }}'.format(token)
38 message = '{{"action": "JOIN_AGENT", "workspace": "{}", "token": "{}", "executors": [] }}'.format(workspace.name, token)
3639 assert proto.onMessage(message, False)
3740
38 def test_leave_agent_happy_path(self, session, proto, test_client):
41 def test_leave_agent_happy_path(self, session, proto, workspace, test_client):
3942 token = _join_agent(test_client, session)
40 message = '{{"action": "JOIN_AGENT", "token": "{}" }}'.format(token)
43 message = '{{"action": "JOIN_AGENT", "workspace": "{}", "token": "{}", "executors": [] }}'.format(workspace.name, token)
4144 assert proto.onMessage(message, False)
4245
4346 message = '{{"action": "LEAVE_AGENT" }}'.format(token)
4447 assert not proto.onMessage(message, False)
4548
46 def test_agent_status(self, session, proto, test_client):
49 def test_agent_status(self, session, proto, workspace, test_client):
4750 token = _join_agent(test_client, session)
4851 agent = Agent.query.one()
4952 assert not agent.is_online
50 message = '{{"action": "JOIN_AGENT", "token": "{}" }}'.format(token)
53 message = '{{"action": "JOIN_AGENT", "workspace": "{}", "token": "{}", "executors": [] }}'.format(workspace.name, token)
5154 assert proto.onMessage(message, False)
5255 assert agent.is_online
5356
5457 message = '{{"action": "LEAVE_AGENT"}}'.format(token)
5558 assert not proto.onMessage(message, False)
5659 assert not agent.is_online
60
61
62 class TestCheckExecutors:
63
64 def test_new_executors_not_in_database(self, session):
65 agent = AgentFactory.create()
66 executors = [
67 {'executor_name': 'nmap_executor', 'args': {'param1': True}}
68 ]
69
70 assert update_executors(agent, executors)
71
72 def test_executors_with_missing_args(self, session):
73 agent = AgentFactory.create()
74 executors = [
75 {'executor_name': 'nmap_executor'}
76 ]
77
78 assert update_executors(agent, executors)
79
80 def test_executors_with_missing_executor_name(self, session):
81 agent = AgentFactory.create()
82 executors = [
83 {'invalid_key': 'nmap_executor'}
84 ]
85
86 assert update_executors(agent, executors)
87
88 def test_executors_with_some_invalid_executors(self, session):
89 agent = AgentFactory.create()
90 executors = [
91 {'invalid_key': 'nmap_executor'},
92 {'executor_name': 'executor 1', 'args': {'param1': True}}
93 ]
94
95 assert update_executors(agent, executors)
96 count_executors = Executor.query.filter_by(agent=agent).count()
97 assert count_executors == 1
98
99 def test_executor_already_in_database_but_new_parameters_incoming(self, session):
100 agent = AgentFactory.create()
101 old_params = {'old_param': True}
102 executor = ExecutorFactory.create(agent=agent, parameters_metadata=old_params)
103 session.add(executor)
104 session.commit()
105 new_params = old_params
106 new_params.update({'param1': True})
107 executors = [
108 {'executor_name': executor.name, 'args': new_params}
109 ]
110
111 assert update_executors(agent, executors)
112 from_db_executor = Executor.query.filter_by(id=executor.id, agent=agent).first()
113 assert from_db_executor.parameters_metadata == new_params
114
115 def test_new_executor_and_delete_the_old_one(self, session):
116 agent = AgentFactory.create()
117 params = {'old_param': True}
118 executor = ExecutorFactory.create(
119 name='old_executor',
120 agent=agent,
121 parameters_metadata=params
122 )
123 session.add(executor)
124 session.commit()
125 executors = [
126 {'executor_name': 'new executor', 'args': {'param1': True}}
127 ]
128
129 assert update_executors(agent, executors)
130 count_executors = Executor.query.filter_by(agent=agent).count()
131 assert count_executors == 1
132 current_executor = Executor.query.filter_by(agent=agent).first()
133 assert current_executor.name == 'new executor'
134 assert current_executor.parameters_metadata == {'param1': True}
135
136 def test_remove_all_executors(self, session):
137 agent = AgentFactory.create()
138 params = {'old_param': True}
139 executor = ExecutorFactory.create(
140 name='old_executor',
141 agent=agent,
142 parameters_metadata=params
143 )
144 session.add(executor)
145 session.commit()
146 executors = [
147 ]
148
149 assert update_executors(agent, executors)
150 count_executors = Executor.query.filter_by(agent=agent).count()
151 assert count_executors == 0
152
153 def test_remove_one_of_two_executors(self, session):
154 agent = AgentFactory.create()
155 executor = ExecutorFactory.create(
156 name='executor 1',
157 agent=agent,
158 parameters_metadata={'param1': True}
159 )
160 session.add(executor)
161 another_executor = ExecutorFactory.create(
162 name='executor 2',
163 agent=agent,
164 parameters_metadata={'param2': True}
165 )
166 session.add(executor)
167 session.add(another_executor)
168 session.commit()
169 executors = [
170 {'executor_name': 'executor 2', 'args': {'param2': True}}
171 ]
172
173 assert update_executors(agent, executors)
174 count_executors = Executor.query.filter_by(agent=agent).count()
175 assert count_executors == 1
176 from_db_executor = Executor.query.filter_by(id=another_executor.id, agent=agent).first()
177 assert from_db_executor.name == 'executor 2'