├── .gitattributes ├── Screenshots ├── Diff.png ├── Login.png ├── Revisions.png ├── LandingPage.png ├── Post-Login.png └── Submissions.png ├── logo └── GoASQ_logo.png ├── CONTRIBUTING ├── src ├── db │ ├── index │ │ └── AnswersIndex.txt │ ├── triggers │ │ ├── Before_Update_On_Audit.txt │ │ ├── Before_Delete_On_Audit.txt │ │ ├── After_Insert_On_Answers.txt │ │ ├── After_Update_On_Answers.txt │ │ └── Before_Delete_On_Answers.txt │ ├── table_schemas │ │ ├── Answers.txt │ │ └── Audit.txt │ └── __init__.py ├── vsaq │ └── static │ │ ├── favicon.ico │ │ ├── loading.gif │ │ ├── mode_edit.png │ │ ├── searchicon.png │ │ ├── searchicon.svg │ │ ├── questionnaire │ │ ├── questionnaire_editor_init.js │ │ ├── questionnaire_test_dom.html │ │ ├── items_test_dom.html │ │ ├── utils_test_dom.html │ │ ├── boxitem_test_dom.html │ │ ├── infoitem_test_dom.html │ │ ├── lineitem_test_dom.html │ │ ├── tipitem_test_dom.html │ │ ├── blockitems_test_dom.html │ │ ├── checkitem_test_dom.html │ │ ├── groupitem_test_dom.html │ │ ├── radioitem_test_dom.html │ │ ├── spaceritem_test_dom.html │ │ ├── uploaditem_test_dom.html │ │ ├── yesnoitem_test_dom.html │ │ ├── containeritem_test_dom.html │ │ ├── questionnaire_editor_test_dom.html │ │ ├── externs.js │ │ ├── infoitem_test.js │ │ ├── spaceritem_test.js │ │ ├── containeritem_test.js │ │ ├── boxitem_test.js │ │ ├── checkitem_test.js │ │ ├── spaceritem.js │ │ ├── lineitem_test.js │ │ ├── yesnoitem_test.js │ │ ├── infoitem.js │ │ ├── radioitem_test.js │ │ ├── blockitems_test.js │ │ ├── groupitem_test.js │ │ ├── tipitem_test.js │ │ ├── checkitem.js │ │ ├── boxitem.js │ │ ├── utils_test.js │ │ ├── lineitem.js │ │ ├── containeritem.js │ │ ├── blockitems.js │ │ ├── radioitem.js │ │ └── yesnoitem.js │ │ ├── dialoghelper.js │ │ ├── utils.js │ │ └── qpage_base.js ├── questionnaires │ ├── test_template_extension.json │ └── test_template.json ├── requirements.txt ├── requirements-dev.txt ├── .gitmodules ├── login │ └── img │ │ ├── pk-icon-username.svg │ │ └── pk-icon-password.svg ├── dbHandler.py ├── fileSystemHandler.py ├── randomizer.py ├── nic.py ├── compiler.flags ├── config_reader.py ├── customflask.py ├── translator.py ├── cryptoUtils.py ├── client_side_only_impl │ ├── index.html │ ├── example.html │ ├── vsaq_editor.html │ └── all_tests.html ├── rename_files.sh ├── placeholders.compile.cfg ├── scripts │ └── vsaq_test ├── ldapUtils.py ├── flask_sslify.py ├── download-libs.sh ├── renderer.py ├── app.config ├── app.config.debug └── goasq_server.py ├── .travis.yml ├── .gitignore └── AUTHORS /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Screenshots/Diff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morningstar/GoASQ/HEAD/Screenshots/Diff.png -------------------------------------------------------------------------------- /logo/GoASQ_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morningstar/GoASQ/HEAD/logo/GoASQ_logo.png -------------------------------------------------------------------------------- /Screenshots/Login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morningstar/GoASQ/HEAD/Screenshots/Login.png -------------------------------------------------------------------------------- /Screenshots/Revisions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morningstar/GoASQ/HEAD/Screenshots/Revisions.png -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | Want to contribute? That's exciting! 2 | 3 | Head over to https://github.com/Morningstar/ 4 | -------------------------------------------------------------------------------- /Screenshots/LandingPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morningstar/GoASQ/HEAD/Screenshots/LandingPage.png -------------------------------------------------------------------------------- /Screenshots/Post-Login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morningstar/GoASQ/HEAD/Screenshots/Post-Login.png -------------------------------------------------------------------------------- /Screenshots/Submissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morningstar/GoASQ/HEAD/Screenshots/Submissions.png -------------------------------------------------------------------------------- /src/db/index/AnswersIndex.txt: -------------------------------------------------------------------------------- 1 | idx_AnswersIndex ON Answers (app_tid, app_name, app_champion, qid, app_pid) -------------------------------------------------------------------------------- /src/vsaq/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morningstar/GoASQ/HEAD/src/vsaq/static/favicon.ico -------------------------------------------------------------------------------- /src/vsaq/static/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morningstar/GoASQ/HEAD/src/vsaq/static/loading.gif -------------------------------------------------------------------------------- /src/vsaq/static/mode_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morningstar/GoASQ/HEAD/src/vsaq/static/mode_edit.png -------------------------------------------------------------------------------- /src/vsaq/static/searchicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Morningstar/GoASQ/HEAD/src/vsaq/static/searchicon.png -------------------------------------------------------------------------------- /src/db/triggers/Before_Update_On_Audit.txt: -------------------------------------------------------------------------------- 1 | before_update_audit BEFORE UPDATE ON Audit 2 | BEGIN 3 | SELECT RAISE(ABORT, 'Error: Updating audit table is not allowed.'); 4 | END -------------------------------------------------------------------------------- /src/db/triggers/Before_Delete_On_Audit.txt: -------------------------------------------------------------------------------- 1 | before_delete_audit BEFORE DELETE ON Audit 2 | BEGIN 3 | SELECT RAISE(ABORT, 'Error: Deleting from audit table is not allowed.'); 4 | END -------------------------------------------------------------------------------- /src/vsaq/static/searchicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - 2.7.14 4 | before_install: cd src 5 | install: 6 | - pip install -r requirements-dev.txt 7 | - ./do.sh install_deps 8 | - ./do.sh check_deps 9 | script: 10 | - ./do.sh clean 11 | - ./do.sh build debug 12 | -------------------------------------------------------------------------------- /src/db/table_schemas/Answers.txt: -------------------------------------------------------------------------------- 1 | Answers (qid TEXT PRIMARY KEY, app_status TEXT, app_tid TEXT NULL, app_pid TEXT NULL, app_name TEXT, app_champion TEXT, app_team_email TEXT, user_name TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, answer TEXT, comments TEXT, action_items TEXT, submitter_email TEXT) -------------------------------------------------------------------------------- /src/db/table_schemas/Audit.txt: -------------------------------------------------------------------------------- 1 | Audit (audit_id INTEGER PRIMARY KEY AUTOINCREMENT, qid TEXT, app_status TEXT, app_tid TEXT NULL, app_pid TEXT NULL, app_name TEXT, app_champion TEXT, app_team_email TEXT, user_name TEXT, timestamp DATETIME NOT NULL, answer TEXT, comments TEXT, action_items TEXT, submitter_email TEXT, audit_timestamp DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build folder 2 | src/build/ 3 | 4 | # Ignore third_party dependencies and other dependent modules 5 | src/third_party/ 6 | src/flask/ 7 | src/Logs/ 8 | src/uploads/ 9 | src/LocalLaunchPad/ 10 | *.pyc 11 | 12 | # Others 13 | .DS_Store 14 | *.zip 15 | *.pem 16 | *.db 17 | *.out 18 | *.bak 19 | third_party/closure-compiler 20 | third_party/closure-library 21 | third_party/closure-stylesheets 22 | third_party/js-dossier 23 | -------------------------------------------------------------------------------- /src/db/triggers/After_Insert_On_Answers.txt: -------------------------------------------------------------------------------- 1 | after_insert_answers AFTER INSERT ON Answers 2 | BEGIN 3 | INSERT into Audit (qid, app_status, app_tid, app_pid, app_name, app_champion, app_team_email, user_name, timestamp, answer, comments, action_items, submitter_email) values(NEW.qid, NEW.app_status, NEW.app_tid, NEW.app_pid, NEW.app_name, NEW.app_champion, NEW.app_team_email, NEW.user_name, NEW.timestamp, NEW.answer, NEW.comments, NEW.action_items, NEW.submitter_email); 4 | END; -------------------------------------------------------------------------------- /src/db/triggers/After_Update_On_Answers.txt: -------------------------------------------------------------------------------- 1 | after_update_answers AFTER UPDATE ON Answers 2 | BEGIN 3 | INSERT into Audit (qid, app_status, app_tid, app_pid, app_name, app_champion, app_team_email, user_name, timestamp, answer, comments, action_items, submitter_email) values(OLD.qid, OLD.app_status, OLD.app_tid, OLD.app_pid, OLD.app_name, OLD.app_champion, OLD.app_team_email, OLD.user_name, OLD.timestamp, OLD.answer, OLD.comments, OLD.action_items, OLD.submitter_email); 4 | END; -------------------------------------------------------------------------------- /src/questionnaires/test_template_extension.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "namespace": "mycompany", 4 | "extensions": [ 5 | { 6 | "type": "block", 7 | "insertAfter": "network", 8 | "text": "Questions specific to my company come here.", 9 | "items": [ 10 | { 11 | "type": "line", 12 | "id": "company_specific", 13 | "text": "Company specific question" 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/requirements.txt: -------------------------------------------------------------------------------- 1 | blessings==1.6.1 2 | certifi==2018.1.18 3 | chardet==3.0.4 4 | click==6.7 5 | click-plugins==1.0.3 6 | colorama==0.3.9 7 | Flask==0.12.2 8 | Flask-Limiter==1.0.1 9 | idna==2.6 10 | itsdangerous==0.24 11 | Jinja2==2.10 12 | limits==1.3 13 | MarkupSafe==1.0 14 | netaddr==0.7.19 15 | pyasn1==0.4.2 16 | pyasn1-modules==0.2.1 17 | pycrypto==2.6.1 18 | python-ldap==3.0.0 19 | requests==2.18.4 20 | shodan==1.7.7 21 | six==1.11.0 22 | urllib3==1.22 23 | Werkzeug==0.14.1 24 | XlsxWriter==1.0.2 25 | pysqlite==2.8.3 26 | -------------------------------------------------------------------------------- /src/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | blessings==1.6.1 2 | certifi==2018.1.18 3 | chardet==3.0.4 4 | click==6.7 5 | click-plugins==1.0.3 6 | colorama==0.3.9 7 | Flask==0.12.2 8 | Flask-Limiter==1.0.1 9 | idna==2.6 10 | itsdangerous==0.24 11 | Jinja2==2.10 12 | limits==1.3 13 | MarkupSafe==1.0 14 | netaddr==0.7.19 15 | pyasn1==0.4.2 16 | pyasn1-modules==0.2.1 17 | pycrypto==2.6.1 18 | pyjq==2.1.0 19 | python-ldap==3.0.0 20 | requests==2.18.4 21 | shodan==1.7.7 22 | six==1.11.0 23 | urllib3==1.22 24 | Werkzeug==0.14.1 25 | XlsxWriter==1.0.2 26 | pysqlite==2.8.3 27 | -------------------------------------------------------------------------------- /src/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/closure-compiler"] 2 | path = third_party/closure-compiler 3 | url = https://github.com/google/closure-compiler 4 | [submodule "third_party/closure-library"] 5 | path = third_party/closure-library 6 | url = https://github.com/google/closure-library 7 | [submodule "third_party/closure-stylesheets"] 8 | path = third_party/closure-stylesheets 9 | url = https://github.com/google/closure-stylesheets 10 | [submodule "third_party/js-dossier"] 11 | path = third_party/js-dossier 12 | url = https://github.com/jleyba/js-dossier 13 | -------------------------------------------------------------------------------- /src/login/img/pk-icon-username.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/db/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Morningstar Inc. All rights reserved. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | -------------------------------------------------------------------------------- /src/db/triggers/Before_Delete_On_Answers.txt: -------------------------------------------------------------------------------- 1 | before_delete_answers BEFORE DELETE ON Answers 2 | BEGIN 3 | INSERT into Audit (qid, app_status, app_tid, app_pid, app_name, app_champion, app_team_email, user_name, timestamp, answer, comments, action_items, submitter_email) values(OLD.qid, OLD.app_status, OLD.app_tid, OLD.app_pid, OLD.app_name, OLD.app_champion, OLD.app_team_email, OLD.user_name, OLD.timestamp, OLD.answer, OLD.comments, OLD.action_items, OLD.submitter_email); 4 | 5 | INSERT into Audit (qid, app_status, app_tid, app_pid, app_name, app_champion, app_team_email, user_name, timestamp) values(OLD.qid, 'DELETED', OLD.app_tid, OLD.app_pid, OLD.app_name, OLD.app_champion, OLD.app_team_email, 'ADMIN', OLD.timestamp); 6 | END -------------------------------------------------------------------------------- /src/login/img/pk-icon-password.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This file lists all individuals having contributed content to the repository. 2 | # This file is distinct from the CONTRIBUTING files. 3 | # See the latter for an explanation. 4 | 5 | # Names should be added to this file as: 6 | # email address (Name or Organization) 7 | # The email address is not required for organizations. 8 | 9 | dfabian@google.com (Daniel Fabian) 10 | lwe@google.com (Lukas Weichselbaum) 11 | lcamtuf@google.com (Michal Zalewski) 12 | ruslanh@google.com (Ruslan Habalov) 13 | koto@google.com (Krzysztof Kotowicz) 14 | carstein@google.com (Michal Melewski) 15 | cri@google.com (Chris John Riley) 16 | Praveen.Jha@morningstar.com (Praveen Kumar Jha) 17 | Brian.Cameron@morningstar.com (Brian Cameron) 18 | -------------------------------------------------------------------------------- /src/dbHandler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Morningstar Inc. All rights reserved. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | class DBHandler(object): 15 | 16 | def __init__(self, app=None): 17 | super(DBHandler, self) 18 | -------------------------------------------------------------------------------- /src/fileSystemHandler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Morningstar Inc. All rights reserved. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | class FileSystemHandler(object): 15 | 16 | def __init__(self, app=None): 17 | super(FileSystemHandler, self) 18 | -------------------------------------------------------------------------------- /src/randomizer.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Morningstar Inc. All rights reserved. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | import random 15 | import string 16 | 17 | def Id(size=12, chars=string.ascii_uppercase + string.digits): 18 | """Generates a random identifier""" 19 | token = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(size)) 20 | return token 21 | -------------------------------------------------------------------------------- /src/nic.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Morningstar Inc. All rights reserved. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | 15 | import socket 16 | 17 | class NIC(object): 18 | 19 | def getNetworkIP(self): 20 | for res in socket.getaddrinfo(socket.gethostname(),'http',socket.AF_INET, 0, socket.IPPROTO_TCP): 21 | family, socktype, proto, canonicalname, sockaddr = res 22 | ip, port = sockaddr 23 | return ["127.0.0.1", str(ip)] 24 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/questionnaire_editor_init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Initialize the questionnaire editor here. 19 | */ 20 | 21 | goog.require('vsaq.QuestionnaireEditor'); 22 | 23 | // Initialize the VSAQ WYSIWYG editor. 24 | if (!goog.getObjectByName('goog.testing.TestRunner')) 25 | new vsaq.QuestionnaireEditor(); 26 | -------------------------------------------------------------------------------- /src/compiler.flags: -------------------------------------------------------------------------------- 1 | --jscomp_error=accessControls 2 | --jscomp_error=ambiguousFunctionDecl 3 | --jscomp_error=checkDebuggerStatement 4 | --jscomp_error=checkEventfulObjectDisposal 5 | --jscomp_error=checkRegExp 6 | --jscomp_error=checkTypes 7 | --jscomp_error=checkVars 8 | --jscomp_error=const 9 | --jscomp_error=constantProperty 10 | --jscomp_error=deprecated 11 | --jscomp_error=duplicate 12 | --jscomp_error=duplicateMessage 13 | --jscomp_error=es5Strict 14 | --jscomp_error=es3 15 | --jscomp_error=externsValidation 16 | --jscomp_error=globalThis 17 | --jscomp_error=internetExplorerChecks 18 | --jscomp_error=invalidCasts 19 | --jscomp_error=missingProperties 20 | --jscomp_error=nonStandardJsDocs 21 | --jscomp_error=strictModuleDepCheck 22 | --jscomp_error=undefinedNames 23 | --jscomp_error=undefinedVars 24 | --jscomp_error=unknownDefines 25 | --jscomp_error=uselessCode 26 | --jscomp_error=visibility 27 | --externs=third_party/closure-compiler/contrib/externs/chrome_extensions.js 28 | --only_closure_dependencies 29 | --manage_closure_dependencies 30 | --js third_party/closure-library/closure/goog/deps.js 31 | --js build/deps.js 32 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/questionnaire_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/items_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/utils_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/boxitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/infoitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/lineitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/tipitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/blockitems_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/checkitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/groupitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/radioitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/spaceritem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/uploaditem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/yesnoitem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/containeritem_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/config_reader.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Morningstar Inc. All rights reserved. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | import ConfigParser, sys 15 | 16 | config = ConfigParser.ConfigParser() 17 | 18 | def getSections(config_file): 19 | sections = [] 20 | config.read(config_file) 21 | for section in config.sections(): 22 | sections.append(section) 23 | return sections; 24 | 25 | def getKeyValueForSection(config_file, inSection, sectionName): 26 | sections = getSections(config_file) 27 | for section in sections: 28 | if section == inSection: 29 | print "declare -A %s" % (sectionName) 30 | for key, val in config.items(section): 31 | print '%s[%s]="%s"' % (sectionName, key, val) 32 | return 33 | -------------------------------------------------------------------------------- /src/customflask.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Morningstar Inc. All rights reserved. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | from flask import Flask 15 | from flask import Response 16 | 17 | SERVER_NAME = 'PK' 18 | METHODS_NOT_ALLOWED = "NOT ALLOWED" 19 | 20 | class customFlask(Flask): 21 | def process_response(self, response): 22 | response.headers['Server'] = SERVER_NAME 23 | if response.headers.get('Allow') is not None: 24 | response.headers['Allow'] = METHODS_NOT_ALLOWED 25 | response.headers['X-Frame-Options'] = 'deny' 26 | response.headers['X-XSS-Protection'] = '1; mode=block' 27 | response.headers['X-Content-Type-Options'] = 'nosniff' 28 | response.headers['Content-Security-Policy'] = "default-src 'self'; object-src 'none'; img-src 'self'; script-src 'self' 'unsafe-eval' https://www.google.com/js/maia.js; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://www.google.com/css/maia.css; font-src https://fonts.gstatic.com;" 29 | super(customFlask, self).process_response(response) 30 | return(response) 31 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/questionnaire_editor_test_dom.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | Unit Test of Closure-Using Code 19 | 21 | 22 | 25 | 26 | 27 | 28 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/externs.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @license 4 | * Copyright 2016 Google Inc. All rights reserved. 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | /** 19 | * @fileoverview Externs for questionnaire. 20 | * @externs 21 | */ 22 | 23 | 24 | /** @type {Object} */ 25 | var qjson = {}; 26 | 27 | 28 | /** 29 | * @typedef {{ 30 | * auth: string, 31 | * choices: Object., 32 | * choicesConds: Object., 33 | * className: string, 34 | * cond: string, 35 | * customTitle: string, 36 | * default: string, 37 | * defaultChoice: boolean, 38 | * id: string, 39 | * inputType: string, 40 | * inputPattern: string, 41 | * inputTitle: string, 42 | * maxlength: number, 43 | * name: string, 44 | * no: string, 45 | * placeholder: string, 46 | * required: boolean, 47 | * severity: string, 48 | * style: string, 49 | * text: string, 50 | * todo: string, 51 | * type: string, 52 | * warn: string, 53 | * why: string, 54 | * yes: string 55 | * }} 56 | */ 57 | qjson.QuestionnaireItem; 58 | -------------------------------------------------------------------------------- /src/questionnaires/test_template.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "questionnaire": [ 4 | { 5 | "type": "block", 6 | "text": "My questionnaire", 7 | "items": [ 8 | { 9 | "type": "block", 10 | "id": "network", 11 | "text": "Network section", 12 | "items": [ 13 | { 14 | "type": "info", 15 | "text": "What security standards do you apply?" 16 | }, 17 | { 18 | "type": "checkgroup", 19 | "defaultChoice": false, 20 | "choices": [ 21 | {"standard_1": "Standard 1"}, 22 | {"standard_2": "Standard 2"} 23 | ] 24 | } 25 | ] 26 | }, 27 | { 28 | "type": "block", 29 | "text": "Data section", 30 | "items": [ 31 | { 32 | "type": "check", 33 | "id": "data_warning", 34 | "text": "Click me to trigger a warning." 35 | }, 36 | { 37 | "type": "tip", 38 | "id": "data_tip", 39 | "cond": "data_warning/yes", 40 | "text": "You shouldn't click the box above.", 41 | "warn": "yes", 42 | "severity": "critical" 43 | } 44 | ] 45 | }, 46 | { 47 | "type": "block", 48 | "text": "Final section", 49 | "items": [ 50 | { 51 | "type": "check", 52 | "id": "declaration", 53 | "text": "I hereby declare that the above answers are true to the best of my knowledge and belief." 54 | } 55 | ] 56 | } 57 | ] 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/infoitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.InfoItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.InfoItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.InfoItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.testing.jsunit'); 27 | goog.require('vsaq.questionnaire.items.InfoItem'); 28 | 29 | var CAPTION = 'infoitem_caption'; 30 | var ID = 'infoitem_id'; 31 | 32 | var info; 33 | 34 | 35 | /** 36 | * Initializes variables used by all tests. 37 | */ 38 | function setUp() { 39 | info = new vsaq.questionnaire.items.InfoItem(ID, [], CAPTION); 40 | } 41 | 42 | 43 | /** 44 | * Tests whether info items are rendered correctly. 45 | */ 46 | function testInfoItem() { 47 | var el = info.container; 48 | 49 | assertEquals(String(goog.dom.TagName.DIV), el.tagName); 50 | assertEquals(ID, el.id); 51 | assertEquals(CAPTION, goog.dom.getTextContent(el)); 52 | } 53 | 54 | 55 | /** 56 | * Tests parsing of InfoItems. 57 | */ 58 | function testInfoItemParse() { 59 | var testStack = [{ 60 | 'type': 'info', 61 | 'text': CAPTION, 62 | 'id': ID 63 | }]; 64 | info = vsaq.questionnaire.items.InfoItem.parse(testStack); 65 | assert(info instanceof vsaq.questionnaire.items.InfoItem); 66 | assertEquals(ID, info.id); 67 | assertEquals(CAPTION, info.info); 68 | assertEquals(0, testStack.length); 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/spaceritem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.SpacerItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.SpacerItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.SpacerItemTests'); 23 | 24 | goog.require('goog.dom.TagName'); 25 | goog.require('goog.dom.classlist'); 26 | goog.require('goog.testing.asserts'); 27 | goog.require('goog.testing.jsunit'); 28 | goog.require('vsaq.questionnaire.items.SpacerItem'); 29 | 30 | var ID = 'spaceritem_id'; 31 | 32 | var spacer; 33 | 34 | 35 | /** 36 | * Initializes variables used by all tests. 37 | */ 38 | function setUp() { 39 | spacer = new vsaq.questionnaire.items.SpacerItem(ID, []); 40 | } 41 | 42 | 43 | /** 44 | * Tests whether spacer items are rendered correctly. 45 | */ 46 | function testSpacerItem() { 47 | var el = spacer.container; 48 | 49 | assertEquals(String(goog.dom.TagName.DIV), el.tagName); 50 | assertNotUndefined(el.firstChild); 51 | assertEquals(String(goog.dom.TagName.HR), el.firstChild.tagName); 52 | assertTrue(goog.dom.classlist.contains(el.firstChild, 'vsaq-spacer')); 53 | } 54 | 55 | 56 | /** 57 | * Tests parsing of SpacerItems. 58 | */ 59 | function testSpacerItemParse() { 60 | var testStack = [{ 'type': 'spacer' }]; 61 | spacer = vsaq.questionnaire.items.SpacerItem.parse(testStack); 62 | assert(spacer instanceof vsaq.questionnaire.items.SpacerItem); 63 | assertEquals(0, testStack.length); 64 | } 65 | 66 | -------------------------------------------------------------------------------- /src/translator.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Morningstar Inc. All rights reserved. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | import logging, os.path, re 15 | 16 | class Translator(object): 17 | """Translates the request paths to the local directory path""" 18 | DIRECTORY_MAP = { 19 | "/": "build/", 20 | "/login/": "build/login/", 21 | "/login/css/": "build/login/css/", 22 | "/login/js/": "build/login/js/", 23 | "/login/img/": "build/login/img/", 24 | "/static/": "build/static/", 25 | "/vsaq/": "vsaq/", 26 | "/vsaq/static/questionnaire/": "vsaq/static/questionnaire/", 27 | "/javascript/closure/": "third_party/closure-library/closure/goog/", 28 | "/javascript/vsaq/": "vsaq/", 29 | "/third_party/closure/": 30 | "third_party/closure-library/third_party/closure/", 31 | "/third_party/closure-templates-compiler/": 32 | "third_party/closure-templates-compiler/", 33 | "/build/templates/vsaq/static/questionnaire/": 34 | "build/templates/vsaq/static/questionnaire/" 35 | } 36 | 37 | def translate_path(self, path): 38 | """Serves files from different directories.""" 39 | # Remove all parameters from filenames. 40 | path = re.sub(r"\?.*$", "", path) 41 | for prefix, dest_dir in Translator.DIRECTORY_MAP.items(): 42 | translatedPath = dest_dir + path[len(prefix):] 43 | if path.startswith(prefix) and os.path.isfile(translatedPath): 44 | logging.debug("Translator.translate_path:%s", translatedPath) 45 | return translatedPath 46 | logging.debug("Translator.translate_path:NOT_FOUND:%s. Returned: %s", path, "build/index.html") 47 | return "build/index.html" 48 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/containeritem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.ContainerItem 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.ContainerItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.ContainerItemTests'); 23 | 24 | goog.require('goog.array'); 25 | goog.require('goog.testing.jsunit'); 26 | goog.require('vsaq.questionnaire.items.CheckItem'); 27 | goog.require('vsaq.questionnaire.items.ContainerItem'); 28 | 29 | var CAPTION = 'test_caption'; 30 | var ID = 'test_id'; 31 | var ADMIN = 'admin'; 32 | 33 | var NUMBER_CONTAINER_ITEMS = 5; 34 | 35 | var container; 36 | 37 | 38 | /** 39 | * Initializes variables used by all tests. 40 | */ 41 | function setUp() { 42 | container = new vsaq.questionnaire.items.ContainerItem(ID, [], ADMIN, null); 43 | for (var i = 0; i < NUMBER_CONTAINER_ITEMS; i++) { 44 | var checkItem = new vsaq.questionnaire.items.CheckItem(ID + i, [], 45 | CAPTION + i); 46 | container.addItem(checkItem); 47 | } 48 | } 49 | 50 | 51 | /** 52 | * Tests whether containers are storing items correctly. 53 | */ 54 | function testContainerItem() { 55 | assertEquals(container.containerItems.length, NUMBER_CONTAINER_ITEMS); 56 | // Test if after deletion each gap was correctly closed again. 57 | var containedItems = []; 58 | goog.array.forEach(container.containerItems, function(item) { 59 | containedItems.push(item); 60 | }); 61 | for (var i = 0; i < NUMBER_CONTAINER_ITEMS; i++) 62 | container.deleteItem(containedItems[i]); 63 | assertEquals(container.containerItems.length, 0); 64 | } 65 | -------------------------------------------------------------------------------- /src/cryptoUtils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Morningstar Inc. All rights reserved. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | import base64, logging, os 15 | 16 | from base64 import b64encode, b64decode 17 | from Crypto.Cipher import AES 18 | from flask import session 19 | 20 | class Cryptor(object): 21 | 22 | def encrypt(self, data): 23 | BLOCK_SIZE = 32 24 | # the character used for padding 25 | # used to ensure that your value is always a multiple of BLOCK_SIZE 26 | PADDING = '{' 27 | encoded = '' 28 | try: 29 | pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING 30 | EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s))) 31 | secret = os.urandom(BLOCK_SIZE) 32 | secretToken = b64encode(secret).decode('utf-8') 33 | logging.debug("Secret Key: %s for user %s generated",secretToken, session.get('_user')) 34 | cipher = AES.new(secret, AES.MODE_ECB) 35 | encoded = EncodeAES(cipher, data) 36 | logging.debug('Encrypted string:%s', encoded) 37 | session['_eUser'] = encoded 38 | session['_eUserK'] = secretToken 39 | except Exception as e: 40 | logging.error('Cryptor.encrypt:Error while encrypting:' + data + ' :\n\n' + 41 | repr(e), exc_info=True) 42 | return encoded 43 | 44 | def decrypt(self, encryptedString): 45 | PADDING = '{' 46 | decoded = '' 47 | try: 48 | DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING) 49 | secretToken = session.get('_eUserK') 50 | secret = b64decode(secretToken.encode('utf-8')) 51 | cipher = AES.new(secret, AES.MODE_ECB) 52 | decoded = DecodeAES(cipher, encryptedString) 53 | except Exception as e: 54 | logging.error('Cryptor.decrypt:Error while decrypting:' + str(encryptedString) + ' :\n\n' + 55 | repr(e), exc_info=True) 56 | return decoded 57 | -------------------------------------------------------------------------------- /src/client_side_only_impl/index.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | [APPLICATION_TITLE] 22 | 23 | 24 | 25 | 26 | 34 | 35 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |

48 |

Security

49 | 52 |

[DR_HOME_LABEL]

53 | 57 |
58 |
59 |
60 | 61 |
62 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /src/rename_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2018 Morningstar Inc. All rights reserved. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Running this shell script will rename the json files containing the answers 16 | # in the following format: 17 | # ___.json 18 | # The script also removes any spaces from the subdirectories and replaces them with underscores. 19 | # The renamed files will be copied into "GOASQ_Updated" directory along side the directory from 20 | # where this script is run. 21 | # CAUTION: In the re-run, it won't delete any previously generated files. 22 | # Requires jq as dependency for parsing JSON files. 23 | 24 | PARENT_DIRECTORY=$1 25 | NEW_DIRECTORY_NAME="GOASQ_Updated" 26 | # Make sure that files and folders do not have spaces and new lines 27 | find ./${PARENT_DIRECTORY} -name "* *" -print0 | sort -rz | while read -d $'\0' f; do mv -v "$f" "$(dirname "$f")/$(basename "${f// /_}")"; done 28 | # Get most recently modified files and rename them appropriately 29 | DIRECTORIES=`find ./${PARENT_DIRECTORY} -type d -follow` 30 | for dir in $DIRECTORIES 31 | do 32 | FILE=`find $dir -type f -exec stat -f '%m%t%Sm %N' {} \; | sort -nr -u | cut -d . -f2- | head -1` 33 | FILE_NAME=$(basename $FILE) 34 | if [ "$FILE_NAME" != ".DS_Store" ]; then 35 | METADATA=`cat .${FILE} | jq '{name:.app_name,champion:.app_champion,email:.app_team_email}'` 36 | UNIQUE_ID=`cat /dev/urandom | env LC_CTYPE=C tr -dc 'A-Z0-9' | fold -w 12 | head -n 1` 37 | APP_NAME=`echo $METADATA | jq .name | sed 's/"//g' | sed 's/[\/]/ /g'` 38 | APP_CHAMPION=`echo $METADATA | jq .champion | sed 's/"//g' | sed 's/[;,\/<>]/ /g'` 39 | TEAM_CONTACT=`echo $METADATA | jq .email | sed 's/"//g' | sed 's/[;,\/<>]/ /g'` 40 | PADDED_FILENAME=`printf " %-60s%s" "$FILE_NAME"` 41 | echo "File will be renamed from: ${PADDED_FILENAME} to: ${UNIQUE_ID}_${APP_NAME}_${APP_CHAMPION}_${TEAM_CONTACT}.json" 42 | mkdir -p "./$NEW_DIRECTORY_NAME/$dir" 43 | yes | \cp ".${FILE}" "./${NEW_DIRECTORY_NAME}/$dir/${UNIQUE_ID}_${APP_NAME}_${APP_CHAMPION}_${TEAM_CONTACT}.json" 44 | fi 45 | done 46 | -------------------------------------------------------------------------------- /src/vsaq/static/dialoghelper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview A helper to handle dialogs. The reason goog.ui.dialog is not 19 | * used is that it makes it fairly hard to apply custom styling. 20 | */ 21 | 22 | goog.provide('vsaq.helpers.Dialog'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.events'); 27 | goog.require('goog.events.EventType'); 28 | goog.require('goog.soy'); 29 | 30 | 31 | 32 | /** 33 | * Constructor for dialogs. 34 | * @param {!Element} place Element where the dialog can be inserted into the 35 | * DOM. 36 | * @param {Function} template A soy template for the dialog. 37 | * @param {Object=} opt_parameters Parameters passed to the soy template. 38 | * @param {Function=} opt_clickCallback Function called when anything in the 39 | * dialog is clicked. 40 | * @constructor 41 | */ 42 | vsaq.helpers.Dialog = function(place, template, opt_parameters, 43 | opt_clickCallback) { 44 | this.element_ = goog.soy.renderAsElement(template, opt_parameters); 45 | this.backdrop_ = 46 | goog.dom.createDom(goog.dom.TagName.DIV, 'vsaq-overlay-backdrop'); 47 | goog.dom.appendChild(place, this.element_); 48 | goog.dom.appendChild(place, this.backdrop_); 49 | if (opt_clickCallback) 50 | goog.events.listen(this.element_, [goog.events.EventType.CLICK], 51 | opt_clickCallback); 52 | }; 53 | 54 | 55 | /** 56 | * Shows or hides the dialog. 57 | * @param {boolean} isVisible Whether the dialog should be made visible or 58 | * invisible. 59 | */ 60 | vsaq.helpers.Dialog.prototype.setVisible = function(isVisible) { 61 | this.element_.style.display = isVisible ? 'block' : 'none'; 62 | this.backdrop_.style.display = isVisible ? 'block' : 'none'; 63 | this.element_.style.marginTop = -this.element_.clientHeight / 2 + 'px'; 64 | this.element_.style.marginLeft = -this.element_.clientHeight / 2 + 'px'; 65 | }; 66 | 67 | 68 | /** 69 | * Disposes the dialog. 70 | */ 71 | vsaq.helpers.Dialog.prototype.dispose = function() { 72 | this.setVisible(false); 73 | goog.dom.removeNode(this.element_); 74 | goog.dom.removeNode(this.backdrop_); 75 | }; 76 | -------------------------------------------------------------------------------- /src/client_side_only_impl/example.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | VSAQ - Security Assessment Questionnaires 22 | 23 | 24 | 25 | 26 | 34 | 35 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |

Example Questionnaires

48 | 52 | 53 |

Tests

54 | 57 |
58 |
59 |
60 |
61 | 62 |
63 |
64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/boxitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.BoxItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.BoxItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.BoxItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.dom.classlist'); 27 | goog.require('goog.testing.asserts'); 28 | goog.require('goog.testing.jsunit'); 29 | goog.require('vsaq.questionnaire.items.BoxItem'); 30 | 31 | var CAPTION = 'boxitem_caption'; 32 | var ID = 'boxitem_id'; 33 | var VALUE = 'boxitem_value'; 34 | 35 | var box; 36 | 37 | 38 | /** 39 | * Initializes variables used by all tests. 40 | */ 41 | function setUp() { 42 | box = new vsaq.questionnaire.items.BoxItem(ID, [], CAPTION); 43 | } 44 | 45 | 46 | /** 47 | * Tests whether box items are rendered correctly. 48 | */ 49 | function testBoxItem() { 50 | var el = box.container; 51 | 52 | assertEquals(String(goog.dom.TagName.DIV), el.tagName); 53 | var desc = goog.dom.getFirstElementChild(el); 54 | assertTrue('Class not set', 55 | goog.dom.classlist.contains(desc, 'vsaq-question-title')); 56 | assertEquals(CAPTION, goog.dom.getTextContent(desc)); 57 | var area = goog.dom.getNextElementSibling(desc); 58 | assertEquals(ID, area.id); 59 | assertEquals('', area.value); 60 | } 61 | 62 | 63 | /** 64 | * Tests setting and retrieving the value of the item. 65 | */ 66 | function testBoxItemSetGetValue() { 67 | assertEquals('', box.getValue()); 68 | box.setValue(VALUE); 69 | assertEquals(VALUE, box.getValue()); 70 | } 71 | 72 | 73 | /** 74 | * Tests parsing of BoxItems. 75 | */ 76 | function testBoxItemParse() { 77 | var testStack = [{ 78 | 'type': 'box', 79 | 'text': CAPTION, 80 | 'id': ID, 81 | 'required' : true, 82 | 'placeholder': 'placeholder' 83 | }]; 84 | box = vsaq.questionnaire.items.BoxItem.parse(testStack); 85 | assert(box instanceof vsaq.questionnaire.items.BoxItem); 86 | assertEquals(ID, box.id); 87 | assertEquals(CAPTION, box.text); 88 | assertEquals('placeholder', box.placeholder); 89 | assertTrue(box.required); 90 | assertEquals(0, testStack.length); 91 | } 92 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/checkitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.CheckItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.CheckItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.CheckItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.testing.asserts'); 27 | goog.require('goog.testing.jsunit'); 28 | goog.require('vsaq.questionnaire.items.CheckItem'); 29 | 30 | var CAPTION = 'checkitem_caption'; 31 | var ID = 'checkitem_id'; 32 | var VALUE = 'checked'; 33 | 34 | var check; 35 | 36 | 37 | /** 38 | * Tests whether blocks are rendered correctly. 39 | */ 40 | function setUp() { 41 | check = new vsaq.questionnaire.items.CheckItem(ID, [], CAPTION); 42 | } 43 | 44 | 45 | /** 46 | * Helper function that returns true if string has suffix. 47 | * @param {String} str String that should be checked for the suffix. 48 | * @param {String} suffix The suffix the string should be checked for. 49 | * @return {bool} True if the string has the given suffix. 50 | */ 51 | function endsWith(str, suffix) { 52 | return str.indexOf(suffix, str.length - suffix.length) !== -1; 53 | } 54 | 55 | 56 | /** 57 | * Tests whether check items are rendered correctly. 58 | */ 59 | function testCheckItem() { 60 | var el = check.container; 61 | 62 | assertEquals(String(goog.dom.TagName.DIV), el.tagName); 63 | assertContains('vsaq-checkbox-item', el.className); 64 | assert(check.checkBox_ instanceof HTMLInputElement); 65 | var label = goog.dom.getLastElementChild(el); 66 | assert(endsWith(goog.dom.getTextContent(label), CAPTION)); 67 | } 68 | 69 | 70 | /** 71 | * Tests setting and retrieving the value of the item. 72 | */ 73 | function testCheckItemSetGetValue() { 74 | assertEquals('', check.getValue()); 75 | check.setValue(VALUE); 76 | assertEquals(VALUE, check.getValue()); 77 | } 78 | 79 | 80 | /** 81 | * Tests parsing of CheckItems. 82 | */ 83 | function testCheckItemParse() { 84 | var testStack = [{ 85 | 'type': 'check', 86 | 'text': CAPTION, 87 | 'id': ID 88 | }]; 89 | check = vsaq.questionnaire.items.CheckItem.parse(testStack); 90 | assert(check instanceof vsaq.questionnaire.items.CheckItem); 91 | assertEquals(ID, check.id); 92 | assertEquals(CAPTION, check.text); 93 | assertEquals(0, testStack.length); 94 | } 95 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/spaceritem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Questionnaire informational items. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.SpacerItem'); 22 | 23 | goog.require('goog.dom'); 24 | goog.require('goog.soy'); 25 | goog.require('vsaq.questionnaire.items.Item'); 26 | goog.require('vsaq.questionnaire.items.ParseError'); 27 | goog.require('vsaq.questionnaire.templates'); 28 | 29 | 30 | 31 | /** 32 | * An item that shows some information to the user. The information may contain 33 | * HTML. 34 | * @param {?string} id An ID uniquely identifying the item. 35 | * @param {?string} conditions A string containing conditions which must be met 36 | * for the item to be visible to the user. 37 | * @extends {vsaq.questionnaire.items.Item} 38 | * @constructor 39 | */ 40 | vsaq.questionnaire.items.SpacerItem = function(id, conditions) { 41 | goog.base(this, id, conditions); 42 | 43 | this.render(); 44 | }; 45 | goog.inherits(vsaq.questionnaire.items.SpacerItem, 46 | vsaq.questionnaire.items.Item); 47 | 48 | 49 | /** 50 | * Render the HTML for this item. 51 | */ 52 | vsaq.questionnaire.items.SpacerItem.prototype.render = function() { 53 | var oldNode = this.container; 54 | this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.spacer, 55 | {id: this.id}); 56 | goog.dom.replaceNode(this.container, oldNode); 57 | }; 58 | 59 | 60 | /** 61 | * Type of the question. This is used to distinguish questions in serialized 62 | * format. 63 | * @type {string} 64 | * @const 65 | */ 66 | vsaq.questionnaire.items.SpacerItem.TYPE = 'spacer'; 67 | 68 | 69 | /** 70 | * Parses SpacerItems. If the topmost item in the passed Array is a 71 | * SpacerItem, it is consumed and a SpacerItem instance is returned. 72 | * If the topmost item is not a SpacerItem, an exception is thrown. 73 | * @param {!Array.} questionStack Array of serialized 74 | * questionnaire Items. 75 | * @return {!vsaq.questionnaire.items.SpacerItem} The parsed SpacerItem. 76 | */ 77 | vsaq.questionnaire.items.SpacerItem.parse = function(questionStack) { 78 | var item = questionStack.shift(); 79 | if (item.type != vsaq.questionnaire.items.SpacerItem.TYPE) 80 | throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.'); 81 | 82 | return new vsaq.questionnaire.items.SpacerItem(item.id, item.cond); 83 | }; 84 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/lineitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.LineItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.LineItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.LineItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.testing.jsunit'); 27 | goog.require('vsaq.questionnaire.items.LineItem'); 28 | 29 | var CAPTION = 'lineitem_caption'; 30 | var ID = 'lineitem_id'; 31 | var VALUE = 'lineitem_value'; 32 | 33 | var line; 34 | 35 | 36 | /** 37 | * Initializes variables used by all tests. 38 | */ 39 | function setUp() { 40 | line = new vsaq.questionnaire.items.LineItem(ID, [], CAPTION); 41 | } 42 | 43 | 44 | /** 45 | * Tests whether line items are rendered correctly. 46 | */ 47 | function testLineItem() { 48 | var el = line.container; 49 | 50 | assertEquals(String(goog.dom.TagName.DIV), el.tagName); 51 | var desc = goog.dom.getFirstElementChild(el); 52 | assertEquals(CAPTION, goog.dom.getTextContent(desc)); 53 | var input = goog.dom.getNextElementSibling(desc); 54 | assertEquals(String(goog.dom.TagName.INPUT), input.tagName); 55 | assertEquals('', input.value); 56 | assertEquals(ID, input.id); 57 | } 58 | 59 | 60 | /** 61 | * Tests setting and retrieving the value of the item. 62 | */ 63 | function testLineItemSetGetValue() { 64 | assertEquals('', line.getValue()); 65 | line.setValue(VALUE); 66 | assertEquals(VALUE, line.getValue()); 67 | } 68 | 69 | 70 | /** 71 | * Tests parsing of LineItems. 72 | */ 73 | function testLineItemParse() { 74 | var testStack = [{ 75 | 'type': 'line', 76 | 'text': CAPTION, 77 | 'id': ID, 78 | 'required' : true, 79 | 'inputPattern': '.*', 80 | 'inputTitle': 'input_title', 81 | 'inputType': 'date', 82 | 'placeholder': 'placeholder', 83 | 'maxlength': 100, 84 | }]; 85 | line = vsaq.questionnaire.items.LineItem.parse(testStack); 86 | assert(line instanceof vsaq.questionnaire.items.LineItem); 87 | assertEquals(ID, line.id); 88 | assertEquals(CAPTION, line.text); 89 | assertEquals(0, testStack.length); 90 | assertEquals('input_title', line.inputTitle); 91 | assertEquals('placeholder', line.placeholder); 92 | assertEquals(100, line.maxlength); 93 | assertEquals('.*', line.inputPattern); 94 | assertEquals('date', line.inputType); 95 | assertTrue(line.required); 96 | } 97 | -------------------------------------------------------------------------------- /src/placeholders.compile.cfg: -------------------------------------------------------------------------------- 1 | [app.config] 2 | APPLICATION_TITLE=General Open Architecture Security Questionnaire (GoASQ) 3 | 4 | [app.config.debug] 5 | APPLICATION_TITLE=General Open Architecture Security Questionnaire (GoASQ) 6 | 7 | [build/index.html] 8 | APPLICATION_TITLE=General Open Architecture Security Questionnaire 9 | APPLICATION_ACRONYM=GoASQ 10 | APPSEC_HOME_HREF=\/vsaq.html?qpath=questionnaires\/mstar_0_1.json 11 | DR_HOME_HREF=\/ 12 | DR_SHORT_HOME_HREF=\/ 13 | DR_HOME_LABEL=Disaster Recovery 14 | DR_FULL_QUESTIONNAIRE_HREF_LABEL=Disaster Recovery Questionnaire v0.1 15 | DR_SHORT_QUESTIONNAIRE_HREF_LABEL=Disaster Recovery Questionnaire - Short v0.1 16 | QUESTIONNAIRE_SHORT_TITLE_AND_VERSION=Application Security Questionnaire Version 0.1 17 | 18 | [build/vsaq.html] 19 | APPLICATION_TITLE=General Open Architecture Security Questionnaire 20 | APPLICATION_ACRONYM=GoASQ 21 | TEAM_CONTACT=Champion 22 | TEAM_IDENTIFIER_LABEL=Team ID 23 | PROJECT_IDENTIFIER_LABEL=Project ID 24 | 25 | [build/vsaq_editor.html] 26 | APPLICATION_TITLE=General Open Architecture Security Questionnaire 27 | APPLICATION_ACRONYM=GoASQ 28 | PAGE_TITLE=General Open Architecture Security Questionnaire Editor (alpha) 29 | 30 | [build/vsaq_binary.js] 31 | BUG_TRACKER_FILTER_HREF=https:\/\/bugtracker.example.com\/issues\/?query 32 | BUG_TRACKER_FILTER_QUERY=project%20%3D%20MyProject%20and%20%22Severity%22%20%20in%20(Critical%2C%20High)%20and%20resolution%3DUnresolved%20and%20%22Application%20Name%22%20%3D%20%22INSERT_APP_NAME_HERE%22 33 | TEAM_CONTACT_EMAIL_HREF=mailto:YourSecurityTeam@Organization.com 34 | TEAM_CONTACT_EMAIL=TEAM_DEVELOPER_EMAIL 35 | EMAIL_SUBJECT_ARCHITECTURE_REVIEW=Completed Architecture Review for APPLICATION_NAME 36 | BUG_TRACKER_URL=https:\/\/bugtracker.example.com\/issues\/ 37 | QUESTIONNAIRE_PATH_PARAMETER=\/vsaq.html?qpath=questionnaires\/mstar_0_1.json\&q= 38 | 39 | [build/questionnaires/mstar_0_1.json] 40 | QUESTIONNAIRE_TITLE_AND_VERSION=General Open Architecture Security Questionnaire Version 0.1 41 | PROJECT_SECURITY_CHAMPION_CONTACT_DOCUMENTATION_HREF=Security Champion 42 | ORGANIZATION_NAME=Organization 43 | ORGANIZATION_SECURITY_POLICIES_HREF=Organization Information Security Policies and Standards 44 | ORGANIZATION_TRAINING_RESOURCES_HREF=relevant resources 45 | BUG_TRACKER_SYSTEM_NAME=Bug Tracker 46 | ORGANIZATION_AWS_RESOURCES_HREF=AWS resources 47 | ORGANIZATION_SECURITY_LANDSCAPE_REFERENCE_HREF=security landscape has changed 48 | PROJECT_UNRESOLVED_BUGS_HREF=open critical or high severity vulnerabilities<\/a> 49 | ORGANIZATION_APPSEC_EMAIL_HREF=YourSecurityTeam@Organization.com<\/a> 50 | ORGANIZATION_VAULT_LABEL=secret.server.at.your.organization.com 51 | ORGANIZATION_LOGGING_STANDARDS_HREF=Organization Application Logging Standard 52 | ORGANIZATION_WEB_SERVER_LOGGING_STANDARDS_HREF=web server logging requirements 53 | ORGANIZATION_ENVIRONMENT_STRATEGY_HREF=Organization Environment Strategy 54 | ORGANIZATION_PATCH_MANAGEMENT_POLICY_HREF=Patch Management Policy 55 | TEAM_CONTACT_EMAIL_HREF=mailto:YourSecurityTeam@Organization.com 56 | DB_CONNECTION_BEST_PRACTICE_HREF= 57 | ORGANIZATION_RECOMMENDED_AUTH_SOLUTION_NAME=authentication solution recommended by organization 58 | MAXLENGTH_BOX_ITEM=2000 59 | MAXLENGTH_LINE_ITEM=100 60 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/yesnoitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.YesNoItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.YesNoItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.YesNoItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.dom.classlist'); 27 | goog.require('goog.testing.asserts'); 28 | goog.require('goog.testing.jsunit'); 29 | goog.require('vsaq.questionnaire.items.YesNoItem'); 30 | 31 | var CAPTION = 'yesnoitem_caption'; 32 | var ID = 'yesnoitem_id'; 33 | var VALUE = 'yes'; 34 | var YES = 'yes_caption'; 35 | var NO = 'no_caption'; 36 | 37 | var yesno; 38 | 39 | 40 | /** 41 | * Initializes variables used by all tests. 42 | */ 43 | function setUp() { 44 | yesno = new vsaq.questionnaire.items.YesNoItem(ID, [], CAPTION, YES, NO); 45 | } 46 | 47 | 48 | /** 49 | * Tests whether yesno items are rendered correctly. 50 | */ 51 | function testYesNoItem() { 52 | var el = yesno.container; 53 | 54 | assertEquals(el.tagName, String(goog.dom.TagName.DIV)); 55 | assertTrue(goog.dom.classlist.contains(el, 'vsaq-yesno-block')); 56 | 57 | var desc = goog.dom.getFirstElementChild(el); 58 | assertEquals(String(goog.dom.TagName.DIV), desc.tagName); 59 | assertTrue(goog.dom.classlist.contains(desc, 'vsaq-question-title')); 60 | assertEquals(CAPTION, goog.dom.getTextContent(desc)); 61 | 62 | var label = yesno.yesRadio_.parentNode.getElementsByTagName('span')[0]; 63 | assertContains(YES, goog.dom.getTextContent(label)); 64 | assertFalse(yesno.yesRadio_.checked); 65 | 66 | label = yesno.noRadio_.parentNode.getElementsByTagName('span')[0]; 67 | assertContains(NO, goog.dom.getTextContent(label)); 68 | assertFalse(yesno.noRadio_.checked); 69 | } 70 | 71 | 72 | /** 73 | * Tests setting and retrieving the value of the item. 74 | */ 75 | function testYesNoItemSetGetValue() { 76 | assertEquals('', yesno.getValue()); 77 | yesno.setValue(VALUE); 78 | assertEquals(VALUE, yesno.getValue()); 79 | assertTrue(yesno.yesRadio_.checked); 80 | assertFalse(yesno.noRadio_.checked); 81 | assertTrue(yesno.isChecked()); 82 | } 83 | 84 | 85 | /** 86 | * Tests parsing of YesNoItems. 87 | */ 88 | function testYesNoItemParse() { 89 | var testStack = [{ 90 | 'type': 'yesno', 91 | 'text': CAPTION, 92 | 'id': ID, 93 | 'yes': YES, 94 | 'no': NO 95 | }]; 96 | yesno = vsaq.questionnaire.items.YesNoItem.parse(testStack); 97 | assert(yesno instanceof vsaq.questionnaire.items.YesNoItem); 98 | assertEquals(ID, yesno.id); 99 | assertEquals(CAPTION, yesno.text); 100 | assertEquals(YES, yesno.yes); 101 | assertEquals(NO, yesno.no); 102 | assertEquals(0, testStack.length); 103 | 104 | assert(yesno.yesRadio_ instanceof HTMLInputElement); 105 | assert(yesno.noRadio_ instanceof HTMLInputElement); 106 | } 107 | 108 | -------------------------------------------------------------------------------- /src/scripts/vsaq_test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Copyright 2018 Morningstar Inc. All rights reserved. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # vsaq_test 16 | # By; Brian Cameron (brian.cameron@morningstar.com) 17 | # 18 | # A simple Perl program that validates a VSAQ Questionnaire JSON file 19 | # reporting about common errors, including: 20 | # - An "id" value not being unique. 21 | # - An id (choice) used in a combo/check widget not being unique. 22 | # - A "cond" value pointing to an invalid "id" value. 23 | # 24 | # Usage: vsaq_test (VSAQ JSON filename) 25 | # - The filename should correspond to the VSAQ JSON file to check. 26 | # 27 | use warnings; 28 | 29 | my $idhash = {}; 30 | my $choicehash = {}; 31 | my $error_flag = 0; 32 | 33 | if (!$ARGV[0]) { 34 | print "\nUsage: checkjson (VSAQ JSON filename)\n\n"; 35 | exit; 36 | } 37 | 38 | my $filename = $ARGV[0]; 39 | if (! -f $filename) { 40 | print "\nFilename \"$filename\" does not exist.\n\n"; 41 | exit; 42 | } 43 | 44 | my $line_cnt = 1; 45 | open (my $fh, '<:encoding(UTF-8)', $filename) or die "Could not open file $filename' $!"; 46 | while (my $line = <$fh>) { 47 | chomp $line; 48 | if ($line =~ /\"id\"/) { 49 | my $idval = $line; 50 | $idval =~ s/^.*:\s*//g; 51 | $idval =~ s/[",]//g; 52 | if ($idhash{$idval}) { 53 | if ($error_flag == 0) { 54 | print "\n"; 55 | } 56 | print "ERROR (line $line_cnt): The ID #$idval# is used more than once.\n"; 57 | $error_flag=1; 58 | } 59 | $idhash{$idval} = 1; 60 | } 61 | if ($line =~ /[{]\".*\":\s*\".*\"[}]/) { 62 | my $choiceval = $line; 63 | $choiceval =~ s/\":\s*\".*\"[}]//g; 64 | $choiceval =~ s/^\s*[{]\"//g; 65 | $choiceval =~ s/,//g; 66 | $choiceval =~ s/\s*$//g; 67 | if ($choicehash{$choiceval}) { 68 | if ($error_flag == 0) { 69 | print "\n"; 70 | } 71 | print "ERROR (line $line_cnt): The choice #$choiceval# is used more than once.\n"; 72 | $error_flag=1; 73 | } 74 | $choicehash{$choiceval} = 1; 75 | } 76 | $line_cnt += 1; 77 | } 78 | close ($fh); 79 | 80 | $line_cnt = 1; 81 | open (my $fh2, '<:encoding(UTF-8)', $filename) or die "Could not open file $filename' $!"; 82 | while (my $line = <$fh2>) { 83 | chomp $line; 84 | if ($line =~ /\"cond\"/) { 85 | my $condval = $line; 86 | $condval =~ s/.*:\s*//g; 87 | $condval =~ s/[",!()]//g; 88 | $condval =~ s/&&//g; 89 | $condval =~ s/\|\|//g; 90 | $condval =~ s/\s+/ /g; 91 | 92 | my @words = split / /, $condval; 93 | foreach $word (@words) { 94 | if (! $choicehash{$word}) { 95 | if ($error_flag == 0) { 96 | print "\n"; 97 | } 98 | print "ERROR (line $line_cnt): The condition #$word# is not defined.\n"; 99 | $error_flag=1; 100 | } 101 | } 102 | } 103 | $line_cnt += 1; 104 | } 105 | close ($fh2); 106 | 107 | if ($error_flag == 0) { 108 | print "\nNo errors found in file \"$filename\".\n"; 109 | } 110 | print "\n"; 111 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/infoitem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Questionnaire informational items. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.InfoItem'); 22 | 23 | goog.require('goog.dom'); 24 | goog.require('goog.soy'); 25 | goog.require('goog.string'); 26 | goog.require('vsaq.questionnaire.items.Item'); 27 | goog.require('vsaq.questionnaire.items.ParseError'); 28 | goog.require('vsaq.questionnaire.templates'); 29 | 30 | 31 | 32 | /** 33 | * An item that shows some information to the user. The information may contain 34 | * HTML. 35 | * @param {?string} id An ID uniquely identifying the item. 36 | * @param {?string} conditions A string containing conditions which must be met 37 | * for the item to be visible to the user. 38 | * @param {?string} info The information that should be shown to the user. 39 | * @extends {vsaq.questionnaire.items.Item} 40 | * @constructor 41 | */ 42 | vsaq.questionnaire.items.InfoItem = function(id, conditions, info) { 43 | goog.base(this, id, conditions); 44 | 45 | /** 46 | * The information that should be shown to the user. 47 | * @type {string} 48 | */ 49 | this.info = goog.string.makeSafe(info); 50 | var propertyInformation = { 51 | nameInClass: 'info', 52 | mandatory: true 53 | }; 54 | this.addPropertyInformation('text', propertyInformation); 55 | 56 | this.render(); 57 | }; 58 | goog.inherits(vsaq.questionnaire.items.InfoItem, 59 | vsaq.questionnaire.items.Item); 60 | 61 | 62 | /** 63 | * Render the HTML for this item. 64 | */ 65 | vsaq.questionnaire.items.InfoItem.prototype.render = function() { 66 | var oldNode = this.container; 67 | this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.info, 68 | { 69 | id: this.id, 70 | captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.info) 71 | }); 72 | goog.dom.replaceNode(this.container, oldNode); 73 | }; 74 | 75 | 76 | /** 77 | * Type of the question. This is used to distinguish questions in serialized 78 | * format. 79 | * @type {string} 80 | * @const 81 | */ 82 | vsaq.questionnaire.items.InfoItem.TYPE = 'info'; 83 | 84 | 85 | /** 86 | * Parses InfoItems. If the topmost item in the passed Array is a 87 | * InfoItem, it is consumed and a InfoItem instance is returned. 88 | * If the topmost item is not a CheckItem, an exception is thrown. 89 | * @param {!Array.} questionStack Array of serialized 90 | * questionnaire Items. 91 | * @return {!vsaq.questionnaire.items.InfoItem} The parsed InfoItem. 92 | */ 93 | vsaq.questionnaire.items.InfoItem.parse = function(questionStack) { 94 | var item = questionStack.shift(); 95 | if (item.type != vsaq.questionnaire.items.InfoItem.TYPE) 96 | throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.'); 97 | 98 | return new vsaq.questionnaire.items.InfoItem(item.id, item.cond, item.text); 99 | }; 100 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/radioitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.RadioItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.RadioItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.RadioItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.events'); 27 | goog.require('goog.events.EventType'); 28 | goog.require('goog.testing.asserts'); 29 | goog.require('goog.testing.events'); 30 | goog.require('goog.testing.events.Event'); 31 | goog.require('goog.testing.jsunit'); 32 | goog.require('vsaq.questionnaire.items.BlockItem'); 33 | goog.require('vsaq.questionnaire.items.Item'); 34 | goog.require('vsaq.questionnaire.items.RadioItem'); 35 | 36 | var CAPTION = 'radioitem_caption'; 37 | var ID = 'radioitem_id'; 38 | var ID2 = 'radioitem_id2'; 39 | var VALUE = 'checked'; 40 | 41 | var radio, radio2; 42 | 43 | 44 | /** 45 | * Initializes variables used by all tests. 46 | */ 47 | function setUp() { 48 | var block = new vsaq.questionnaire.items.BlockItem('block_id', [], ''); 49 | radio = new vsaq.questionnaire.items.RadioItem(ID, [], CAPTION); 50 | radio2 = new vsaq.questionnaire.items.RadioItem(ID2, [], CAPTION); 51 | block.addItem(radio); 52 | block.addItem(radio2); 53 | } 54 | 55 | 56 | /** 57 | * Tests whether radio items are rendered correctly. 58 | */ 59 | function testRadioItem() { 60 | var el = radio.container; 61 | 62 | assertEquals(String(goog.dom.TagName.DIV), el.tagName); 63 | assertContains('vsaq-radio-item', el.className); 64 | assert(radio.radioButton instanceof HTMLInputElement); 65 | assertContains(CAPTION, goog.dom.getTextContent(el.firstChild)); 66 | assertFalse(radio.radioButton.checked); 67 | assertFalse(radio.isChecked()); 68 | } 69 | 70 | 71 | /** 72 | * Tests setting and retrieving the value of the item. 73 | */ 74 | function testRadioItemSetGetValue() { 75 | assertEquals('', radio.getValue()); 76 | radio.setValue(VALUE); 77 | assertEquals(VALUE, radio.getValue()); 78 | } 79 | 80 | 81 | /** 82 | * Tests parsing of RadioItems. 83 | */ 84 | function testRadioItemParse() { 85 | var testStack = [{ 86 | 'type': 'radio', 87 | 'text': CAPTION, 88 | 'id': ID 89 | }]; 90 | radio = vsaq.questionnaire.items.RadioItem.parse(testStack); 91 | assert(radio instanceof vsaq.questionnaire.items.RadioItem); 92 | assertEquals(ID, radio.id); 93 | assertEquals(CAPTION, radio.text); 94 | assertEquals(0, testStack.length); 95 | } 96 | 97 | 98 | /** 99 | * Tests that no two radio items can be selected at the same time. 100 | */ 101 | function testRadioItemChanges() { 102 | radio.setValue(true); 103 | assertEquals(VALUE, radio.getValue()); 104 | assertEquals('', radio2.getValue()); 105 | var e = new goog.testing.events.Event(goog.events.EventType.CHANGE, 106 | radio.radioButton); 107 | var eventSent = false; 108 | goog.events.listen(radio.eventDispatcher, 109 | [vsaq.questionnaire.items.Item.CHANGED], 110 | function(e) { 111 | eventSent = true; 112 | assertEquals(VALUE, e.changes[ID]); 113 | assertEquals('', e.changes[ID2]); 114 | }); 115 | goog.testing.events.fireBrowserEvent(e); 116 | assertEquals(true, eventSent); 117 | } 118 | 119 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/blockitems_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.BlockItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.BlockItemsTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.BlockItemsTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.dom.classlist'); 27 | goog.require('goog.testing.MockControl'); 28 | goog.require('goog.testing.events'); 29 | goog.require('goog.testing.jsunit'); 30 | goog.require('goog.testing.mockmatchers.ObjectEquals'); 31 | goog.require('vsaq.questionnaire.items.BlockItem'); 32 | goog.require('vsaq.questionnaire.items.Item'); 33 | goog.require('vsaq.questionnaire.items.RadioItem'); 34 | 35 | var CAPTION = 'block_caption'; 36 | var ID = 'test_id'; 37 | var ADMIN = 'admin'; 38 | 39 | var block; 40 | 41 | 42 | /** 43 | * Initializes variables used by all tests. 44 | */ 45 | function setUp() { 46 | block = new vsaq.questionnaire.items.BlockItem( 47 | ID, [], CAPTION, ADMIN); 48 | } 49 | 50 | 51 | /** 52 | * Tests whether blocks are rendered correctly. 53 | */ 54 | function testBlockItem() { 55 | var el = block.container; 56 | var legend = goog.dom.getFirstElementChild(el); 57 | 58 | assertEquals(String(goog.dom.TagName.FIELDSET), el.tagName); 59 | assert(goog.dom.classlist.contains(el, 'vsaq-block')); 60 | assertEquals(String(goog.dom.TagName.LEGEND), legend.tagName); 61 | 62 | var span = goog.dom.getFirstElementChild(legend); 63 | assertEquals(String(goog.dom.TagName.SPAN), span.tagName); 64 | assertEquals(CAPTION, span.innerHTML); 65 | } 66 | 67 | 68 | /** 69 | * Tests changes to radio buttons within the block. 70 | */ 71 | function testBlockRadioChanges() { 72 | var rad1 = new vsaq.questionnaire.items.RadioItem('radio1_id', [], 'radio1'); 73 | var rad2 = new vsaq.questionnaire.items.RadioItem('radio2_id', [], 'radio2'); 74 | 75 | block.addItem(rad1); 76 | block.addItem(rad2); 77 | 78 | var expectedEvent = { 79 | type: vsaq.questionnaire.items.Item.CHANGED, 80 | source: block, 81 | changes: {'radio1_id': rad1.getValue(), 'radio2_id': ''} 82 | }; 83 | 84 | var mock = new goog.testing.MockControl(); 85 | mock.createFunctionMock(block.eventDispatcher, 'dispatchEvent'); 86 | 87 | block.eventDispatcher.dispatchEvent( 88 | new goog.testing.mockmatchers.ObjectEquals(expectedEvent)); 89 | 90 | mock.$replayAll(); 91 | 92 | // Change the selection. 93 | goog.testing.events.fireClickEvent(rad1.radioButton); 94 | 95 | mock.$verifyAll(); 96 | mock.$tearDown(); 97 | } 98 | 99 | 100 | /** 101 | * Tests adding of containerItems to blocks. 102 | */ 103 | function testBlockAddItem() { 104 | var rad = new vsaq.questionnaire.items.RadioItem('radio1_id', [], 'radio1'); 105 | block.addItem(rad); 106 | assertContains('Item not added.', rad, block.containerItems); 107 | assertEquals(rad.container, block.container.lastChild); 108 | } 109 | 110 | 111 | /** 112 | * Tests parsing of BlockItems. 113 | */ 114 | function testStartBlockParse() { 115 | var testStack = [{ 116 | 'type': 'block', 117 | 'text': CAPTION, 118 | 'id': ID, 119 | 'auth' : ADMIN 120 | }]; 121 | 122 | block = vsaq.questionnaire.items.BlockItem.parse(testStack); 123 | 124 | assert(block instanceof vsaq.questionnaire.items.BlockItem); 125 | assertEquals(ID, block.id); 126 | assertEquals(CAPTION, block.text); 127 | assertEquals(ADMIN, block.auth); 128 | assertEquals(0, testStack.length); 129 | } 130 | -------------------------------------------------------------------------------- /src/ldapUtils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Morningstar Inc. All rights reserved. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | import ast, ldap, logging 15 | 16 | from flask import current_app 17 | 18 | class LDAPServer(object): 19 | 20 | def __init__(self, app=None): 21 | super(LDAPServer, self) 22 | self.app = app or current_app 23 | self.connection = None 24 | 25 | def authenticateAndSearch(self, username, password, searchAccountName): 26 | authSuccessful = self.authenticate(username, password) 27 | userDetails = {} 28 | if authSuccessful: 29 | userDetails = self.search(searchAccountName) 30 | self.disconnect() 31 | return userDetails 32 | 33 | def authenticate(self, username, password): 34 | user = username+self.app.config['LDAP_USER_DOMAIN'] 35 | index = self.app.config['LDAP_PROVIDERS_USE_INDEX']; 36 | logging.debug('Connecting to LDAP provider:%s for user:%s', self.app.config['LDAP_PROVIDERS'][index], user) 37 | self.disconnect() 38 | connection = ldap.initialize(self.app.config['LDAP_PROVIDERS'][index]) 39 | connection.protocol_version = ldap.VERSION2 40 | result = False 41 | try: 42 | connection.bind_s(user, password, ldap.AUTH_SIMPLE) 43 | self.connection = connection 44 | result = True 45 | logging.info("User logged in:%s",username) 46 | except ldap.LDAPError as e: 47 | self.disconnect() 48 | self.connection = None 49 | logging.error("LDAPServer.authenticate:Exception while trying for /login request for user:\n" + 50 | username + "\n" + repr(e), exc_info=True) 51 | except Exception as e: 52 | self.disconnect() 53 | logging.error("LDAPServer.authenticate:Exception for user:\n" + username + "\n" + 54 | repr(e), exc_info=True) 55 | return result 56 | 57 | def search(self, searchAccountName): 58 | search_filter = self.app.config['LDAP_SEARCH_CN']+searchAccountName #"CN=Full Name" 59 | userDetails = {} 60 | if self.connection is None: 61 | return userDetails 62 | try: 63 | #if authentication was successful, get the full user data 64 | result = self.connection.search_s(self.app.config['LDAP_BASE_DN'],ldap.SCOPE_SUBTREE,search_filter) 65 | except Exception as e: 66 | self.disconnect() 67 | logging.error("LDAPServer.search:Exception while searching for account:\n" + searchAccountName + "\n" + 68 | repr(e), exc_info=True) 69 | return userDetails 70 | rawDict = ast.literal_eval(str(result[0]))[1] 71 | 72 | 73 | data = { k: v for k, v in rawDict.iteritems() } 74 | userDetails['t'] = data.get('title')[0] 75 | userDetails['m'] = data.get('mail')[0] 76 | userDetails['u'] = data.get('name')[0] 77 | 78 | logging.debug("Print the data start") 79 | for key,val in data.items(): 80 | logging.debug("data item: key: {} value {}".format(key, val)) 81 | logging.debug("Print the data end") 82 | 83 | if self.app.config['REVIEWERS_AD_GROUP'] in data.get('memberOf'): 84 | logging.info("Get the review approved in the REVIEWS AD Group.") 85 | userDetails['a'] = True 86 | 87 | acctName = data.get("sAMAccountName")[0] 88 | logging.debug("Account Name {}".format(acctName)) 89 | for reviewer in self.app.config['REVIEWERS_ADDITIONAL']: 90 | logging.debug("Reviewer: {}".format(reviewer)) 91 | 92 | if acctName in self.app.config['REVIEWERS_ADDITIONAL']: 93 | logging.info("Get the review approved in the REVIEWS ADDITIONAL LIST.") 94 | userDetails['a'] = True 95 | 96 | return userDetails 97 | 98 | def disconnect(self): 99 | if self.connection is not None: 100 | self.connection.unbind_s() 101 | logging.info("Disconnected from LDAP provider!") 102 | 103 | def testResponse(self): 104 | userDetails = {} 105 | userDetails['t'] = "Test Title" 106 | userDetails['m'] = "TestEmail@Example.com" 107 | userDetails['u'] = "Test User" 108 | userDetails['a'] = True 109 | return userDetails 110 | -------------------------------------------------------------------------------- /src/client_side_only_impl/vsaq_editor.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | [PAGE_TITLE] 22 | 23 | 24 | 25 | 26 | 34 | 35 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 54 | 55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | Edit Options 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 |
Editor
Light-Editor
Unrolled (don't check conditions)
Live edit mode
84 | Template 85 | (→) Export template 86 | Parse template (←)
87 | 99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | 107 | 108 |
109 |
110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/groupitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.GroupItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.GroupItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.GroupItemTests'); 23 | 24 | goog.require('goog.array'); 25 | goog.require('goog.testing.asserts'); 26 | goog.require('goog.testing.jsunit'); 27 | goog.require('vsaq.questionnaire.items.CheckItem'); 28 | goog.require('vsaq.questionnaire.items.CheckgroupItem'); 29 | goog.require('vsaq.questionnaire.items.GroupItem'); 30 | goog.require('vsaq.questionnaire.items.RadioItem'); 31 | goog.require('vsaq.questionnaire.items.RadiogroupItem'); 32 | 33 | 34 | var TEST_CHECKGROUP = { 35 | 'type': 'checkgroup', 36 | 'defaultChoice': true, 37 | 'text': 'caption', 38 | 'choices': [ 39 | {'choice_id_1': 'Text 1'}, 40 | {'choice_id_2': 'Text 2'} 41 | ], 42 | 'choicesConds': [] 43 | }; 44 | 45 | var TEST_RADIOGROUP = { 46 | 'type': 'radiogroup', 47 | 'text': 'caption', 48 | 'defaultChoice': false, 49 | 'choices': [ 50 | {'choice_id_1': 'Text 1'}, 51 | {'choice_id_2': 'Text 2'} 52 | ], 53 | 'choicesConds': [] 54 | }; 55 | 56 | var checkgroupItem, radiogroupItem; 57 | 58 | 59 | /** 60 | * Initializes variables used by all tests. 61 | */ 62 | function setUp() { 63 | checkgroupItem = new vsaq.questionnaire.items.CheckgroupItem( 64 | null, null, TEST_CHECKGROUP.text, TEST_CHECKGROUP.defaultChoice, 65 | TEST_CHECKGROUP.choices, TEST_CHECKGROUP.choicesConds); 66 | 67 | radiogroupItem = new vsaq.questionnaire.items.RadiogroupItem( 68 | null, null, TEST_RADIOGROUP.text, TEST_RADIOGROUP.defaultChoice, 69 | TEST_RADIOGROUP.choices, TEST_RADIOGROUP.choicesConds); 70 | } 71 | 72 | 73 | /** 74 | * Tests whether all possible GroupItems are initialized correctly. 75 | */ 76 | 77 | function testGroupItem() { 78 | 79 | var defaultChoiceFound; 80 | 81 | // First verify the correctness of check groups. 82 | defaultChoiceFound = false; 83 | 84 | var expectedNumberChoices = TEST_CHECKGROUP.choices.length; 85 | 86 | if (TEST_CHECKGROUP['defaultChoice']) 87 | expectedNumberChoices++; 88 | assert(expectedNumberChoices == checkgroupItem.containerItems.length); 89 | 90 | goog.array.forEach(checkgroupItem.containerItems, function(item) { 91 | assert(item instanceof vsaq.questionnaire.items.CheckItem); 92 | // All choices are supposed to have the containerItem as a parent. 93 | assertNotUndefined(item.parentItem); 94 | 95 | if (item.text == vsaq.questionnaire.items.GroupItem.DEFAULT_CHOICE) 96 | defaultChoiceFound = true; 97 | }); 98 | assert(defaultChoiceFound == TEST_CHECKGROUP['defaultChoice']); 99 | 100 | // Now verify the correctness of radio groups. 101 | defaultChoiceFound = false; 102 | expectedNumberChoices = TEST_RADIOGROUP.choices.length; 103 | if (TEST_RADIOGROUP['defaultChoice']) 104 | expectedNumberChoices++; 105 | assert(expectedNumberChoices == radiogroupItem.containerItems.length); 106 | 107 | goog.array.forEach(radiogroupItem.containerItems, function(item) { 108 | assert(item instanceof vsaq.questionnaire.items.RadioItem); 109 | // All choices are supposed to have the containerItem as a parent. 110 | assertNotUndefined(item.parentItem); 111 | 112 | if (item.text == vsaq.questionnaire.items.GroupItem.DEFAULT_CHOICE) 113 | defaultChoiceFound = true; 114 | }); 115 | assert(defaultChoiceFound == TEST_RADIOGROUP['defaultChoice']); 116 | } 117 | 118 | 119 | /** 120 | * Tests parsing of all possible GroupItems. 121 | */ 122 | function testGroupItemParse() { 123 | 124 | var testStack = [TEST_CHECKGROUP]; 125 | checkgroupItem = vsaq.questionnaire.items.CheckgroupItem.parse(testStack); 126 | assert(checkgroupItem instanceof vsaq.questionnaire.items.CheckgroupItem); 127 | 128 | testStack = [TEST_RADIOGROUP]; 129 | radiogroupItem = vsaq.questionnaire.items.RadiogroupItem.parse(testStack); 130 | assert(radiogroupItem instanceof vsaq.questionnaire.items.RadiogroupItem); 131 | 132 | // Verify all items once again 133 | testGroupItem(); 134 | } 135 | -------------------------------------------------------------------------------- /src/flask_sslify.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (c) 2012, Kenneth Reitz 4 | # All rights reserved. 5 | 6 | # Redistribution and use in source and binary forms, with or without modification, 7 | # are permitted provided that the following conditions are met: 8 | 9 | # Redistributions of source code must retain the above copyright notice, this list 10 | # of conditions and the following disclaimer. 11 | # Redistributions in binary form must reproduce the above copyright notice, this 12 | # list of conditions and the following disclaimer in the documentation and/or other 13 | # materials provided with the distribution. 14 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 18 | # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 20 | # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 21 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 22 | # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 23 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | from flask import current_app, redirect, request 26 | 27 | YEAR_IN_SECS = 31536000 28 | 29 | class SSLify(object): 30 | """Secures your Flask App.""" 31 | 32 | def __init__(self, app=None, age=YEAR_IN_SECS, subdomains=False, permanent=False, skips=None, ssl_debug=False, preload=False): 33 | self.app = app or current_app 34 | self.hsts_age = age 35 | self.ssl_debug = ssl_debug 36 | self.preload = preload 37 | 38 | self.hsts_include_subdomains = subdomains 39 | self.permanent = permanent 40 | self.skip_list = skips 41 | 42 | if app is not None: 43 | self.init_app(app) 44 | 45 | def init_app(self, app): 46 | """Configures the specified Flask app to enforce SSL.""" 47 | app.config.setdefault('SSLIFY_SUBDOMAINS', False) 48 | app.config.setdefault('SSLIFY_PERMANENT', False) 49 | app.config.setdefault('SSLIFY_SKIPS', None) 50 | 51 | self.hsts_include_subdomains = self.hsts_include_subdomains or app.config['SSLIFY_SUBDOMAINS'] 52 | self.permanent = self.permanent or self.app.config['SSLIFY_PERMANENT'] 53 | self.skip_list = self.skip_list or self.app.config['SSLIFY_SKIPS'] 54 | self.hsts_include_preload = self.preload or self.app.config['SSLIFY_PRELOAD'] 55 | 56 | app.before_request(self.redirect_to_ssl) 57 | app.after_request(self.set_hsts_header) 58 | 59 | @property 60 | def hsts_header(self): 61 | """Returns the proper HSTS policy.""" 62 | hsts_policy = 'max-age={0}'.format(self.hsts_age) 63 | 64 | if self.hsts_include_subdomains: 65 | hsts_policy += '; includeSubDomains' 66 | 67 | if self.hsts_include_preload: 68 | hsts_policy += '; preload' 69 | 70 | return hsts_policy 71 | 72 | @property 73 | def skip(self): 74 | """Checks the skip list.""" 75 | # Should we skip? 76 | if self.skip_list and isinstance(self.skip_list, list): 77 | for skip in self.skip_list: 78 | if request.path.startswith('/{0}'.format(skip)): 79 | return True 80 | return False 81 | 82 | @property 83 | def debug_criteria(self): 84 | if self.ssl_debug: 85 | return False 86 | else: 87 | return current_app.debug 88 | 89 | def redirect_to_ssl(self): 90 | """Redirect incoming requests to HTTPS.""" 91 | # Should we redirect? 92 | criteria = [ 93 | request.is_secure, 94 | self.debug_criteria, 95 | current_app.testing, 96 | request.headers.get('X-Forwarded-Proto', 'http') == 'https', 97 | "X-Appengine-Cron" in request.headers, 98 | "X-Appengine-TaskName" in request.headers 99 | ] 100 | 101 | if not any(criteria) and not self.skip: 102 | if request.url.startswith('http://'): 103 | url = request.url.replace('http://', 'https://', 1) 104 | code = 302 105 | if self.permanent: 106 | code = 301 107 | r = redirect(url, code=code) 108 | return r 109 | 110 | def set_hsts_header(self, response): 111 | """Adds HSTS header to each response.""" 112 | # Should we add STS header? 113 | if request.is_secure and not self.skip: 114 | response.headers.setdefault('Strict-Transport-Security', self.hsts_header) 115 | return response 116 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/tipitem_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.items.TipItem. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.TipItemTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.TipItemTests'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.testing.asserts'); 27 | goog.require('goog.testing.jsunit'); 28 | goog.require('vsaq.questionnaire.items.TipItem'); 29 | 30 | var CAPTION = 'tipitem_caption'; 31 | var TIP_ID = 'tipitem_id'; 32 | var WARN_ID = 'warntipitem_id'; 33 | var WHY_ID = 'whytipitem_id'; 34 | var NAME_ID = 'namedtipitem_id'; 35 | var TODO_ID = 'todotipitem_id'; 36 | var CUSTOMTITLE_ID = 'customtitletipitem_id'; 37 | var WHY_TEXT = 'whytipitem_text'; 38 | var VALUE = 'whytipitem_value'; 39 | var NAME = 'whytip_name'; 40 | var TODO = 'whytip_todo'; 41 | var CUSTOMTITLE_TEXT = 'customtitle_text'; 42 | 43 | var tip; 44 | var warn_tip; 45 | var why_tip; 46 | 47 | 48 | /** 49 | * Initializes variables used by all tests. 50 | */ 51 | function setUp() { 52 | tip = new vsaq.questionnaire.items.TipItem(TIP_ID, [], CAPTION, false); 53 | warn_tip = new vsaq.questionnaire.items.TipItem(WARN_ID, [], CAPTION, true); 54 | why_tip = new vsaq.questionnaire.items.TipItem( 55 | WHY_ID, [], CAPTION, true, 'medium', WHY_TEXT); 56 | name_tip = new vsaq.questionnaire.items.TipItem( 57 | NAME_ID, [], CAPTION, true, 'medium', null, NAME); 58 | todo_tip = new vsaq.questionnaire.items.TipItem( 59 | TODO_ID, [], CAPTION, true, 'medium', null, null, TODO); 60 | custom_title_tip = new vsaq.questionnaire.items.TipItem( 61 | CUSTOMTITLE_ID, [], CAPTION, true, 'medium', null, null, 62 | null, CUSTOMTITLE_TEXT); 63 | } 64 | 65 | 66 | /** 67 | * Tests whether tip items are rendered correctly. 68 | */ 69 | function testTipItem() { 70 | var el = tip.container; 71 | assertEquals(String(goog.dom.TagName.DIV), el.tagName); 72 | assertEquals(TIP_ID, tip.id); 73 | assertContains('Tip', goog.dom.getTextContent(el)); 74 | assertContains(CAPTION, goog.dom.getTextContent(el)); 75 | assertNotContains('Warning', goog.dom.getTextContent(el)); 76 | 77 | el = warn_tip.container; 78 | assertEquals(String(goog.dom.TagName.DIV), el.tagName); 79 | assertEquals(WARN_ID, warn_tip.id); 80 | assertNotContains('Tip', goog.dom.getTextContent(el)); 81 | assertContains(CAPTION, goog.dom.getTextContent(el)); 82 | assertContains('Warning', goog.dom.getTextContent(el)); 83 | assertContains('vsaq-bubble-medium', el.innerHTML); 84 | 85 | el = why_tip.container; 86 | assertEquals(String(goog.dom.TagName.DIV), el.tagName); 87 | assertEquals(WHY_ID, why_tip.id); 88 | assertNotContains('Tip', goog.dom.getTextContent(el)); 89 | assertContains(CAPTION, goog.dom.getTextContent(el)); 90 | assertContains('Warning', goog.dom.getTextContent(el)); 91 | assertContains('vsaq-bubble-medium', el.innerHTML); 92 | 93 | assertEquals(NAME_ID, name_tip.id); 94 | assertEquals(NAME, name_tip.name); 95 | 96 | assertEquals(TODO_ID, todo_tip.id); 97 | assertEquals(TODO, todo_tip.todo); 98 | 99 | assertEquals(CUSTOMTITLE_ID, custom_title_tip.id); 100 | assertEquals(CUSTOMTITLE_TEXT, custom_title_tip.customTitle); 101 | } 102 | 103 | 104 | /** 105 | * Tests setting and retrieving the value of the item. 106 | */ 107 | function testTipItemSetGetValue() { 108 | assertEquals('', why_tip.getValue()); 109 | why_tip.setValue(VALUE); 110 | assertEquals(VALUE, why_tip.getValue()); 111 | } 112 | 113 | 114 | /** 115 | * Tests parsing of TipItems. 116 | */ 117 | function testTipItemParse() { 118 | var testStack = [{ 119 | 'type': 'tip', 120 | 'text': CAPTION, 121 | 'id': TIP_ID, 122 | 'warn': 'yes', 123 | 'why': WHY_TEXT, 124 | 'todo': TODO, 125 | 'customTitle' : CUSTOMTITLE_TEXT, 126 | }]; 127 | tip = vsaq.questionnaire.items.TipItem.parse(testStack); 128 | assert(tip instanceof vsaq.questionnaire.items.TipItem); 129 | assertEquals(TIP_ID, tip.id); 130 | assertEquals(CAPTION, tip.text); 131 | assertEquals(true, tip.warn); 132 | assertEquals(WHY_TEXT, tip.clarification); 133 | assertEquals(0, testStack.length); 134 | assertEquals(TODO, tip.todo); 135 | assertEquals(CUSTOMTITLE_TEXT, tip.customTitle); 136 | } 137 | 138 | -------------------------------------------------------------------------------- /src/client_side_only_impl/all_tests.html: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | VSAQ - All JsUnit Tests 20 | 21 | 22 | 23 | 26 | 27 | 56 | 57 | 58 |

VSAQ - All JsUnit Tests

59 |
60 | 61 |
62 | Settings:
63 | 64 |
65 |
66 | 67 | (use regexp, e.g. "structs.*") 68 |
69 |

Tests:

70 |

 71 | 
154 | 
155 | 
156 | 


--------------------------------------------------------------------------------
/src/download-libs.sh:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/env bash
  2 | # Copyright 2016 Google Inc. All rights reserved.
  3 | #
  4 | # Licensed under the Apache License, Version 2.0 (the "License");
  5 | # you may not use this file except in compliance with the License.
  6 | # You may obtain a copy of the License at
  7 | #
  8 | # http://www.apache.org/licenses/LICENSE-2.0
  9 | #
 10 | # Unless required by applicable law or agreed to in writing, software
 11 | # distributed under the License is distributed on an "AS IS" BASIS,
 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 | # See the License for the specific language governing permissions and
 14 | # limitations under the License.
 15 | #
 16 | # @fileoverview Shell script to download VSAQ build dependencies
 17 | #
 18 | 
 19 | export JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF8
 20 | THIRD_PARTY_DIRECTORY="third_party"
 21 | 
 22 | type ant >/dev/null 2>&1 || {
 23 |   echo >&2 "Ant is required to build VSAQ dependencies."
 24 |   exit 1
 25 | }
 26 | type javac >/dev/null 2>&1 || {
 27 |   echo >&2 "Java compiler is required to build VSAQ dependencies."
 28 |   exit 1
 29 | }
 30 | type git >/dev/null 2>&1 || {
 31 |   echo >&2 "Git is required to build VSAQ dependencies."
 32 |   exit 1
 33 | }
 34 | type curl >/dev/null 2>&1 || {
 35 |   echo >&2 "Curl is required to build VSAQ dependencies."
 36 |   exit 1
 37 | }
 38 | type unzip >/dev/null 2>&1 || {
 39 |   echo >&2 "Unzip is required to build VSAQ dependencies."
 40 |   exit 1
 41 | }
 42 | jversion=$(java -version 2>&1 | grep version | awk -F '"' '{print $2}')
 43 | if [[ $jversion < "1.7" ]]; then
 44 |   echo "Java 1.7 or higher is required to build VSAQ."
 45 |   exit 1
 46 | fi
 47 | 
 48 | # if repo was downloaded as zip from github, init git and clear submodules
 49 | if [ ! -d .git ]; then
 50 |   git init
 51 |   rm -rf $THIRD_PARTY_DIRECTORY/closure-compiler
 52 |   rm -rf $THIRD_PARTY_DIRECTORY/closure-library
 53 |   rm -rf $THIRD_PARTY_DIRECTORY/closure-stylesheets
 54 |   rm -rf $THIRD_PARTY_DIRECTORY/js-dossier
 55 | fi
 56 | 
 57 | if [ ! -d $THIRD_PARTY_DIRECTORY ]; then
 58 |   mkdir $THIRD_PARTY_DIRECTORY
 59 | fi
 60 | cd $THIRD_PARTY_DIRECTORY
 61 | 
 62 | git init
 63 | git submodule add -f https://github.com/google/closure-compiler closure-compiler
 64 | git submodule add -f https://github.com/google/closure-library closure-library
 65 | git submodule add -f https://github.com/google/closure-stylesheets closure-stylesheets
 66 | git submodule add -f https://github.com/jleyba/js-dossier js-dossier
 67 | 
 68 | git submodule init
 69 | git submodule update
 70 | 
 71 | # Pin submodules to particular commits
 72 | cd closure-compiler
 73 | git branch -d 59b42c9fc8fc752b3ff3aabe04ad89a96f9a7bf7
 74 | git checkout -b 59b42c9fc8fc752b3ff3aabe04ad89a96f9a7bf7 59b42c9fc8fc752b3ff3aabe04ad89a96f9a7bf7
 75 | cd ..
 76 | cd closure-library
 77 | git branch -d dc369cde87d7ef6dfb46d3b873f872ebee7d07cd
 78 | git checkout -b dc369cde87d7ef6dfb46d3b873f872ebee7d07cd dc369cde87d7ef6dfb46d3b873f872ebee7d07cd
 79 | cd ..
 80 | cd js-dossier
 81 | git branch -d 6f2d09ee26925b7417f9f6bd1547dffe700ab60f
 82 | git checkout -b 6f2d09ee26925b7417f9f6bd1547dffe700ab60f 6f2d09ee26925b7417f9f6bd1547dffe700ab60f
 83 | cd ..
 84 | 
 85 | # build closure compiler
 86 | if [ ! -f closure-compiler/build/compiler.jar ] && [ -d closure-compiler ]; then
 87 |   cd closure-compiler
 88 |   ant clean
 89 |   ant jar
 90 |   cd ..
 91 | fi
 92 | 
 93 | # checkout closure templates compiler
 94 | if [ ! -d closure-templates-compiler ]; then
 95 |   curl https://dl.google.com/closure-templates/closure-templates-for-javascript-latest.zip -O
 96 |   unzip closure-templates-for-javascript-latest.zip -d closure-templates-compiler
 97 |   rm closure-templates-for-javascript-latest.zip
 98 | fi
 99 | 
100 | # build css compiler
101 | if [ ! -f closure-stylesheets/target/closure-stylesheets.jar ]; then
102 |   cd closure-stylesheets
103 |   mvn compile assembly:single
104 |   mv ./target/closure-stylesheets-1.6.0-SNAPSHOT-jar-with-dependencies.jar ./target/closure-stylesheets.jar
105 |   cd ..
106 | fi
107 | 
108 | if [ -f chrome_extensions.js ]; then
109 |   rm -f chrome_extensions.js
110 | fi
111 | 
112 | # Temporary fix
113 | # Soy file bundled with the compiler does not compile with strict settings:
114 | # lib/closure-templates-compiler/soyutils_usegoog.js:1762: ERROR - element JS_STR_CHARS does not exist on this enum
115 | cd closure-templates-compiler
116 | echo $PWD
117 | curl https://raw.githubusercontent.com/google/closure-templates/0cbc8543c34d3f7727dd83a2d1938672f16d5c20/javascript/soyutils_usegoog.js -O
118 | cd ..
119 | 
120 | cd ..
121 | echo "third_party/closure-library contents..."
122 | ls third_party/closure-library
123 | echo "third_party/closure-templates-compiler contents..."
124 | ls third_party/closure-templates-compiler
125 | echo "third_party/closure-stylesheets/target contents..."
126 | ls third_party/closure-stylesheets/target
127 | echo "third_party/closure-compiler/build contents..."
128 | ls third_party/closure-compiler/build
129 | echo "third_party/closure-compiler/contrib/externs contents..."
130 | ls third_party/closure-compiler/contrib/externs
131 | 


--------------------------------------------------------------------------------
/src/vsaq/static/questionnaire/checkitem.js:
--------------------------------------------------------------------------------
  1 | /**
  2 |  * @license
  3 |  * Copyright 2016 Google Inc. All rights reserved.
  4 |  * Licensed under the Apache License, Version 2.0 (the "License");
  5 |  * you may not use this file except in compliance with the License.
  6 |  * You may obtain a copy of the License at
  7 |  *
  8 |  *   http://www.apache.org/licenses/LICENSE-2.0
  9 |  *
 10 |  * Unless required by applicable law or agreed to in writing, software
 11 |  * distributed under the License is distributed on an "AS IS" BASIS,
 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 |  * See the License for the specific language governing permissions and
 14 |  * limitations under the License.
 15 |  */
 16 | 
 17 | /**
 18 |  * @fileoverview A questionnaire item with a checkbox.
 19 |  */
 20 | 
 21 | goog.provide('vsaq.questionnaire.items.CheckItem');
 22 | 
 23 | goog.require('goog.dom');
 24 | goog.require('goog.events');
 25 | goog.require('goog.events.EventType');
 26 | goog.require('goog.soy');
 27 | goog.require('vsaq.questionnaire.items.ParseError');
 28 | goog.require('vsaq.questionnaire.items.ValueItem');
 29 | goog.require('vsaq.questionnaire.templates');
 30 | goog.require('vsaq.questionnaire.utils');
 31 | 
 32 | 
 33 | 
 34 | /**
 35 |  * A question that allows the user to answer by ticking a checkbox.
 36 |  * @param {string} id An ID uniquely identifying the question.
 37 |  * @param {?string} conditions A string containing conditions which must be met
 38 |  *     for the item to be visible to the user.
 39 |  * @param {string} caption The caption to show next to the checkbox.
 40 |  * @extends {vsaq.questionnaire.items.ValueItem}
 41 |  * @constructor
 42 |  */
 43 | vsaq.questionnaire.items.CheckItem = function(id, conditions, caption) {
 44 |   goog.base(this, id, conditions, caption);
 45 | 
 46 |   /**
 47 |    * The checkbox that is the actual control behind this question.
 48 |    * @type {!HTMLInputElement}
 49 |    * @private
 50 |    */
 51 |   this.checkBox_;
 52 | 
 53 |   this.render();
 54 | };
 55 | goog.inherits(vsaq.questionnaire.items.CheckItem,
 56 |               vsaq.questionnaire.items.ValueItem);
 57 | 
 58 | 
 59 | /**
 60 |  * Render the HTML for this item.
 61 |  */
 62 | vsaq.questionnaire.items.CheckItem.prototype.render = function() {
 63 |   var oldNode = this.container;
 64 |   this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.check,
 65 |       {
 66 |         id: this.id,
 67 |         captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text)
 68 |       });
 69 |   goog.dom.replaceNode(this.container, oldNode);
 70 | 
 71 |   this.checkBox_ = /** @type {!HTMLInputElement} */ (
 72 |       vsaq.questionnaire.utils.findById(this.container, this.id));
 73 |   goog.events.listen(this.checkBox_, goog.events.EventType.CHANGE,
 74 |       this.answerChanged, true, this);
 75 | };
 76 | 
 77 | 
 78 | /**
 79 |  * Constant indicating the string value used if the item is selected/checked.
 80 |  * @type {string}
 81 |  */
 82 | vsaq.questionnaire.items.CheckItem.CHECKED_VALUE = 'checked';
 83 | 
 84 | 
 85 | /**
 86 |  * Type of the question. This is used to distinguish questions in serialized
 87 |  * format.
 88 |  * @type {string}
 89 |  * @const
 90 |  */
 91 | vsaq.questionnaire.items.CheckItem.TYPE = 'check';
 92 | 
 93 | 
 94 | /**
 95 |  * Parses CheckItems. If the topmost item in the passed Array is an a
 96 |  * CheckItem, it is consumed and a CheckItem instance is returned.
 97 |  * If the topmost item is not a CheckItem, an exception is thrown.
 98 |  * @param {!Array.} questionStack Array of serialized
 99 |  *     questionnaire Items.
100 |  * @return {!vsaq.questionnaire.items.CheckItem} The parsed CheckItem.
101 |  */
102 | vsaq.questionnaire.items.CheckItem.parse = function(questionStack) {
103 |   var item = questionStack.shift();
104 |   if (item.type != vsaq.questionnaire.items.CheckItem.TYPE)
105 |     throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.');
106 | 
107 |   return new vsaq.questionnaire.items.CheckItem(item.id, item.cond, item.text);
108 | };
109 | 
110 | 
111 | /** @inheritDoc */
112 | vsaq.questionnaire.items.CheckItem.prototype.setReadOnly = function(readOnly) {
113 |   this.checkBox_.disabled = readOnly;
114 | };
115 | 
116 | 
117 | /** @inheritDoc */
118 | vsaq.questionnaire.items.CheckItem.prototype.isChecked = function(opt_value) {
119 |   return this.checkBox_.checked;
120 | };
121 | 
122 | 
123 | /** @inheritDoc */
124 | vsaq.questionnaire.items.CheckItem.prototype.getValue = function() {
125 |   return this.checkBox_.checked ?
126 |       vsaq.questionnaire.items.CheckItem.CHECKED_VALUE : '';
127 | };
128 | 
129 | 
130 | /** @inheritDoc */
131 | vsaq.questionnaire.items.CheckItem.prototype.setInternalValue =
132 |     function(value) {
133 |   if (goog.isBoolean(value)) {
134 |     this.checkBox_.checked = value;
135 |   } else if (goog.isString(value)) {
136 |     this.checkBox_.checked =
137 |         value == vsaq.questionnaire.items.CheckItem.CHECKED_VALUE;
138 |   }
139 | };
140 | 
141 | 
142 | /** @inheritDoc */
143 | vsaq.questionnaire.items.CheckItem.prototype.isAnswered = function() {
144 |   // Always returns true, since with checkboxes we can't know whether
145 |   // the checkbox is un-answered or intentionally left unchecked.
146 |   return true;
147 | };
148 | 
149 | 


--------------------------------------------------------------------------------
/src/renderer.py:
--------------------------------------------------------------------------------
  1 | # Copyright 2018 Morningstar Inc. All rights reserved.
  2 | # Licensed under the Apache License, Version 2.0 (the "License");
  3 | # you may not use this file except in compliance with the License.
  4 | # You may obtain a copy of the License at
  5 | #
  6 | # http://www.apache.org/licenses/LICENSE-2.0
  7 | #
  8 | # Unless required by applicable law or agreed to in writing, software
  9 | # distributed under the License is distributed on an "AS IS" BASIS,
 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 11 | # See the License for the specific language governing permissions and
 12 | # limitations under the License.
 13 | 
 14 | import datetime, os.path, time
 15 | import randomizer
 16 | 
 17 | from flask import abort, current_app, make_response, render_template, request, session
 18 | from translator import Translator
 19 | from wsgiref.handlers import format_date_time
 20 | 
 21 | class Renderer(Translator):
 22 | 
 23 |   def __init__(self, app=None):
 24 |     super(Renderer, self)
 25 |     self.handlers = {'html': self.htmlResponse, \
 26 |             '/': self.htmlResponse, \
 27 |             'css': self.staticContentResponse, \
 28 |             'gif': self.staticContentResponse, \
 29 |             'js': self.staticContentResponse, \
 30 |             'json': self.jsonContentResponse, \
 31 |             'svg': self.staticContentResponse }
 32 |     self.contentTypes = {'css': "text/css", \
 33 |             'gif': "image/gif", \
 34 |             'html': "text/html; charset=utf-8", \
 35 |             'js': "application/javascript", \
 36 |             'json': "application/json; charset=utf-8", \
 37 |             'svg': "image/svg+xml" }
 38 |     self.app = app or current_app
 39 |     if app is not None:
 40 |       self.init_app(app)
 41 | 
 42 |   def init_app(self, app):
 43 |     self.app.after_request(self.setResponseHeaders)
 44 | 
 45 |   def handle_request(self):
 46 |     """Serves questionnaire page to the browser."""
 47 |     if self.needsFullResponse:
 48 |       extension = request.path.split(".")[-1]
 49 |       return self.handlers.get(extension, self.defaultResponse)()
 50 |     else:
 51 |       return make_response("", 304)
 52 | 
 53 |   def htmlResponse(self):
 54 |     args = request.args.copy()
 55 |     response = self.translate_path(request.path)
 56 |     if args.get('qpath') is not None:
 57 |       qpath = args['qpath']
 58 |       return render_template(response, qpath=qpath)
 59 |     else:
 60 |       return render_template(response)
 61 | 
 62 |   def staticContentResponse(self):
 63 |     extension = request.path.split(".")[-1]
 64 |     response = self.translate_path(request.path)
 65 |     r = make_response(render_template(response))
 66 |     return r
 67 | 
 68 |   def jsonContentResponse(self):
 69 |     questionnaires = self.app.config['QUESTIONNAIRES_SERVED']
 70 |     for questionnaire in questionnaires:
 71 |       if questionnaire in request.path:
 72 |         return self.staticContentResponse()
 73 |     return self.defaultResponse()
 74 | 
 75 |   def defaultResponse(self):
 76 |     abort(400)
 77 | 
 78 |   def setResponseHeaders(self, response):
 79 |     if request.method == "GET" and (response.status_code == 200 or response.status_code == 304):
 80 |       now = datetime.datetime.now()
 81 |       expires_time = now + datetime.timedelta(seconds=self.app.config['SEND_FILE_MAX_AGE_DEFAULT'])
 82 |       expires_time = expires_time.replace(second=0, microsecond=0)
 83 |       extension = request.path.split(".")[-1]
 84 |       response.headers.set('Content-Type', self.contentTypes.get(extension, "text/html"))
 85 |       response.headers['Cache-Control'] = 'public, max-age=' + str(self.app.config['SEND_FILE_MAX_AGE_DEFAULT'])
 86 |       response.headers['Expires'] = format_date_time(time.mktime(expires_time.timetuple()))
 87 |       response.headers['Last-Modified'] = self.fileLastModified
 88 |     elif request.method == "GET":
 89 |       response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0'
 90 |       response.headers['Expires'] = '-1'
 91 |     return response
 92 | 
 93 |   @property
 94 |   def fileLastModified(self):
 95 |     filePath = self.translate_path(request.path)
 96 |     try:
 97 |       dt=os.path.getmtime(os.path.join(self.app.root_path, filePath))
 98 |       localLastModified = format_date_time(time.mktime(datetime.datetime.utcfromtimestamp(dt).timetuple()))
 99 |       return localLastModified
100 |     except:
101 |       pass
102 |     now = datetime.datetime.now()
103 |     return format_date_time(time.mktime(now.timetuple()))
104 | 
105 |   @property
106 |   def needsFullResponse(self):
107 |     if request.headers.get('If-Modified-Since') is not None:
108 |       remoteLastModified = request.headers.get('If-Modified-Since')
109 |       if self.fileLastModified == remoteLastModified:
110 |         return False
111 |     return True
112 | 
113 |   @property
114 |   def Id(self):
115 |     """Gets a unique questionnaire ID"""
116 |     qid = ''
117 |     if request is not None and request.path == '/vsaq.html':
118 |       if session.get('qid') is not None and (session['qid']).isalnum() and len(session['qid']) == 12:
119 |         qid = session['qid']
120 |       else:
121 |         qid = randomizer.Id()
122 |         session['qid'] = qid
123 |     else:
124 |       qid = randomizer.Id()
125 |       session['qid'] = qid
126 |     return qid
127 | 


--------------------------------------------------------------------------------
/src/app.config:
--------------------------------------------------------------------------------
 1 | # Copyright 2018 Morningstar Inc. All rights reserved.
 2 | # Licensed under the Apache License, Version 2.0 (the "License");
 3 | # you may not use this file except in compliance with the License.
 4 | # You may obtain a copy of the License at
 5 | #
 6 | # http://www.apache.org/licenses/LICENSE-2.0
 7 | #
 8 | # Unless required by applicable law or agreed to in writing, software
 9 | # distributed under the License is distributed on an "AS IS" BASIS,
10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | # See the License for the specific language governing permissions and
12 | # limitations under the License.
13 | 
14 | # configuration settings
15 | ENABLE_TEST_MODE = False
16 | ENABLE_TEST_MODE_LOCAL_ONLY = True
17 | 
18 | # MUST CHECK FOR PROD/DEBUG ENVIRONMENTS
19 | DEBUG = False
20 | # The policy of the default logging handler. The default is 'always' which means that 
21 | # the default logging handler is always active. 'debug' will only activate logging in 
22 | # debug mode, 'production' will only log in production and 'never' disables it entirely.
23 | LOGGER_HANDLER_POLICY = 'always'
24 | PORT = 80
25 | FLASK_DEBUG=0
26 | HOST_HOME_URL = 'https://example.com/vsaq.html?qpath=questionnaires/mstar_0_1.json&q='
27 | 
28 | # Logging
29 | LOG_FILE_NAME = 'Logs/GoASQ.log'
30 | # 4 MB EACH
31 | LOG_FILE_MAX_SIZE = 4194304
32 | LOG_FILE_BACKUP_COUNT = 50
33 | 
34 | # Common
35 | ALLOWED_USERNAME_CHARACTERS = "[a-zA-Z0-9\@\\\/\.]+"
36 | CERTIFICATE_FILE = 'cert.pem'
37 | DATABASE = '/tmp/flaskr.db'
38 | LOCAL_DB_MODE = '{"FILE_SYSTEM":"True", "SQLITE":"True"}'
39 | FILE_NAMING_CONVENTION = '{"conventionKeys": ["app_name", "app_champion", "app_team_email", "app_tid", "app_pid"]}'
40 | JSON_AS_ASCII = False
41 | JSONIFY_MIMETYPE = 'application/json'
42 | MAX_CONTENT_LENGTH = 102400
43 | PERMANENT_SESSION_LIFETIME = 300
44 | PRIVATE_KEY_FILE = 'key.pem'
45 | QUESTIONNAIRES_SERVED = ['mstar_0_1.json']
46 | RATE_LIMITING_DEFAULTS = ["200 per day", "50 per hour"]
47 | REMEMBER_COOKIE_HTTPONLY = True
48 | REMEMBER_COOKIE_SECURE = True
49 | SECRET_KEY = 'ahjramukneevarp'
50 | SEND_FILE_MAX_AGE_DEFAULT = 300
51 | SESSION_COOKIE_HTTPONLY = True
52 | SESSION_COOKIE_NAME = 'session'
53 | SESSION_COOKIE_PATH = '/'
54 | SESSION_COOKIE_SECURE = True
55 | SESSION_REFRESH_EACH_REQUEST = True
56 | CERF_STRICT = False
57 | THREADED = True
58 | UPLOAD_FOLDER = 'uploads/'
59 | URL_RULES = ['/', '/static/', '/login/img/', '/login/js/', '/login/css/', '/login/', '/questionnaires/', '/']
60 | PERMISSIBLE_CONTENT_LENGTHS = {'/submit':1024 * 1024, '/savedraft':1024 * 1024, '/diff':256,'/loadone':256,'/status':256,'/submissions':256,'/logout':256,'/login':256}
61 | PERMISSIBLE_CONTENT_TYPE='application/x-www-form-urlencoded'
62 | 
63 | # SSLIFY specific
64 | SSLIFY_PERMANENT = True
65 | SSLIFY_PRELOAD = True
66 | SSLIFY_SKIPS = ''
67 | SSLIFY_SUBDOMAINS = True
68 | 
69 | #LDAP
70 | LDAP_PROVIDERS = ['ldaps://ldap1.example.com','ldaps://ldap2.example.com','ldaps://ldap3.example.com','ldaps://etc.example.com']
71 | LDAP_PROVIDERS_USE_INDEX = 0
72 | LDAP_BASE_DN = 'OU=My Cool OU,DC=example,DC=com'
73 | LDAP_USER_DOMAIN = '@example.com'
74 | LDAP_SEARCH_CN = 'sAMAccountName='
75 | LDAP_USERNAME_MAX_LENGTH = 40
76 | 
77 | MAIL_SERVER_INTERNAL = 'mail.example.com'
78 | 
79 | # Email notifications about errors
80 | SERVER_ADMINS = ['admin1@example.com, admin2@example.com']
81 | MAIL_SENDER = 'YourSecurityTeam@example.com'
82 | ERROR_DIGEST_CAPACITY = 10
83 | 
84 | # Email notifications about submissions
85 | REVIEWERS = ['YourSecurityTeam@example.com']
86 | REVIEWERS_AD_GROUP = 'CN=Security Team,OU=My Cool OU,OU=Common,OU=My Cool OU,DC=example,DC=com'
87 | REVIEWERS_ADDITIONAL = ['', '']
88 | 
89 | MAIL_SEND_ATTACHMENT = True
90 | MAIL_SUBJECT = 'QID: {} : Security Architecture Review for project: {}'
91 | MAIL_BODY_DRAFT = 'Hi there,

Please note that your answers for questionnaire ID: {} have been saved as draft. This has NOT been submitted yet. Please answer any remaining question(s) and submit it soon. Feel free to share this e-mail with your colleagues and have them submit with answers to all questions.
{}{}
Thanks,
Security Team

(This is an auto-generated email by [APPLICATION_TITLE])' 92 | MAIL_BODY_SUBMITTED = 'Hello AppSec team,

I have submitted questionnaire ID: {} . Can you please review this?
Here is the project summary for your reference:

{}

Thanks,
{}

(This is an auto-generated email by [APPLICATION_TITLE])' 93 | MAIL_BODY_IN_REVIEW = 'Hi there,

Status of the questionnaire ID: {} has now changed to In-Review. You may expect to receive comments from the reviewers while they are reviewing it.
{}{}
Thanks,
Security Team

(This is an auto-generated email by [APPLICATION_TITLE])' 94 | MAIL_BODY_APPROVED = 'Hi there,

Status of the questionnaire ID: {} has now changed to Approved. You may expect to receive approval comments from the reviewers if they have not already shared.
Please note that if any pending work items were identified during this review, the team must address those at the earliest.{}{}
Thanks,
Security Team

(This is an auto-generated email by [APPLICATION_TITLE])' 95 | -------------------------------------------------------------------------------- /src/vsaq/static/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Utility functions for vsaq. 19 | */ 20 | 21 | goog.provide('vsaq.utils'); 22 | 23 | goog.require('goog.array'); 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.classlist'); 26 | goog.require('goog.dom.forms'); 27 | goog.require('goog.events'); 28 | goog.require('goog.events.EventType'); 29 | goog.require('goog.object'); 30 | goog.require('goog.string'); 31 | goog.require('goog.string.format'); 32 | goog.require('goog.structs'); 33 | 34 | 35 | /** 36 | * Adds a listener for clicks to the given element (if it exists). 37 | * @param {string} elementId The id of the element that can be clicked. 38 | * @param {!Function} callback The function that shall be called when the 39 | * element is clicked. 40 | * @return {boolean} Whether the element was found. 41 | */ 42 | vsaq.utils.addClickHandler = function(elementId, callback) { 43 | var element = goog.dom.getElement(elementId); 44 | if (element) { 45 | goog.events.listen(element, [goog.events.EventType.CLICK], callback); 46 | return true; 47 | } 48 | return false; 49 | }; 50 | 51 | 52 | /** 53 | * Adds an onclick handler to the document, and dispatches clicks on clickable 54 | * elements (marked by having the a CSS class assigned) to the correct handler. 55 | * The called function is passed the clicked element, as well as the original 56 | * event. 57 | * @param {Object.} handlers An object mapping CSS class names 58 | * to specific functions. If a click on the web page is registered, and one 59 | * of the clicked element's ancestors has the CSS class specified in the 60 | * attribute name assigned, the function passed as the value is called. 61 | */ 62 | vsaq.utils.initClickables = function(handlers) { 63 | goog.events.listen(goog.dom.getDocument(), [goog.events.EventType.CLICK], 64 | function(e) { 65 | goog.structs.forEach(handlers, function(handler, className) { 66 | var clickable = goog.dom.getAncestorByClass(e.target, className); 67 | if (clickable && 68 | !goog.dom.classlist.contains(clickable, 'maia-button-disabled')) { 69 | handler(clickable, e); 70 | } 71 | }); 72 | }); 73 | }; 74 | 75 | 76 | /** 77 | * Creates a request content out of a format string by URI encoding parameters. 78 | * @param {string} format Template string containing % specifiers. 79 | * @param {...(string|number)} var_args Values format is to be filled with. 80 | * @return {string} Formatted string with URI encoded parameters. 81 | */ 82 | vsaq.utils.createContent = function(format, var_args) { 83 | var params = goog.array.slice(arguments, 1); 84 | var encodedParams = goog.array.map(params, encodeURIComponent); 85 | goog.array.insertAt(encodedParams, format, 0); 86 | 87 | return goog.string.format.apply(null, encodedParams); 88 | }; 89 | 90 | 91 | /** 92 | * Get the value of an element, or null if the element does not exist or 93 | * does not have a value. 94 | * @param {string} el Name of element. 95 | * @return {(string|undefined)} Value of the element. 96 | */ 97 | vsaq.utils.getValue = function(el) { 98 | var elt = goog.dom.getElement(el); 99 | if (!elt) 100 | return undefined; 101 | var value = goog.object.get(elt, 'value'); 102 | if (!value) 103 | return undefined; 104 | return value; 105 | }; 106 | 107 | 108 | /** 109 | * Ads a change handler to the vendor project selector in the main menu. 110 | */ 111 | vsaq.utils.initProjectSelector = function() { 112 | var select = goog.dom.getElement('vsaq_vendor_project_selector'); 113 | if (select) { 114 | goog.events.listen(select, [goog.events.EventType.CHANGE], function(e) { 115 | var projectUrl = goog.dom.forms.getValue(e.target); 116 | if (projectUrl) { 117 | var currentSubPage = window.location.pathname.match('^/.*/'); 118 | document.location = currentSubPage + projectUrl; 119 | } 120 | }); 121 | } 122 | }; 123 | 124 | 125 | /** 126 | * Takes an argument an removes all VSAQON string concatenations, comments and 127 | * some more escaped values. 128 | * @param {string} vsaqon A string in VSAQON that contains non-JSON conform 129 | * elements. 130 | * @return {string} A JSON conform string. 131 | */ 132 | vsaq.utils.vsaqonToJson = function(vsaqon) { 133 | // Get rid of javascript-style string concatenations 134 | var text = vsaqon.replace(/" \+[ ]*\r?\n[ ]*"/g, ''); 135 | // Replace all \x escaped values with \u00-style strings 136 | text = text.replace(/\\x/g, '\\u00'); 137 | // Remove //-styled comments 138 | text = text.replace(/\n[ ]*\/\/[^\r\n]*/g, ''); 139 | return text; 140 | }; 141 | -------------------------------------------------------------------------------- /src/app.config.debug: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Morningstar Inc. All rights reserved. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # configuration settings 15 | ENABLE_TEST_MODE = False 16 | ENABLE_TEST_MODE_LOCAL_ONLY = False 17 | 18 | # MUST CHECK FOR PROD/DEBUG ENVIRONMENTS 19 | DEBUG = True 20 | # The policy of the default logging handler. The default is 'always' which means that 21 | # the default logging handler is always active. 'debug' will only activate logging in 22 | # debug mode, 'production' will only log in production and 'never' disables it entirely. 23 | LOGGER_HANDLER_POLICY = 'always' 24 | PORT = 80 25 | FLASK_DEBUG=0 26 | HOST_HOME_URL = 'https://127.0.0.1:80/vsaq.html?qpath=questionnaires/mstar_0_1.json&q=' 27 | 28 | # Logging 29 | LOG_FILE_NAME = 'Logs/GoASQ.log' 30 | # 4 MB EACH 31 | LOG_FILE_MAX_SIZE = 4194304 32 | LOG_FILE_BACKUP_COUNT = 50 33 | 34 | # Common 35 | ALLOWED_USERNAME_CHARACTERS = "[a-zA-Z0-9\@\\\/\.]+" 36 | CERTIFICATE_FILE = 'cert.pem' 37 | DATABASE = '/tmp/flaskr.db' 38 | LOCAL_DB_MODE = '{"FILE_SYSTEM":"True", "SQLITE":"True"}' 39 | FILE_NAMING_CONVENTION = '{"conventionKeys": ["app_name", "app_champion", "app_team_email", "app_tid", "app_pid"]}' 40 | JSON_AS_ASCII = False 41 | JSONIFY_MIMETYPE = 'application/json' 42 | MAX_CONTENT_LENGTH = 102400 43 | PERMANENT_SESSION_LIFETIME = 86400 44 | PRIVATE_KEY_FILE = 'key.pem' 45 | QUESTIONNAIRES_SERVED = ['mstar_0_1.json','infrastructure.json','physical_and_datacenter.json','security_privacy_programs.json','test_template.json','test_template_extension.json','webapp.json'] 46 | RATE_LIMITING_DEFAULTS = ["20000 per day", "500 per hour"] 47 | REMEMBER_COOKIE_HTTPONLY = True 48 | REMEMBER_COOKIE_SECURE = True 49 | SECRET_KEY = 'ahjramukneevarp' 50 | SEND_FILE_MAX_AGE_DEFAULT = 30 51 | SESSION_COOKIE_HTTPONLY = True 52 | SESSION_COOKIE_NAME = 'session' 53 | SESSION_COOKIE_PATH = '/' 54 | SESSION_COOKIE_SECURE = True 55 | SESSION_REFRESH_EACH_REQUEST = True 56 | CERF_STRICT = False 57 | THREADED = True 58 | UPLOAD_FOLDER = 'uploads/' 59 | URL_RULES = ['/', '/static/', '/login/img/', '/login/js/', '/login/css/', '/login/', '/questionnaires/', '/'] 60 | PERMISSIBLE_CONTENT_LENGTHS = {'/submit':1024 * 1024, '/savedraft':1024 * 1024, '/diff':256,'/loadone':256,'/status':256,'/submissions':256,'/logout':256,'/login':256} 61 | PERMISSIBLE_CONTENT_TYPE='application/x-www-form-urlencoded' 62 | 63 | # SSLIFY specific 64 | SSLIFY_PERMANENT = True 65 | SSLIFY_PRELOAD = True 66 | SSLIFY_SKIPS = '' 67 | SSLIFY_SUBDOMAINS = True 68 | 69 | #LDAP 70 | LDAP_PROVIDERS = ['ldaps://ldap1.example.com','ldaps://ldap2.example.com','ldaps://ldap3.example.com','ldaps://etc.example.com'] 71 | LDAP_PROVIDERS_USE_INDEX = 0 72 | LDAP_BASE_DN = 'OU=My Cool OU,DC=example,DC=com' 73 | LDAP_USER_DOMAIN = '@example.com' 74 | LDAP_SEARCH_CN = 'sAMAccountName=' 75 | LDAP_USERNAME_MAX_LENGTH = 40 76 | 77 | MAIL_SERVER_INTERNAL = 'mail.example.com' 78 | 79 | # Email notifications about errors 80 | SERVER_ADMINS = ['admin1@example.com, admin2@example.com'] 81 | MAIL_SENDER = 'YourSecurityTeam@example.com' 82 | ERROR_DIGEST_CAPACITY = 10 83 | 84 | # Email notifications about submissions 85 | REVIEWERS = ['YourSecurityTeam@example.com'] 86 | REVIEWERS_AD_GROUP = 'CN=Security Team,OU=My Cool OU,OU=Common,OU=My Cool OU,DC=example,DC=com' 87 | REVIEWERS_ADDITIONAL = ['', ''] 88 | 89 | MAIL_SEND_ATTACHMENT = True 90 | MAIL_SUBJECT = 'QID: {} : Security Architecture Review for project: {}' 91 | MAIL_BODY_DRAFT = 'Hi there,

Please note that your answers for questionnaire ID: {} have been saved as draft. This has NOT been submitted yet. Please answer any remaining question(s) and submit it soon. Feel free to share this e-mail with your colleagues and have them submit with answers to all questions.
{}{}
Thanks,
Security Team

(This is an auto-generated email by [APPLICATION_TITLE])' 92 | MAIL_BODY_SUBMITTED = 'Hello AppSec team,

I have submitted questionnaire ID: {} . Can you please review this?
Here is the project summary for your reference:

{}

Thanks,
{}

(This is an auto-generated email by [APPLICATION_TITLE])' 93 | MAIL_BODY_IN_REVIEW = 'Hi there,

Status of the questionnaire ID: {} has now changed to In-Review. You may expect to receive comments from the reviewers while they are reviewing it.
{}{}
Thanks,
Security Team

(This is an auto-generated email by [APPLICATION_TITLE])' 94 | MAIL_BODY_APPROVED = 'Hi there,

Status of the questionnaire ID: {} has now changed to Approved. You may expect to receive approval comments from the reviewers if they have not already shared.
Please note that if any pending work items were identified during this review, the team must address those at the earliest.{}{}
Thanks,
Security Team

(This is an auto-generated email by [APPLICATION_TITLE])' 95 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/boxitem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview A questionnaire item with a textarea. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.BoxItem'); 22 | 23 | goog.require('goog.dom'); 24 | goog.require('goog.events'); 25 | goog.require('goog.events.EventType'); 26 | goog.require('goog.soy'); 27 | goog.require('vsaq.questionnaire.items.ParseError'); 28 | goog.require('vsaq.questionnaire.items.ValueItem'); 29 | goog.require('vsaq.questionnaire.templates'); 30 | goog.require('vsaq.questionnaire.utils'); 31 | 32 | 33 | 34 | /** 35 | * A question that allows the user to answer in a textarea box. 36 | * @param {string} id An ID uniquely identifying the question. 37 | * @param {?string} conditions A string containing conditions which must be met 38 | * for the item to be visible to the user. 39 | * @param {string} caption The caption to show above the textarea. 40 | * @param {string=} opt_placeholder The placeholder text, displayed in place of 41 | * the value. 42 | * @param {string=} opt_inputPattern HTML5 pattern attribute value for the input 43 | * field. See {@link 44 | * https://html.spec.whatwg.org/multipage/forms.html#the-pattern-attribute}. 45 | * @param {string=} opt_inputTitle HTML5 title attribute value for the input 46 | * field. See {@link 47 | * https://html.spec.whatwg.org/multipage/forms.html#attr-input-title}. 48 | * @param {boolean=} opt_isRequired Iff true, the item value is required. 49 | * @param {number=} opt_maxlength HTML maxlength attribute value for the input 50 | * field. See {@link 51 | * https://html.spec.whatwg.org/multipage/forms.html#attr-fe-maxlength} 52 | * @extends {vsaq.questionnaire.items.ValueItem} 53 | * @constructor 54 | */ 55 | vsaq.questionnaire.items.BoxItem = function(id, conditions, caption, 56 | opt_placeholder, opt_inputPattern, opt_inputTitle, opt_isRequired, 57 | opt_maxlength) { 58 | goog.base(this, id, conditions, caption, opt_placeholder, opt_inputPattern, 59 | opt_inputTitle, opt_isRequired, opt_maxlength); 60 | 61 | /** 62 | * The text area where the user can provide an answer. 63 | * @type {!HTMLTextAreaElement} 64 | * @private 65 | */ 66 | this.textArea_; 67 | 68 | this.render(); 69 | }; 70 | goog.inherits(vsaq.questionnaire.items.BoxItem, 71 | vsaq.questionnaire.items.ValueItem); 72 | 73 | 74 | /** 75 | * Render the HTML for this item. 76 | */ 77 | vsaq.questionnaire.items.BoxItem.prototype.render = function() { 78 | var oldNode = this.container; 79 | this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.box, { 80 | id: this.id, 81 | captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text), 82 | placeholder: this.placeholder, 83 | inputPattern: this.inputPattern, 84 | inputTitle: this.inputTitle, 85 | isRequired: Boolean(this.required), 86 | maxlength: this.maxlength 87 | }); 88 | goog.dom.replaceNode(this.container, oldNode); 89 | 90 | this.textArea_ = /** @type {!HTMLTextAreaElement} */ 91 | (vsaq.questionnaire.utils.findById(this.container, this.id)); 92 | goog.events.listen( 93 | this.textArea_, 94 | [goog.events.EventType.KEYUP, goog.events.EventType.CHANGE], 95 | this.answerChanged, 96 | true, 97 | this); 98 | }; 99 | 100 | 101 | /** 102 | * Type of the question. This is used to distinguish questions in serialized 103 | * format. 104 | * @type {string} 105 | * @const 106 | */ 107 | vsaq.questionnaire.items.BoxItem.TYPE = 'box'; 108 | 109 | 110 | /** 111 | * Parses BoxItems. If the topmost item in the passed Array is an a 112 | * BoxItem, it is consumed and a BoxItem instance is returned. 113 | * If the topmost item is not a BoxItem, an exception is thrown. 114 | * @param {!Array.} questionStack Array of serialized 115 | * questionnaire Items. 116 | * @return {!vsaq.questionnaire.items.BoxItem} The parsed BoxItem. 117 | */ 118 | vsaq.questionnaire.items.BoxItem.parse = function(questionStack) { 119 | var item = questionStack.shift(); 120 | if (item.type != vsaq.questionnaire.items.BoxItem.TYPE) 121 | throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.'); 122 | 123 | return new vsaq.questionnaire.items.BoxItem(item.id, item.cond, item.text, 124 | item.placeholder, item.inputPattern, item.inputTitle, item.required, 125 | item.maxlength); 126 | }; 127 | 128 | 129 | /** @inheritDoc */ 130 | vsaq.questionnaire.items.BoxItem.prototype.setReadOnly = function(readOnly) { 131 | this.textArea_.readOnly = readOnly; 132 | }; 133 | 134 | 135 | /** @inheritDoc */ 136 | vsaq.questionnaire.items.BoxItem.prototype.getValue = function() { 137 | return this.textArea_.value; 138 | }; 139 | 140 | 141 | /** @inheritDoc */ 142 | vsaq.questionnaire.items.BoxItem.prototype.setInternalValue = 143 | function(value) { 144 | this.textArea_.value = /** @type {string} */ (value); 145 | }; 146 | 147 | 148 | /** @inheritDoc */ 149 | vsaq.questionnaire.items.BoxItem.prototype.isAnswered = function() { 150 | return this.getValue().length > 0; 151 | }; 152 | 153 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/utils_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Tests for vsaq.questionnaire.utils. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.UtilsTests'); 22 | goog.setTestOnly('vsaq.questionnaire.items.UtilsTests'); 23 | 24 | goog.require('goog.testing.asserts'); 25 | goog.require('goog.testing.jsunit'); 26 | goog.require('vsaq.questionnaire.utils'); 27 | 28 | 29 | /** 30 | * Tests tokenizing expressions. 31 | */ 32 | function testTokenizeExpression() { 33 | var exp1 = 'a&&b'; 34 | var exp1b = 'a &&b'; 35 | var exp1c = 'a && b'; 36 | var exp1_tok = ['a', '&', 'b']; 37 | 38 | var exp2 = '(a&&(b))'; 39 | var exp2_tok = [['a', '&', ['b']]]; 40 | 41 | var exp3 = '(a&&b)'; 42 | var exp3_tok = [['a', '&', 'b']]; 43 | 44 | var exp4 = '((aa)) && b'; 45 | var exp4_tok = [[['aa']], '&', 'b']; 46 | 47 | var exp5 = '((aa)&&(aa||bb))'; 48 | var exp5_tok = [[['aa'], '&', ['aa', '|', 'bb']]]; 49 | 50 | var exp6 = 'a&&(b||c&&!(!d||(e&&f)))&&(g||h)'; 51 | var exp6_tok = 52 | ['a', '&', ['b', '|', 'c', '&', '!', ['!', 'd', '|', ['e', '&', 'f']]], 53 | '&', ['g', '|', 'h']]; 54 | 55 | var exp7 = 'a&&b&&c(d,e)'; 56 | var exp7_tok = 57 | ['a', '&', 'b', '&', 'c', ['d', ',', 'e']]; 58 | 59 | var exp8 = 'a||"b"||c'; 60 | var exp8_tok = 61 | ['a', '|', '"b"', '|', 'c']; 62 | 63 | var exp9 = '"a\\"b\\\\\\"c\\\\"'; 64 | var exp9_tok = 65 | ['"a\\"b\\\\\\"c\\\\"']; 66 | 67 | var invalid_exp1 = 'a!&&b'; 68 | var invalid_exp2 = 'a&&(c||d'; 69 | var invalid_exp3 = 'a&b'; 70 | var invalid_exp4 = 'a&&b&&(c'; 71 | var invalid_exp5 = 'a&&b&&c)'; 72 | var invalid_exp6 = 'a&&b&&c(d'; 73 | var invalid_exp7 = 'a&&b&&c(d,'; 74 | var invalid_exp8 = 'a&&b&&c(d e)'; 75 | var invalid_exp9 = 'a(b(c)'; 76 | var invalid_expA = '"a'; 77 | var invalid_expB = '"\\"'; 78 | var invalid_expC = 'a "'; 79 | var invalid_expD = 'a "b'; 80 | 81 | 82 | var tokenize = vsaq.questionnaire.utils.tokenizeExpression; 83 | 84 | assertArrayEquals(exp1_tok, tokenize(exp1)); 85 | assertArrayEquals(exp1_tok, tokenize(exp1b)); 86 | assertArrayEquals(exp1_tok, tokenize(exp1c)); 87 | 88 | assertArrayEquals(exp2_tok, tokenize(exp2)); 89 | assertArrayEquals(exp3_tok, tokenize(exp3)); 90 | assertArrayEquals(exp4_tok, tokenize(exp4)); 91 | assertArrayEquals(exp5_tok, tokenize(exp5)); 92 | assertArrayEquals(exp6_tok, tokenize(exp6)); 93 | assertArrayEquals(exp7_tok, tokenize(exp7)); 94 | assertArrayEquals(exp8_tok, tokenize(exp8)); 95 | assertArrayEquals(exp9_tok, tokenize(exp9)); 96 | 97 | assertThrows(function() { tokenize(invalid_exp1) }); 98 | assertThrows(function() { tokenize(invalid_exp2) }); 99 | assertThrows(function() { tokenize(invalid_exp3) }); 100 | assertThrows(function() { tokenize(invalid_exp4) }); 101 | assertThrows(function() { tokenize(invalid_exp5) }); 102 | assertThrows(function() { tokenize(invalid_exp6) }); 103 | assertThrows(function() { tokenize(invalid_exp7) }); 104 | assertThrows(function() { tokenize(invalid_exp8) }); 105 | assertThrows(function() { tokenize(invalid_exp9) }); 106 | assertThrows(function() { tokenize(invalid_expA) }); 107 | assertThrows(function() { tokenize(invalid_expB) }); 108 | assertThrows(function() { tokenize(invalid_expC) }); 109 | assertThrows(function() { tokenize(invalid_expD) }); 110 | } 111 | 112 | 113 | /** 114 | * Tests evaluating expressions. 115 | */ 116 | function testEvalExpression() { 117 | var resolver = function(variable) { 118 | return variable == 'true'; 119 | }; 120 | var evalExp = vsaq.questionnaire.utils.evalExpression; 121 | 122 | var fexp1 = 'true && false'; 123 | var fexp2 = '(true && false)'; 124 | var fexp3 = 'false && false'; 125 | var fexp4 = 'false || false'; 126 | var fexp5 = 'true && (false && !true)'; 127 | var fexp6 = 'contains("abc", "b") && contains("abc", "d")'; 128 | 129 | var texp1 = 'true && true'; 130 | var texp2 = 'true && (true || false)'; 131 | var texp3 = 'false || !false'; 132 | var texp4 = 'true && (false || (!true)) || (true && true)'; 133 | var texp5 = 'matches("a\\"b\\\\\\"c\\\\", "^(z|[a]).(?:b{1,})..[asxdc].$")'; 134 | var texp6 = 'matches("abc", "B", "i")'; 135 | var texp7 = '!(matches("abc", "d"))'; 136 | var texp8 = 'contains("abc", "b")'; 137 | var texp9 = '!(contains("abc", "d"))'; 138 | var texp10 = 'contains("abc", "b") && contains("abc", "c")'; 139 | 140 | assertEquals(false, evalExp(fexp1, resolver)); 141 | assertEquals(false, evalExp(fexp2, resolver)); 142 | assertEquals(false, evalExp(fexp3, resolver)); 143 | assertEquals(false, evalExp(fexp4, resolver)); 144 | assertEquals(false, evalExp(fexp5, resolver)); 145 | assertEquals(false, evalExp(fexp6, resolver)); 146 | 147 | assertEquals(true, evalExp(texp1, resolver)); 148 | assertEquals(true, evalExp(texp2, resolver)); 149 | assertEquals(true, evalExp(texp3, resolver)); 150 | assertEquals(true, evalExp(texp4, resolver)); 151 | assertEquals(true, evalExp(texp5, resolver)); 152 | assertEquals(true, evalExp(texp6, resolver)); 153 | assertEquals(true, evalExp(texp7, resolver)); 154 | assertEquals(true, evalExp(texp8, resolver)); 155 | assertEquals(true, evalExp(texp9, resolver)); 156 | assertEquals(true, evalExp(texp10, resolver)); 157 | } 158 | -------------------------------------------------------------------------------- /src/vsaq/static/qpage_base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Main client-side renderer for questionnaire. 19 | */ 20 | 21 | goog.provide('vsaq.QpageBase'); 22 | 23 | goog.require('goog.array'); 24 | goog.require('goog.dom'); 25 | goog.require('goog.dom.TagName'); 26 | goog.require('goog.events'); 27 | goog.require('goog.events.EventType'); 28 | goog.require('goog.structs'); 29 | goog.require('goog.ui.Tooltip'); 30 | goog.require('vsaq.Questionnaire'); 31 | goog.require('vsaq.utils'); 32 | 33 | 34 | 35 | /** 36 | * Initialize the questionnaire page. 37 | * @constructor 38 | */ 39 | vsaq.QpageBase = function() { 40 | this.changes = {}; 41 | var qNode = goog.dom.getElement('_vsaq_body'); 42 | if (!qNode) { 43 | alert('Did not find an element with ID _vsaq_body to render the ' + 44 | 'questionnaire into.'); 45 | return; 46 | } 47 | this.questionnaire = new vsaq.Questionnaire(qNode); 48 | this.nextUpdateAttemptIn = vsaq.QpageBase.SAVE_TIMEOUT_LENGTH; 49 | this.isReadOnly = goog.dom.getElement('_rom_').value == 'true'; 50 | 51 | this.statusIndicator = goog.dom.getElement('_vsaq_saved_status') || 52 | goog.dom.createDom(goog.dom.TagName.SPAN); 53 | this.questionnaire.setReadOnlyMode(this.isReadOnly); 54 | 55 | vsaq.utils.initClickables({ 56 | 'eh-edit': goog.bind(this.makeEditable, this) 57 | }); 58 | 59 | goog.events.listen(window, [goog.events.EventType.BEFOREUNLOAD], 60 | function() { 61 | if (vsaq.qpageObject_ && vsaq.qpageObject_.unsavedChanges()) 62 | return 'You have unsaved changes.'; 63 | }); 64 | }; 65 | 66 | 67 | /** 68 | * Length of timeout to automatically save updates (in seconds). 69 | * @type {number} 70 | * @const 71 | * @protected 72 | */ 73 | vsaq.QpageBase.SAVE_TIMEOUT_LENGTH = 2; 74 | 75 | 76 | /** 77 | * The questionnaire shown on the page. 78 | * @type {vsaq.Questionnaire} 79 | */ 80 | vsaq.QpageBase.prototype.questionnaire; 81 | 82 | 83 | /** 84 | * A dictionary containing all the changes since the last successful save. Keys 85 | * are the ids of the changed questions, values are the updated values of the 86 | * question. 87 | * @type {Object.} 88 | * @protected 89 | */ 90 | vsaq.QpageBase.prototype.changes; 91 | 92 | 93 | /** 94 | * TimeoutID for timeout that saves changes every few seconds. 95 | * @type {number} 96 | * @protected 97 | */ 98 | vsaq.QpageBase.prototype.saveTimeout; 99 | 100 | 101 | /** 102 | * The current timeout for saving changes (in sec). 103 | * @type {number} 104 | * @protected 105 | */ 106 | vsaq.QpageBase.prototype.nextUpdateAttemptIn; 107 | 108 | 109 | /** 110 | * Whether or not the questionnaire is read-only. 111 | * @type {boolean} 112 | * @protected 113 | */ 114 | vsaq.QpageBase.prototype.isReadOnly; 115 | 116 | 117 | /** 118 | * Whether or not the questionnaire is read-only. 119 | * @type {!Element} 120 | * @protected 121 | */ 122 | vsaq.QpageBase.prototype.statusIndicator; 123 | 124 | 125 | /** 126 | * Make questionnaire editable. 127 | */ 128 | vsaq.QpageBase.prototype.makeEditable = function() { 129 | this.isReadOnly = false; 130 | this.questionnaire.setReadOnlyMode(this.isReadOnly); 131 | this.questionnaire.render(); 132 | }; 133 | 134 | 135 | /** 136 | * Attempts to keep track of updates that were done to the current 137 | * questionnaire. 138 | */ 139 | vsaq.QpageBase.prototype.sendUpdate = goog.abstractMethod; 140 | 141 | 142 | /** 143 | * Creates tooltips for all span elements with the class "tooltip-link". The 144 | * content of the tooltip is taken from the data-html-tooltip attribute. 145 | * @protected 146 | */ 147 | vsaq.QpageBase.prototype.installToolTips = function() { 148 | var ttElements = goog.dom.getElementsByClass('tooltip-link', 149 | this.questionnaire.getRootElement()); 150 | goog.array.forEach(ttElements, function(element) { 151 | var tip = new goog.ui.Tooltip(element); 152 | tip.setText(element.getAttribute('data-tooltip')); 153 | tip.className = 'vsaq-tooltip'; 154 | }); 155 | }; 156 | 157 | 158 | /** 159 | * Sets a timeout for when to next attempt to save changes. 160 | * @param {boolean} lastSaveFailed Whether or not the last attempt to save 161 | * updates failed. 162 | * @protected 163 | */ 164 | vsaq.QpageBase.prototype.scheduleNextUpdate = function(lastSaveFailed) { 165 | if (lastSaveFailed) { 166 | if (this.nextUpdateAttemptIn <= 256) 167 | this.nextUpdateAttemptIn *= 2; 168 | } else { 169 | this.nextUpdateAttemptIn = vsaq.QpageBase.SAVE_TIMEOUT_LENGTH; 170 | } 171 | 172 | this.saveTimeout = window.setTimeout( 173 | goog.bind(this.sendUpdate, this), this.nextUpdateAttemptIn * 1000); 174 | }; 175 | 176 | 177 | /** 178 | * Loads the template of a questionnaire. Once completed, tries to load the 179 | * answers as well. 180 | */ 181 | vsaq.QpageBase.prototype.loadQuestionnaire = goog.abstractMethod; 182 | 183 | 184 | /** 185 | * Returns whether there are unsaved changes. 186 | * @return {boolean} Returns true if there are unsaved changes. 187 | */ 188 | vsaq.QpageBase.prototype.unsavedChanges = function() { 189 | return (goog.structs.getCount(this.changes) > 0); 190 | }; 191 | 192 | 193 | /** 194 | * Instance of the questionnaire page for the current page. 195 | * @type {vsaq.QpageBase} 196 | * @protected 197 | */ 198 | vsaq.qpageObject_; 199 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/lineitem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview A questionnaire item with an input field. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.LineItem'); 22 | 23 | goog.require('goog.dom'); 24 | goog.require('goog.events'); 25 | goog.require('goog.events.EventType'); 26 | goog.require('goog.soy'); 27 | goog.require('vsaq.questionnaire.items.ParseError'); 28 | goog.require('vsaq.questionnaire.items.ValueItem'); 29 | goog.require('vsaq.questionnaire.templates'); 30 | goog.require('vsaq.questionnaire.utils'); 31 | 32 | 33 | 34 | /** 35 | * A question that allows the user to answer in an input field. 36 | * @param {string} id An ID uniquely identifying the question. 37 | * @param {?string} conditions A string containing conditions which must be met 38 | * for the item to be visible to the user. 39 | * @param {string} caption The caption to show above the input field. 40 | * @param {string=} opt_inputType The type of the stored value. 41 | * @param {string=} opt_placeholder The placeholder text, displayed in place of 42 | * the value. 43 | * @param {string=} opt_inputPattern HTML5 pattern attribute value for the input 44 | * field. See {@link 45 | * https://html.spec.whatwg.org/multipage/forms.html#the-pattern-attribute}. 46 | * @param {string=} opt_inputTitle HTML5 title attribute value for the input 47 | * field. See {@link 48 | * https://html.spec.whatwg.org/multipage/forms.html#attr-input-title}. 49 | * @param {boolean=} opt_isRequired Iff true, the item value is required. 50 | * @param {number=} opt_maxlength HTML maxlength attribute value for the input 51 | * field. See {@link 52 | * https://html.spec.whatwg.org/multipage/forms.html#attr-fe-maxlength} 53 | * @extends {vsaq.questionnaire.items.ValueItem} 54 | * @constructor 55 | */ 56 | vsaq.questionnaire.items.LineItem = function(id, conditions, caption, 57 | opt_inputType, opt_placeholder, opt_inputPattern, opt_inputTitle, 58 | opt_isRequired, opt_maxlength) { 59 | goog.base(this, id, conditions, caption, opt_placeholder, opt_inputPattern, 60 | opt_inputTitle, opt_isRequired, opt_maxlength); 61 | 62 | /** 63 | * The html input element where the user can answer the question. 64 | * @type {!HTMLInputElement} 65 | * @private 66 | */ 67 | this.textBox_; 68 | 69 | /** 70 | * The type of the stored value. 71 | * @type {string|undefined} 72 | */ 73 | this.inputType = opt_inputType; 74 | 75 | this.render(); 76 | }; 77 | goog.inherits(vsaq.questionnaire.items.LineItem, 78 | vsaq.questionnaire.items.ValueItem); 79 | 80 | 81 | /** 82 | * Render the HTML for this item. 83 | */ 84 | vsaq.questionnaire.items.LineItem.prototype.render = function() { 85 | var oldNode = this.container; 86 | this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.line, 87 | { 88 | id: this.id, 89 | captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text), 90 | type: this.inputType, 91 | inputPattern: this.inputPattern, 92 | inputTitle: this.inputTitle, 93 | placeholder: this.placeholder, 94 | isRequired: Boolean(this.required), 95 | maxlength: this.maxlength 96 | }); 97 | goog.dom.replaceNode(this.container, oldNode); 98 | 99 | this.textBox_ = /** @type {!HTMLInputElement} */ 100 | (vsaq.questionnaire.utils.findById(this.container, this.id)); 101 | goog.events.listen( 102 | this.textBox_, 103 | [goog.events.EventType.KEYUP, goog.events.EventType.CHANGE], 104 | this.answerChanged, 105 | true, 106 | this); 107 | }; 108 | 109 | 110 | /** 111 | * Type of the question. This is used to distinguish questions in serialized 112 | * format. 113 | * @type {string} 114 | * @const 115 | */ 116 | vsaq.questionnaire.items.LineItem.TYPE = 'line'; 117 | 118 | 119 | /** 120 | * Parses LineItems. If the topmost item in the passed Array is an a 121 | * LineItem, it is consumed and a LineItem instance is returned. 122 | * If the topmost item is not a LineItem, an exception is thrown. 123 | * @param {!Array.} questionStack Array of serialized 124 | * questionnaire Items. 125 | * @return {!vsaq.questionnaire.items.LineItem} The parsed LineItem. 126 | */ 127 | vsaq.questionnaire.items.LineItem.parse = function(questionStack) { 128 | var item = questionStack.shift(); 129 | if (item.type != vsaq.questionnaire.items.LineItem.TYPE) 130 | throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.'); 131 | 132 | return new vsaq.questionnaire.items.LineItem(item.id, item.cond, item.text, 133 | item.inputType, item.placeholder, item.inputPattern, item.inputTitle, 134 | item.required, item.maxlength); 135 | }; 136 | 137 | 138 | /** @inheritDoc */ 139 | vsaq.questionnaire.items.LineItem.prototype.setReadOnly = function(readOnly) { 140 | this.textBox_.readOnly = readOnly; 141 | }; 142 | 143 | 144 | /** @inheritDoc */ 145 | vsaq.questionnaire.items.LineItem.prototype.getValue = function() { 146 | return this.textBox_.value; 147 | }; 148 | 149 | 150 | /** @inheritDoc */ 151 | vsaq.questionnaire.items.LineItem.prototype.setInternalValue = 152 | function(value) { 153 | this.textBox_.value = /** @type {string} */ (value); 154 | }; 155 | 156 | 157 | /** @inheritDoc */ 158 | vsaq.questionnaire.items.LineItem.prototype.isAnswered = function() { 159 | return this.getValue().length > 0; 160 | }; 161 | -------------------------------------------------------------------------------- /src/goasq_server.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Morningstar Inc. All rights reserved. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | """Runs a server for General Open Architecture Security Questionnaire (GOASQ).""" 15 | 16 | import logging, os.path, ssl 17 | 18 | from cryptoUtils import Cryptor 19 | from customflask import customFlask 20 | from datetime import timedelta 21 | from flask import abort, Flask, jsonify, request, send_from_directory, session 22 | from flask_limiter import Limiter 23 | from flask_limiter.util import get_remote_address 24 | from flask_sslify import SSLify 25 | from flask.views import MethodView 26 | from functools import wraps 27 | from globals import generate_csrf_token, sharedSession, setup_Defaults, setup_Logging, setup_Notification_For_Errors, setup_Notification_For_Reviews 28 | from postHandler import PostHandler 29 | from renderer import Renderer 30 | from translator import Translator 31 | from werkzeug.exceptions import default_exceptions, HTTPException 32 | 33 | template_dir = os.path.dirname(__file__) 34 | app = customFlask(__name__, template_folder=template_dir) 35 | app.config.from_envvar('APP_SETTINGS', silent=True) 36 | translator = Translator() 37 | renderer = Renderer(app) 38 | postHandler = PostHandler(app) 39 | 40 | class ServerRequestHandler(MethodView): 41 | """Request handler for GoASQ server.""" 42 | 43 | def get(self, pathParam): 44 | logging.debug("ServerRequestHandler.get:%s, pathParam:%s", request.remote_addr, pathParam); 45 | if request.path == "/loadone": 46 | return postHandler.handle_request() 47 | else: 48 | return renderer.handle_request() 49 | 50 | def post(self, pathParam): 51 | logging.debug("ServerRequestHandler.post:%s, pathParam:%s", request.remote_addr, pathParam); 52 | return postHandler.handle_request() 53 | 54 | def head(self, pathParam): 55 | logging.debug("ServerRequestHandler.head:%s, pathParam:%s", request.remote_addr, pathParam); 56 | logging.error("ServerRequestHandler.head: Unsupported HEAD request from remote IP: %s, pathParam:%s", request.remote_addr, pathParam); 57 | return jsonify(error="Not Allowed") 58 | 59 | def options(self, pathParam): 60 | logging.debug("ServerRequestHandler.options:%s, pathParam:%s", request.remote_addr, pathParam); 61 | logging.error("ServerRequestHandler.options: Unsupported OPTIONS request from remote IP: %s, pathParam:%s", request.remote_addr, pathParam); 62 | return jsonify(error="Not Allowed") 63 | 64 | @app.errorhandler(Exception) 65 | def handle_error(e): 66 | code = 500 67 | logging.error('goasq_server.errorhandler:Error in server application:\n\n' + 68 | repr(e), exc_info=True) 69 | if isinstance(e, HTTPException): 70 | code = e.code 71 | error = "No donuts for you!" 72 | if app.config['DEBUG']: 73 | error = str(e) 74 | r = jsonify(error=error, csrf=generate_csrf_token()) 75 | logging.error('ServerRequestHandler.handle_error:' + str(r) + '\n\n' + repr(e), exc_info=True) 76 | return r, code 77 | 78 | @app.before_request 79 | def csrf_protect(): 80 | session.permanent = True 81 | if request.method == "POST": 82 | token = session.pop('_csrf_token', None) 83 | if app.config['CERF_STRICT'] == True and (token is None or token != request.form.get('_xsrf_')): 84 | logging.debug("GLOBAL.csrf_protect:Token mismatch(Session: %s, Request: %s)", 85 | str(token), 86 | str(request.form.get('_xsrf_'))) 87 | postHandler.clearAndLogoutSession() 88 | abort(403) 89 | if request.path != "/login" and request.path != "/logout": 90 | eUsername = request.cookies.get('u') 91 | cryptor = Cryptor() 92 | if eUsername is not None: 93 | dUsername = cryptor.decrypt(eUsername) 94 | if session.get('_eUser') != eUsername or session.get('_user') != dUsername: 95 | postHandler.clearAndLogoutSession() 96 | abort(401) 97 | else: 98 | postHandler.clearAndLogoutSession() 99 | abort(401) 100 | 101 | @app.after_request 102 | def update_response_header(response): 103 | extension = request.path.split(".")[-1] 104 | if extension != "html" and "text/html" in response.headers.get('Content-Type'): 105 | response.headers.set('Content-Type', "application/json; charset=utf-8") 106 | return response 107 | 108 | @app.route('/favicon.ico') 109 | def favicon(): 110 | return send_from_directory(os.path.join(app.root_path, 'build/static'), 111 | 'favicon.ico', mimetype='image/vnd.microsoft.icon') 112 | 113 | def add_url_rules(): 114 | viewCounter = 0 115 | for key in app.config['URL_RULES']: 116 | if key == '/': 117 | app.add_url_rule(key, view_func=ServerRequestHandler.as_view('GOASQ_'+str(viewCounter)), defaults={'pathParam': ''}) 118 | else: 119 | app.add_url_rule(key, view_func=ServerRequestHandler.as_view('GOASQ_'+str(viewCounter))) 120 | viewCounter += 1 121 | 122 | def run_app(): 123 | if __name__ == '__main__': 124 | context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) 125 | context.options |= ssl.OP_NO_SSLv2 126 | context.options |= ssl.OP_NO_SSLv3 127 | context.load_cert_chain(app.config['CERTIFICATE_FILE'], app.config['PRIVATE_KEY_FILE']) 128 | SSLify(app, ssl_debug=app.config['DEBUG']) 129 | for ex in default_exceptions: 130 | app.register_error_handler(ex, handle_error) 131 | Limiter(app, 132 | key_func=get_remote_address, 133 | default_limits=app.config['RATE_LIMITING_DEFAULTS']) 134 | logging.debug("Rate limiting set to:%s", app.config['RATE_LIMITING_DEFAULTS']) 135 | app.run(host='0.0.0.0', 136 | port=app.config['PORT'], 137 | ssl_context=context, 138 | threaded=app.config['THREADED'], 139 | debug=app.config['DEBUG']) 140 | 141 | print("------############################################------") 142 | setup_Defaults(app) 143 | add_url_rules() 144 | setup_Logging(app) 145 | if not app.config['DEBUG']: 146 | setup_Notification_For_Errors(app) 147 | setup_Notification_For_Reviews(app) 148 | run_app() 149 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/containeritem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview A container item that can contain further items. A typical 19 | * container item could be a blockitem or groupitem. 20 | */ 21 | 22 | goog.provide('vsaq.questionnaire.items.ContainerItem'); 23 | 24 | 25 | goog.require('goog.array'); 26 | goog.require('goog.dom'); 27 | goog.require('vsaq.questionnaire.items.Item'); 28 | 29 | 30 | 31 | /** 32 | * A container of items e.g. blockitem or groupitems. 33 | * @inheritDoc 34 | * @extends {vsaq.questionnaire.items.Item} 35 | * @constructor 36 | */ 37 | vsaq.questionnaire.items.ContainerItem = function(id, conditions) { 38 | goog.base(this, id, conditions); 39 | 40 | /** 41 | * An array of contained items. 42 | * @type {!vsaq.questionnaire.items.ItemArray} 43 | * @protected 44 | */ 45 | this.containerItems = []; 46 | }; 47 | goog.inherits(vsaq.questionnaire.items.ContainerItem, 48 | vsaq.questionnaire.items.Item); 49 | 50 | 51 | /** 52 | * Return all container items. 53 | * @return {!vsaq.questionnaire.items.ItemArray} all container items. 54 | */ 55 | vsaq.questionnaire.items.ContainerItem.prototype.getContainerItems = 56 | function() { 57 | return this.containerItems; 58 | }; 59 | 60 | 61 | /** 62 | * Adds a subitem to the current container. 63 | * @param {!vsaq.questionnaire.items.Item} item The item to be added 64 | * to the container. 65 | */ 66 | vsaq.questionnaire.items.ContainerItem.prototype.addItem = function(item) { 67 | this.containerItems.push(item); 68 | item.parentItem = this; 69 | this.container.appendChild(item.container); 70 | }; 71 | 72 | 73 | /** 74 | * Returns the passed item's index within the internal containerItems array. 75 | * @param {!vsaq.questionnaire.items.Item} item The item to be found. 76 | * @return {?number} The index of the item within subItem. 77 | * @private 78 | */ 79 | vsaq.questionnaire.items.ContainerItem.prototype.getContainerItemId_ = 80 | function(item) { 81 | var targetSubItemId = null; 82 | var subItemCounter = 0; 83 | goog.array.some(this.containerItems, function(subItem) { 84 | if (subItem.id == item.id) { 85 | targetSubItemId = subItemCounter; 86 | return true; 87 | } 88 | subItemCounter++; 89 | return false; 90 | }); 91 | return targetSubItemId; 92 | }; 93 | 94 | 95 | /** 96 | * Returns the upper or lower sibling of a given item. 97 | * @param {!vsaq.questionnaire.items.Item} item The reference item. 98 | * @param {!boolean} getLowerSibling true if the lower sibling should be 99 | * returned, else the upper sibling will be returned. 100 | * @return {?vsaq.questionnaire.items.Item} The upper or lower sibling. 101 | */ 102 | vsaq.questionnaire.items.ContainerItem.prototype.getSiblingItem = function(item, 103 | getLowerSibling) { 104 | var targetSubItemId = this.getContainerItemId_(item); 105 | if (targetSubItemId == null) 106 | return null; 107 | var numbercontainerItems_ = this.containerItems.length; 108 | var siblingItem = null; 109 | if (getLowerSibling) { 110 | if (targetSubItemId + 1 < numbercontainerItems_) 111 | siblingItem = this.containerItems[targetSubItemId + 1]; 112 | } else if (targetSubItemId - 1 >= 0) { 113 | siblingItem = this.containerItems[targetSubItemId - 1]; 114 | } 115 | return siblingItem; 116 | }; 117 | 118 | 119 | /** 120 | * Adds a subitem after or before a specific other item in the current 121 | * container. 122 | * @param {!vsaq.questionnaire.items.Item} newItem The item to be inserted. 123 | * @param {!vsaq.questionnaire.items.Item} srcItem The item to which newItem 124 | * is inserted relative to. 125 | * @param {boolean} insertBelow true if newItem is supposed to be inserted 126 | * below srcItem. 127 | * @private 128 | */ 129 | vsaq.questionnaire.items.ContainerItem.prototype.insertItemRelativeTo_ = 130 | function(newItem, srcItem, insertBelow) { 131 | 132 | var srcSubItemId = this.getContainerItemId_(srcItem); 133 | if (srcSubItemId == null) 134 | return; 135 | 136 | var newItemPosition; 137 | if (insertBelow) { 138 | newItemPosition = srcSubItemId + 1; 139 | } else { 140 | // We prepend newItem by inserting it at the same position as srcItem. 141 | // All consecutive items (including srcItem) will then be moved to a 142 | // higher position. 143 | newItemPosition = srcSubItemId; 144 | } 145 | 146 | // Insert newItem directly after srcItem within the containerItems array. 147 | goog.array.insertAt(this.containerItems, newItem, newItemPosition); 148 | newItem.parentItemSet(this); 149 | }; 150 | 151 | 152 | /** 153 | * Adds a subitem after a specific other item in the current container. 154 | * @param {!vsaq.questionnaire.items.Item} newItem The item to be appended to 155 | * the container. 156 | * @param {!vsaq.questionnaire.items.Item} srcItem The item after which the 157 | * newItem will be inserted. 158 | */ 159 | vsaq.questionnaire.items.ContainerItem.prototype.insertAfter = function(newItem, 160 | srcItem) { 161 | this.insertItemRelativeTo_(newItem, srcItem, true); 162 | goog.dom.insertSiblingAfter(newItem.container, srcItem.container); 163 | }; 164 | 165 | 166 | /** 167 | * Adds a subitem before a specific other item in the current container. 168 | * @param {!vsaq.questionnaire.items.Item} newItem The item to be appended to 169 | * the container. 170 | * @param {!vsaq.questionnaire.items.Item} srcItem The item before which the 171 | * newItem will be inserted. 172 | */ 173 | vsaq.questionnaire.items.ContainerItem.prototype.insertBefore = 174 | function(newItem, srcItem) { 175 | this.insertItemRelativeTo_(newItem, srcItem, false); 176 | goog.dom.insertSiblingBefore(newItem.container, srcItem.container); 177 | }; 178 | 179 | 180 | /** 181 | * Remove an item from a container. 182 | * @param {!vsaq.questionnaire.items.Item} item The to be deleted item. 183 | */ 184 | vsaq.questionnaire.items.ContainerItem.prototype.deleteItem = function(item) { 185 | var targetSubItemId = this.getContainerItemId_(item); 186 | if (targetSubItemId == null) 187 | return; 188 | // Remove the target from the array and close the new gap again. 189 | goog.array.removeAt(this.containerItems, targetSubItemId); 190 | goog.dom.removeNode(item.container); 191 | }; 192 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/blockitems.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview Questionnaire block items. 19 | * 20 | * Questionnaires allow to group questions in blocks. Blocks are indicated by 21 | * {vsaq.questionnaire.items.BlockItem}. 22 | */ 23 | 24 | goog.provide('vsaq.questionnaire.items.BlockItem'); 25 | 26 | goog.require('goog.array'); 27 | goog.require('goog.dom'); 28 | goog.require('goog.soy'); 29 | goog.require('goog.string'); 30 | goog.require('vsaq.questionnaire.items.ContainerItem'); 31 | goog.require('vsaq.questionnaire.items.Item'); 32 | goog.require('vsaq.questionnaire.items.ParseError'); 33 | goog.require('vsaq.questionnaire.items.RadioItem'); 34 | goog.require('vsaq.questionnaire.items.ValueItem'); 35 | goog.require('vsaq.questionnaire.templates'); 36 | 37 | 38 | 39 | /** 40 | * Starts a new block in the questionnaire. 41 | * @param {?string} id An ID uniquely identifying the item. 42 | * @param {?string} conditions A string containing conditions which must be met 43 | * for the item to be visible to the user. 44 | * @param {?string} caption The caption of the block. 45 | * @param {?string=} opt_auth The needed authorization to get an item displayed. 46 | * The auth param on {@code vsaq.questionnaire.items.BlockItem} only 47 | * prevents that items are displayed to the user (hidden by display=none). 48 | * @param {?string=} opt_className Name of a CSS class to add to the block. 49 | * @extends {vsaq.questionnaire.items.ContainerItem} 50 | * @constructor 51 | */ 52 | vsaq.questionnaire.items.BlockItem = function(id, conditions, caption, 53 | opt_auth, opt_className) { 54 | goog.base(this, id, conditions); 55 | 56 | /** 57 | * The needed authorization to get an item displayed. 58 | * @type {string} 59 | */ 60 | this.auth = goog.string.makeSafe(opt_auth); 61 | var propertyInformation = { 62 | nameInClass: 'auth', 63 | defaultValues: { 64 | admin: 'admin' 65 | }, 66 | metadata: true 67 | }; 68 | this.addPropertyInformation('auth', propertyInformation); 69 | 70 | /** 71 | * Extra class to add to the item. 72 | * Stores the className attribute from the JSON definition. 73 | * @type {string} 74 | */ 75 | this.className = goog.string.makeSafe(opt_className); 76 | propertyInformation = { 77 | nameInClass: 'className', 78 | defaultValues: { 79 | vsaq_invisible: 'vsaq-invisible' 80 | }, 81 | metadata: true 82 | }; 83 | this.addPropertyInformation('className', propertyInformation); 84 | 85 | /** 86 | * Text shown at the top of the block. 87 | * @type {string} 88 | */ 89 | this.text = goog.string.makeSafe(caption); 90 | propertyInformation = { 91 | nameInClass: 'text', 92 | mandatory: true 93 | }; 94 | this.addPropertyInformation('text', propertyInformation); 95 | 96 | this.render(); 97 | }; 98 | goog.inherits(vsaq.questionnaire.items.BlockItem, 99 | vsaq.questionnaire.items.ContainerItem); 100 | 101 | 102 | /** 103 | * Render the HTML for this item. 104 | */ 105 | vsaq.questionnaire.items.BlockItem.prototype.render = function() { 106 | var oldNode = this.container; 107 | this.container = (goog.soy.renderAsElement( 108 | vsaq.questionnaire.templates.block, { 109 | id: this.id, 110 | captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text), 111 | blockId: this.id 112 | })); 113 | // Append all children. 114 | goog.array.forEach(this.containerItems, function(item) { 115 | this.container.appendChild(item.container); 116 | }, this); 117 | goog.dom.replaceNode(this.container, oldNode); 118 | }; 119 | 120 | 121 | /** 122 | * Returns the number of unanswered questions in the block. 123 | * @return {number} The number of unanswered questions in the block. 124 | */ 125 | vsaq.questionnaire.items.BlockItem.prototype.getUnansweredCount = 126 | function() { 127 | var count = 0, radioChecked = false, hasRadio = false; 128 | 129 | goog.array.forEach(this.containerItems, function(item) { 130 | if (item instanceof vsaq.questionnaire.items.RadioItem) { 131 | hasRadio = true; 132 | if (item.isChecked()) radioChecked = true; 133 | return; 134 | } 135 | // If we come across a ValueItem that is visible and not answered, 136 | // increment the counter. 137 | if (item instanceof vsaq.questionnaire.items.ValueItem && 138 | item.isVisible() && 139 | !item.isAnswered()) 140 | count++; 141 | // Finally, if we come across a Block, count everything in that block 142 | // recursively. 143 | if (item instanceof vsaq.questionnaire.items.BlockItem) 144 | count += item.getUnansweredCount(); 145 | }, this); 146 | 147 | // If there are radio buttons and none is checked, count as one. 148 | if (hasRadio && !radioChecked) count++; 149 | 150 | return count; 151 | }; 152 | 153 | 154 | /** 155 | * Parses BlockItems. If the topmost item in the passed Array is a 156 | * BlockItem, it is consumed and a BlockItem instance is returned. 157 | * If the topmost item is not a BlockItem, an exception is thrown. 158 | * @param {!Array.} questionStack Array of serialized 159 | * questionnaire items. 160 | * @return {!vsaq.questionnaire.items.BlockItem} The parsed BlockItem. 161 | */ 162 | vsaq.questionnaire.items.BlockItem.parse = function(questionStack) { 163 | var item = questionStack.shift(); 164 | if (item.type != vsaq.questionnaire.items.BlockItem.TYPE) 165 | throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.'); 166 | 167 | return new vsaq.questionnaire.items.BlockItem(item.id, item.cond, 168 | item.text, item.auth, item.className); 169 | }; 170 | 171 | 172 | /** @inheritDoc */ 173 | vsaq.questionnaire.items.BlockItem.prototype.exportItem = function() { 174 | var exportProperties = 175 | vsaq.questionnaire.items.Item.prototype.exportItem.call(this); 176 | var containerItems = []; 177 | goog.array.forEach(this.containerItems, function(item) { 178 | containerItems.push(item.exportItem()); 179 | }); 180 | exportProperties.set('items', containerItems); 181 | return exportProperties; 182 | }; 183 | 184 | 185 | /** 186 | * Type of the question. This is used to distinguish questions in serialized 187 | * format. 188 | * @type {string} 189 | * @const 190 | */ 191 | vsaq.questionnaire.items.BlockItem.TYPE = 'block'; 192 | 193 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/radioitem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview A questionnaire item with a radio button. 19 | */ 20 | 21 | goog.provide('vsaq.questionnaire.items.RadioItem'); 22 | 23 | goog.require('goog.array'); 24 | goog.require('goog.dom'); 25 | goog.require('goog.events'); 26 | goog.require('goog.events.EventType'); 27 | goog.require('goog.soy'); 28 | goog.require('vsaq.questionnaire.items.Item'); 29 | goog.require('vsaq.questionnaire.items.ParseError'); 30 | goog.require('vsaq.questionnaire.items.ValueItem'); 31 | goog.require('vsaq.questionnaire.templates'); 32 | goog.require('vsaq.questionnaire.utils'); 33 | 34 | 35 | 36 | /** 37 | * A question that allows the user to answer by choosing a radio button. 38 | * @param {string} id An ID uniquely identifying the question. 39 | * @param {?string} conditions A string containing conditions which must be met 40 | * for the item to be visible to the user. 41 | * @param {string} caption The caption to show next to the radio button. 42 | * @extends {vsaq.questionnaire.items.ValueItem} 43 | * @constructor 44 | */ 45 | vsaq.questionnaire.items.RadioItem = function(id, conditions, caption) { 46 | goog.base(this, id, conditions, caption); 47 | 48 | /** 49 | * The radio button that is the actual control behind this question. 50 | * @type {!HTMLInputElement} 51 | * @private 52 | */ 53 | this.radioButton; 54 | 55 | this.render(); 56 | }; 57 | goog.inherits(vsaq.questionnaire.items.RadioItem, 58 | vsaq.questionnaire.items.ValueItem); 59 | 60 | 61 | /** 62 | * Render the HTML for this item. 63 | */ 64 | vsaq.questionnaire.items.RadioItem.prototype.render = function() { 65 | var oldNode = this.container; 66 | this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.radio, 67 | { 68 | id: this.id, 69 | captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text) 70 | }); 71 | goog.dom.replaceNode(this.container, oldNode); 72 | 73 | this.radioButton = /** @type {!HTMLInputElement} */ ( 74 | vsaq.questionnaire.utils.findById(this.container, this.id)); 75 | goog.events.listen(this.radioButton, 76 | [goog.events.EventType.CHANGE], 77 | function(e) { 78 | // We are only changing the answer for the set radio-button. 79 | if (this.radioButton.checked) this.answerChanged(); 80 | }, true, this); 81 | }; 82 | 83 | 84 | /** 85 | * Constant indicating the string value of the radio item when selected. 86 | * @type {string} 87 | */ 88 | vsaq.questionnaire.items.RadioItem.CHECKED_VALUE = 'checked'; 89 | 90 | 91 | /** 92 | * Type of the question. This is used to distinguish questions in serialized 93 | * format. 94 | * @type {string} 95 | * @const 96 | */ 97 | vsaq.questionnaire.items.RadioItem.TYPE = 'radio'; 98 | 99 | 100 | /** 101 | * Parses RadioItems. If the topmost item in the passed Array is an a 102 | * RadioItem, it is consumed and a RadioItem instance is returned. 103 | * If the topmost item is not a RadioItem, an exception is thrown. 104 | * @param {!Array.} questionStack Array of serialized 105 | * questionnaire Items. 106 | * @return {!vsaq.questionnaire.items.RadioItem} The parsed RadioItem. 107 | */ 108 | vsaq.questionnaire.items.RadioItem.parse = function(questionStack) { 109 | var item = questionStack.shift(); 110 | if (item.type != vsaq.questionnaire.items.RadioItem.TYPE) 111 | throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.'); 112 | 113 | return new vsaq.questionnaire.items.RadioItem(item.id, item.cond, item.text); 114 | }; 115 | 116 | 117 | /** @inheritDoc */ 118 | vsaq.questionnaire.items.RadioItem.prototype.setReadOnly = function(readOnly) { 119 | this.radioButton.disabled = readOnly; 120 | }; 121 | 122 | 123 | /** @inheritDoc */ 124 | vsaq.questionnaire.items.RadioItem.prototype.isChecked = function(opt_value) { 125 | return this.radioButton.checked; 126 | }; 127 | 128 | 129 | /** 130 | * If a radio item is changed, all other radio items in the same container need 131 | * to have their value reset. For this reason, {@code 132 | * vsaq.questionnaire.items.RadioItem} overrides the answerChanged method to 133 | * ensure this happens. 134 | * @protected 135 | */ 136 | vsaq.questionnaire.items.RadioItem.prototype.answerChanged = function() { 137 | var changes = {}; 138 | var containerItems = this.parentItem.getContainerItems(); 139 | goog.array.forEach(containerItems, function(item) { 140 | if (item instanceof vsaq.questionnaire.items.RadioItem) 141 | changes[item.id] = item.getValue(); 142 | }); 143 | changes[this.id] = this.getValue(); 144 | var ev = { 145 | type: vsaq.questionnaire.items.Item.CHANGED, 146 | source: this, 147 | changes: changes 148 | }; 149 | this.eventDispatcher.dispatchEvent(ev); 150 | }; 151 | 152 | 153 | /** @inheritDoc */ 154 | vsaq.questionnaire.items.RadioItem.prototype.getValue = function() { 155 | return this.radioButton.checked ? 156 | vsaq.questionnaire.items.RadioItem.CHECKED_VALUE : ''; 157 | }; 158 | 159 | 160 | /** 161 | * When the item is added to a parent container, the name of the radio item is 162 | * set, so all radio items in a given container have the same name and only one 163 | * can be selected at a time. 164 | * @param {!vsaq.questionnaire.items.ContainerItem} parentItem The container the 165 | * radio item was added to. 166 | */ 167 | vsaq.questionnaire.items.RadioItem.prototype.parentItemSet = function( 168 | parentItem) { 169 | this.parentItem = parentItem; 170 | this.radioButton.name = 'radio_' + parentItem.id; 171 | }; 172 | 173 | 174 | /** @inheritDoc */ 175 | vsaq.questionnaire.items.RadioItem.prototype.setInternalValue = 176 | function(value) { 177 | if (goog.isBoolean(value) && value) { 178 | this.radioButton.checked = true; 179 | } else if (goog.isString(value) && 180 | (value == vsaq.questionnaire.items.RadioItem.CHECKED_VALUE)) { 181 | this.radioButton.checked = true; 182 | } 183 | }; 184 | 185 | 186 | /** 187 | * This function must not be called, as for RadioItems it is impossible to know 188 | * from an individual item whether the group has been answered or not. 189 | * If called, this function throws an exception. 190 | */ 191 | vsaq.questionnaire.items.RadioItem.prototype.isAnswered = function() { 192 | throw 'This function should never be called.'; 193 | }; 194 | -------------------------------------------------------------------------------- /src/vsaq/static/questionnaire/yesnoitem.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2016 Google Inc. All rights reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @fileoverview A questionnaire item which offers two choices (typically 19 | * yes and no). 20 | */ 21 | 22 | goog.provide('vsaq.questionnaire.items.YesNoItem'); 23 | 24 | goog.require('goog.dom'); 25 | goog.require('goog.events'); 26 | goog.require('goog.events.EventType'); 27 | goog.require('goog.soy'); 28 | goog.require('vsaq.questionnaire.items.ParseError'); 29 | goog.require('vsaq.questionnaire.items.ValueItem'); 30 | goog.require('vsaq.questionnaire.templates'); 31 | goog.require('vsaq.questionnaire.utils'); 32 | 33 | 34 | 35 | /** 36 | * A question that allows the user to choose between 2 options. 37 | * @param {string} id An ID uniquely identifying the question. 38 | * @param {?string} conditions A string containing conditions which must be met 39 | * for the item to be visible to the user. 40 | * @param {string} caption The caption to show next to the radio button. 41 | * @param {string} yes String shown as label for the first option. 42 | * @param {string} no String shown as label for the second option. 43 | * @extends {vsaq.questionnaire.items.ValueItem} 44 | * @constructor 45 | */ 46 | vsaq.questionnaire.items.YesNoItem = function(id, conditions, caption, yes, 47 | no) { 48 | goog.base(this, id, conditions, caption); 49 | 50 | /** 51 | * The text for the yes choice. 52 | * @type {!string} 53 | */ 54 | this.yes = yes; 55 | var propertyInformation = { 56 | nameInClass: 'yes', 57 | mandatory: true 58 | }; 59 | this.addPropertyInformation('yes', propertyInformation); 60 | 61 | /** 62 | * The text for the no choice. 63 | * @type {!string} 64 | */ 65 | this.no = no; 66 | propertyInformation = { 67 | nameInClass: 'no', 68 | mandatory: true 69 | }; 70 | this.addPropertyInformation('no', propertyInformation); 71 | 72 | /** 73 | * The radio button for the 'yes' answer. 74 | * @type {!HTMLInputElement} 75 | * @private 76 | */ 77 | this.yesRadio_; 78 | 79 | /** 80 | * The radio button for the 'no' answer. 81 | * @type {!HTMLInputElement} 82 | * @private 83 | */ 84 | this.noRadio_; 85 | 86 | this.render(); 87 | }; 88 | goog.inherits(vsaq.questionnaire.items.YesNoItem, 89 | vsaq.questionnaire.items.ValueItem); 90 | 91 | 92 | /** 93 | * Render the HTML for this item. 94 | */ 95 | vsaq.questionnaire.items.YesNoItem.prototype.render = function() { 96 | var oldNode = this.container; 97 | this.container = goog.soy.renderAsElement(vsaq.questionnaire.templates.yesno, 98 | { 99 | id: this.id, 100 | captionHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.text), 101 | yesHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.yes), 102 | noHtml: soydata.VERY_UNSAFE.ordainSanitizedHtml(this.no) 103 | }); 104 | goog.dom.replaceNode(this.container, oldNode); 105 | 106 | this.yesRadio_ = /** @type {!HTMLInputElement} */ ( 107 | vsaq.questionnaire.utils.findById(this.container, this.id + '/yes')); 108 | this.noRadio_ = /** @type {!HTMLInputElement} */ ( 109 | vsaq.questionnaire.utils.findById(this.container, this.id + '/no')); 110 | goog.events.listen(this.yesRadio_, goog.events.EventType.CLICK, 111 | this.answerChanged, true, this); 112 | goog.events.listen(this.noRadio_, goog.events.EventType.CLICK, 113 | this.answerChanged, true, this); 114 | }; 115 | 116 | 117 | /** 118 | * Constant indicating the string value of the item when 'yes' is selected. 119 | * @type {string} 120 | */ 121 | vsaq.questionnaire.items.YesNoItem.YES_VALUE = 'yes'; 122 | 123 | 124 | /** 125 | * Constant indicating the string value of the item when 'no' is selected. 126 | * @type {string} 127 | */ 128 | vsaq.questionnaire.items.YesNoItem.NO_VALUE = 'no'; 129 | 130 | 131 | /** 132 | * Type of the question. This is used to distinguish questions in serialized 133 | * format. 134 | * @type {string} 135 | * @const 136 | */ 137 | vsaq.questionnaire.items.YesNoItem.TYPE = 'yesno'; 138 | 139 | 140 | /** 141 | * Parses YesNoItems. If the topmost item in the passed Array is an a 142 | * YesNoItem, it is consumed and a YesNoItem instance is returned. 143 | * If the topmost item is not a YesNoItem, an exception is thrown. 144 | * @param {!Array.} questionStack Array of serialized 145 | * questionnaire Items. 146 | * @return {!vsaq.questionnaire.items.YesNoItem} The parsed YesNoItem. 147 | */ 148 | vsaq.questionnaire.items.YesNoItem.parse = function(questionStack) { 149 | var item = questionStack.shift(); 150 | if (item.type != vsaq.questionnaire.items.YesNoItem.TYPE) 151 | throw new vsaq.questionnaire.items.ParseError('Wrong parser chosen.'); 152 | 153 | return new vsaq.questionnaire.items.YesNoItem(item.id, item.cond, item.text, 154 | item.yes, item.no); 155 | }; 156 | 157 | 158 | /** @inheritDoc */ 159 | vsaq.questionnaire.items.YesNoItem.prototype.setReadOnly = function(readOnly) { 160 | this.yesRadio_.disabled = readOnly; 161 | this.noRadio_.disabled = readOnly; 162 | }; 163 | 164 | 165 | /** @inheritDoc */ 166 | vsaq.questionnaire.items.YesNoItem.prototype.isChecked = function(opt_value) { 167 | if (!opt_value) opt_value = '/yes'; 168 | var exp_value = opt_value.substring(opt_value.lastIndexOf('/') + 1); 169 | return (this.getValue() == exp_value); 170 | }; 171 | 172 | 173 | /** @inheritDoc */ 174 | vsaq.questionnaire.items.YesNoItem.prototype.getValue = function() { 175 | if (this.yesRadio_.checked) { 176 | return vsaq.questionnaire.items.YesNoItem.YES_VALUE; 177 | } else if (this.noRadio_.checked) { 178 | return vsaq.questionnaire.items.YesNoItem.NO_VALUE; 179 | } 180 | return ''; 181 | }; 182 | 183 | 184 | /** @inheritDoc */ 185 | vsaq.questionnaire.items.YesNoItem.prototype.setInternalValue = 186 | function(value) { 187 | if (value == vsaq.questionnaire.items.YesNoItem.YES_VALUE) { 188 | this.yesRadio_.checked = true; 189 | this.noRadio_.checked = false; 190 | } else if (value == vsaq.questionnaire.items.YesNoItem.NO_VALUE) { 191 | this.yesRadio_.checked = false; 192 | this.noRadio_.checked = true; 193 | } else { 194 | this.yesRadio_.checked = false; 195 | this.noRadio_.checked = false; 196 | } 197 | }; 198 | 199 | 200 | /** @inheritDoc */ 201 | vsaq.questionnaire.items.YesNoItem.prototype.isAnswered = function() { 202 | return this.getValue().length > 0; 203 | }; 204 | --------------------------------------------------------------------------------