diff --git a/awx/ui/test/e2e/commands/navigateTo.js b/awx/ui/test/e2e/commands/navigateTo.js
new file mode 100644
index 0000000000..4b8bd3048a
--- /dev/null
+++ b/awx/ui/test/e2e/commands/navigateTo.js
@@ -0,0 +1,8 @@
+exports.command = function navigateTo (url) {
+ this.url(url);
+
+ this.waitForElementVisible('div.spinny');
+ this.waitForElementNotVisible('div.spinny');
+
+ return this;
+};
diff --git a/awx/ui/test/e2e/fixtures.js b/awx/ui/test/e2e/fixtures.js
index 9f81fd2083..1cc8a6ea72 100644
--- a/awx/ui/test/e2e/fixtures.js
+++ b/awx/ui/test/e2e/fixtures.js
@@ -7,7 +7,7 @@ import {
spread
} from './api';
-const sid = uuid().substr(0, 8);
+const session = `e2e-${uuid().substr(0, 8)}`;
const store = {};
@@ -50,24 +50,43 @@ const getOrCreate = (endpoint, data) => {
return store[endpoint][identity].then(created => created.data);
};
-const getOrganization = () => getOrCreate('/organizations/', {
- name: `e2e-organization-${sid}`
+const getOrganization = (namespace = session) => getOrCreate('/organizations/', {
+ name: `${namespace}-organization`,
+ description: namespace
});
-const getInventory = () => getOrganization()
+const getInventory = (namespace = session) => getOrganization(namespace)
.then(organization => getOrCreate('/inventories/', {
- name: `e2e-inventory-${sid}`,
+ name: `${namespace}-inventory`,
+ description: namespace,
organization: organization.id
}));
-const getInventoryScript = () => getOrganization()
+const getInventoryScript = (namespace = session) => getOrganization(namespace)
.then(organization => getOrCreate('/inventory_scripts/', {
- name: `e2e-inventory-script-${sid}`,
+ name: `${namespace}-inventory-script`,
+ description: namespace,
organization: organization.id,
script: '#!/usr/bin/env python'
}));
-const getAdminAWSCredential = () => {
+const getInventorySource = (namespace = session) => {
+ const promises = [
+ getInventory(namespace),
+ getInventoryScript(namespace)
+ ];
+
+ return all(promises)
+ .then(spread((inventory, inventoryScript) => getOrCreate('/inventory_sources/', {
+ name: `${namespace}-inventory-source-custom`,
+ description: namespace,
+ source: 'custom',
+ inventory: inventory.id,
+ source_script: inventoryScript.id
+ })));
+};
+
+const getAdminAWSCredential = (namespace = session) => {
const promises = [
get('/me/'),
getOrCreate('/credential_types/', {
@@ -80,7 +99,8 @@ const getAdminAWSCredential = () => {
const [admin] = me.data.results;
return getOrCreate('/credentials/', {
- name: `e2e-aws-credential-${sid}`,
+ name: `${namespace}-credential-aws`,
+ description: namespace,
credential_type: credentialType.id,
user: admin.id,
inputs: {
@@ -92,7 +112,7 @@ const getAdminAWSCredential = () => {
}));
};
-const getAdminMachineCredential = () => {
+const getAdminMachineCredential = (namespace = session) => {
const promises = [
get('/me/'),
getOrCreate('/credential_types/', { name: 'Machine' })
@@ -103,30 +123,34 @@ const getAdminMachineCredential = () => {
const [admin] = me.data.results;
return getOrCreate('/credentials/', {
- name: `e2e-machine-credential-${sid}`,
+ name: `${namespace}-credential-machine-admin`,
+ description: namespace,
credential_type: credentialType.id,
user: admin.id
});
}));
};
-const getTeam = () => getOrganization()
+const getTeam = (namespace = session) => getOrganization(namespace)
.then(organization => getOrCreate('/teams/', {
- name: `e2e-team-${sid}`,
+ name: `${namespace}-team`,
+ description: namespace,
organization: organization.id,
}));
-const getSmartInventory = () => getOrganization()
+const getSmartInventory = (namespace = session) => getOrganization(namespace)
.then(organization => getOrCreate('/inventories/', {
- name: `e2e-smart-inventory-${sid}`,
+ name: `${namespace}-smart-inventory`,
+ description: namespace,
organization: organization.id,
host_filter: 'search=localhost',
kind: 'smart'
}));
-const getNotificationTemplate = () => getOrganization()
+const getNotificationTemplate = (namespace = session) => getOrganization(namespace)
.then(organization => getOrCreate('/notification_templates/', {
- name: `e2e-notification-template-${sid}`,
+ name: `${namespace}-notification-template`,
+ description: namespace,
organization: organization.id,
notification_type: 'slack',
notification_configuration: {
@@ -135,9 +159,10 @@ const getNotificationTemplate = () => getOrganization()
}
}));
-const getProject = () => getOrganization()
+const getProject = (namespace = session) => getOrganization(namespace)
.then(organization => getOrCreate('/projects/', {
- name: `e2e-project-${sid}`,
+ name: `${namespace}-project`,
+ description: namespace,
organization: organization.id,
scm_url: 'https://github.com/ansible/ansible-tower-samples',
scm_type: 'git'
@@ -168,7 +193,7 @@ const waitForJob = endpoint => {
});
};
-const getUpdatedProject = () => getProject()
+const getUpdatedProject = (namespace = session) => getProject(namespace)
.then(project => {
const updateURL = project.related.current_update;
@@ -179,27 +204,28 @@ const getUpdatedProject = () => getProject()
return project;
});
-const getJobTemplate = () => {
+const getJobTemplate = (namespace = session) => {
const promises = [
- getInventory(),
- getAdminMachineCredential(),
- getUpdatedProject()
+ getInventory(namespace),
+ getAdminMachineCredential(namespace),
+ getUpdatedProject(namespace)
];
return all(promises)
- .then(spread((inventory, credential, project) => getOrCreate('/job_templates', {
- name: `e2e-job-template-${sid}`,
+ .then(spread((inventory, credential, project) => getOrCreate('/job_templates/', {
+ name: `${namespace}-job-template`,
+ description: namespace,
inventory: inventory.id,
credential: credential.id,
project: project.id,
- playbook: 'hello_world.yml'
+ playbook: 'hello_world.yml',
})));
};
-const getAuditor = () => getOrganization()
+const getAuditor = (namespace = session) => getOrganization(namespace)
.then(organization => getOrCreate('/users/', {
+ username: `auditor-${uuid().substr(0, 8)}`,
organization: organization.id,
- username: `e2e-auditor-${sid}`,
first_name: 'auditor',
last_name: 'last',
email: 'null@ansible.com',
@@ -208,15 +234,54 @@ const getAuditor = () => getOrganization()
password: 'password'
}));
-const getUser = () => getOrCreate('/users/', {
- username: `e2e-user-${sid}`,
- first_name: `user-${sid}-first`,
- last_name: `user-${sid}-last`,
- email: `null-${sid}@ansible.com`,
- is_superuser: false,
- is_system_auditor: false,
- password: 'password'
-});
+const getUser = (namespace = session) => getOrganization(namespace)
+ .then(organization => getOrCreate('/users/', {
+ username: `user-${uuid().substr(0, 8)}`,
+ organization: organization.id,
+ first_name: 'firstname',
+ last_name: 'lastname',
+ email: 'null@ansible.com',
+ is_superuser: false,
+ is_system_auditor: false,
+ password: 'password'
+ }));
+
+const getJobTemplateAdmin = (namespace = session) => {
+ const rolePromise = getJobTemplate(namespace)
+ .then(obj => obj.summary_fields.object_roles.admin_role);
+
+ const userPromise = getOrganization(namespace)
+ .then(obj => getOrCreate('/users/', {
+ username: `job-template-admin-${uuid().substr(0, 8)}`,
+ organization: obj.id,
+ first_name: 'firstname',
+ last_name: 'lastname',
+ email: 'null@ansible.com',
+ is_superuser: false,
+ is_system_auditor: false,
+ password: 'password'
+ }));
+
+ const assignRolePromise = Promise.all([userPromise, rolePromise])
+ .then(spread((user, role) => post(`/api/v2/roles/${role.id}/users/`, { id: user.id })));
+
+ return Promise.all([userPromise, assignRolePromise])
+ .then(spread(user => user));
+};
+
+const getInventorySourceSchedule = (namespace = session) => getInventorySource(namespace)
+ .then(source => getOrCreate(source.related.schedules, {
+ name: `${source.name}-schedule`,
+ description: namespace,
+ rrule: 'DTSTART:20171104T040000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1'
+ }));
+
+const getJobTemplateSchedule = (namespace = session) => getJobTemplate(namespace)
+ .then(template => getOrCreate(template.related.schedules, {
+ name: `${template.name}-schedule`,
+ description: namespace,
+ rrule: 'DTSTART:20171104T040000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=1'
+ }));
module.exports = {
getAdminAWSCredential,
@@ -224,7 +289,11 @@ module.exports = {
getAuditor,
getInventory,
getInventoryScript,
+ getInventorySource,
+ getInventorySourceSchedule,
getJobTemplate,
+ getJobTemplateAdmin,
+ getJobTemplateSchedule,
getNotificationTemplate,
getOrCreate,
getOrganization,
diff --git a/awx/ui/test/e2e/tests/test-xss.js b/awx/ui/test/e2e/tests/test-xss.js
new file mode 100644
index 0000000000..0ea5382289
--- /dev/null
+++ b/awx/ui/test/e2e/tests/test-xss.js
@@ -0,0 +1,658 @@
+import {
+ getAdminMachineCredential,
+ getInventory,
+ getInventoryScript,
+ getInventorySource,
+ getInventorySourceSchedule,
+ getJobTemplate,
+ getJobTemplateAdmin,
+ getJobTemplateSchedule,
+ getNotificationTemplate,
+ getOrganization,
+ getSmartInventory,
+ getTeam,
+ getUpdatedProject,
+} from '../fixtures';
+
+const data = {};
+const urls = {};
+const pages = {};
+
+module.exports = {
+ before: (client, done) => {
+ const namespace = '
test
';
+
+ const resources = [
+ getOrganization(namespace).then(obj => { data.organization = obj; }),
+ getInventory(namespace).then(obj => { data.inventory = obj; }),
+ getInventoryScript(namespace).then(obj => { data.inventoryScript = obj; }),
+ getSmartInventory(namespace).then(obj => { data.smartInventory = obj; }),
+ getInventorySource(namespace).then(obj => { data.inventorySource = obj; }),
+ getInventorySourceSchedule(namespace).then(obj => { data.sourceSchedule = obj; }),
+ getUpdatedProject(namespace).then(obj => { data.project = obj; }),
+ getAdminMachineCredential(namespace).then(obj => { data.credential = obj; }),
+ getJobTemplate(namespace).then(obj => { data.jobTemplate = obj; }),
+ getJobTemplateSchedule(namespace).then(obj => { data.jobTemplateSchedule = obj; }),
+ getTeam(namespace).then(obj => { data.team = obj; }),
+ getJobTemplateAdmin(namespace).then(obj => { data.user = obj; }),
+ getNotificationTemplate(namespace).then(obj => { data.notification = obj; }),
+ ];
+
+ Promise.all(resources)
+ .then(() => {
+ pages.organizations = client.page.organizations();
+ pages.inventories = client.page.inventories();
+ pages.inventoryScripts = client.page.inventoryScripts();
+ pages.projects = client.page.projects();
+ pages.credentials = client.page.credentials();
+ pages.templates = client.page.templates();
+ pages.teams = client.page.teams();
+ pages.users = client.page.users();
+ pages.notificationTemplates = client.page.notificationTemplates();
+
+ urls.organization = `${pages.organizations.url()}/${data.organization.id}`;
+ urls.inventory = `${pages.inventories.url()}/inventory/${data.inventory.id}`;
+ urls.inventoryScript = `${pages.inventoryScripts.url()}/${data.inventoryScript.id}`;
+ urls.inventorySource = `${urls.inventory}/inventory_sources/edit/${data.inventorySource.id}`;
+ urls.sourceSchedule = `${urls.inventorySource}/schedules/${data.sourceSchedule.id}`;
+ urls.smartInventory = `${pages.inventories.url()}/smart/${data.smartInventory.id}`;
+ urls.project = `${pages.projects.url()}/${data.project.id}`;
+ urls.credential = `${pages.credentials.url()}/${data.credential.id}`;
+ urls.jobTemplate = `${pages.templates.url()}/job_template/${data.jobTemplate.id}`;
+ urls.jobTemplateSchedule = `${urls.jobTemplate}/schedules/${data.jobTemplateSchedule.id}`;
+ urls.team = `${pages.teams.url()}/${data.team.id}`;
+ urls.user = `${pages.users.url()}/${data.user.id}`;
+ urls.notification = `${pages.notificationTemplates.url()}/${data.notification.id}`;
+
+ client.useCss();
+ client.login();
+ client.resizeWindow(1200, 800);
+ client.waitForAngular();
+
+ done();
+ });
+ },
+ 'check template form for unsanitized content': client => {
+ const multiCredentialOpen = 'multi-credential button i[class*="search"]';
+ const multiCredentialExit = 'multi-credential-modal button[class*="exit"]';
+
+ client.url(urls.jobTemplate);
+
+ client.expect.element('#job_template_form').visible;
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.expect.element(multiCredentialOpen).visible;
+ client.expect.element(multiCredentialOpen).enabled;
+
+ client.pause(2000).click(multiCredentialOpen);
+
+ client.expect.element('#multi-credential-modal').visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.click(multiCredentialExit);
+
+ client.pause(500).expect.element('div.spinny').not.visible;
+ client.expect.element('#multi-credential-modal').not.present;
+ },
+ 'check template roles list for unsanitized content': client => {
+ const itemDelete = `#permissions_table tr[id="${data.user.id}"] div[class*="RoleList-deleteContainer"]`;
+
+ client.expect.element('#permissions_tab').visible;
+ client.expect.element('#permissions_tab').enabled;
+
+ client.click('#permissions_tab');
+
+ client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.expect.element('div[ui-view="related"]').visible;
+ client.expect.element('div[ui-view="related"] smart-search input').enabled;
+
+ client.sendKeys('div[ui-view="related"] smart-search input', `id:${data.user.id}`);
+ client.sendKeys('div[ui-view="related"] smart-search input', client.Keys.ENTER);
+
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element(itemDelete).visible;
+ client.expect.element(itemDelete).enabled;
+
+ client.click(itemDelete);
+
+ client.expect.element('#prompt-header').visible;
+ client.expect.element('#prompt-header').text.equal('USER ACCESS REMOVAL');
+ client.expect.element('#prompt_cancel_btn').enabled;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.click('#prompt_cancel_btn');
+
+ client.expect.element('#prompt-header').not.visible;
+ },
+ 'check template permissions view for unsanitized content': client => {
+ client.expect.element('button[aw-tool-tip="Add a permission"]').visible;
+ client.expect.element('button[aw-tool-tip="Add a permission"]').enabled;
+
+ client.click('button[aw-tool-tip="Add a permission"]');
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element('div[class="AddPermissions-header"]').visible;
+ client.expect.element('div[class="AddPermissions-header"]').attribute('innerHTML')
+ .contains('<div id="xss" class="xss">test</div>');
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.expect.element('div[class="AddPermissions-dialog"] button[class*="exit"]').enabled;
+
+ client.click('div[class="AddPermissions-dialog"] button[class*="exit"]');
+
+ client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+
+ // client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element('#job_template_tab').enabled;
+
+ client.click('#job_template_tab');
+
+ client.expect.element('#job_template_form').visible;
+ },
+ 'check template list for unsanitized content': client => {
+ const itemRow = `#templates_table tr[id="${data.jobTemplate.id}"]`;
+ const itemName = `${itemRow} td[class*="name-"] a`;
+
+ client.expect.element('div[class^="Panel"] smart-search').visible;
+ client.expect.element('div[class^="Panel"] smart-search input').enabled;
+
+ client.sendKeys('div[class^="Panel"] smart-search input', `id:${data.jobTemplate.id}`);
+ client.sendKeys('div[class^="Panel"] smart-search input', client.Keys.ENTER);
+
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element('.List-titleBadge').text.equal('1');
+ client.expect.element(itemName).visible;
+
+ client.moveToElement(itemName, 0, 0, () => {
+ client.expect.element(itemName).attribute('aria-describedby');
+
+ client.getAttribute(itemName, 'aria-describedby', ({ value }) => {
+ const tooltip = `#${value}`;
+
+ client.expect.element(tooltip).present;
+ client.expect.element(tooltip).visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ client.expect.element(tooltip).attribute('innerHTML')
+ .contains('<div id="xss" class="xss">test</div>');
+ });
+ });
+
+ client.click(`${itemRow} i[class*="trash"]`);
+
+ client.expect.element('#prompt-header').visible;
+ client.expect.element('#prompt_cancel_btn').enabled;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.click('#prompt_cancel_btn');
+
+ client.expect.element('#prompt-header').not.visible;
+ },
+ 'check user form for unsanitized content': client => {
+ client.navigateTo(urls.user);
+
+ client.expect.element('#user_form').visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ },
+ 'check user roles list for unsanitized content': client => {
+ const { admin_role } = data.jobTemplate.summary_fields.object_roles;
+ const itemDelete = `#permissions_table tr[id="${admin_role.id}"] #delete-action`;
+
+ client.expect.element('#permissions_tab').visible;
+ client.expect.element('#permissions_tab').enabled;
+
+ client.click('#permissions_tab');
+
+ client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.expect.element('div[ui-view="related"]').visible;
+ client.expect.element('div[ui-view="related"] smart-search input').enabled;
+
+ client.sendKeys('div[ui-view="related"] smart-search input', `id:${admin_role.id}`);
+ client.sendKeys('div[ui-view="related"] smart-search input', client.Keys.ENTER);
+
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element(itemDelete).visible;
+ client.expect.element(itemDelete).enabled;
+
+ client.click(itemDelete);
+
+ client.expect.element('#prompt-header').visible;
+ client.expect.element('#prompt-header').text.equal('REMOVE ROLE');
+ client.expect.element('#prompt_cancel_btn').enabled;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.click('#prompt_cancel_btn');
+
+ client.expect.element('#prompt-header').not.visible;
+ },
+ 'check user permissions view for unsanitized content': client => {
+ client.expect.element('button[aw-tool-tip="Grant Permission"]').enabled;
+
+ client.click('button[aw-tool-tip="Grant Permission"]');
+
+ client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+ client.expect.element('div[class="AddPermissions-header"]').visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.expect.element('div[class="AddPermissions-dialog"] button[class*="exit"]').enabled;
+
+ client.click('div[class="AddPermissions-dialog"] button[class*="exit"]');
+
+ client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+ },
+ 'check notification form for unsanitized content': client => {
+ client.navigateTo(urls.notification);
+
+ client.expect.element('#notification_template_form').visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ },
+ 'check notification list for unsanitized content': client => {
+ const itemRow = `#notification_templates_table tr[id="${data.notification.id}"]`;
+ const itemName = `${itemRow} td[class*="name-"] a`;
+
+ client.expect.element('div[class^="Panel"] smart-search').visible;
+ client.expect.element('div[class^="Panel"] smart-search input').enabled;
+
+ client.sendKeys('div[class^="Panel"] smart-search input', `id:${data.notification.id}`);
+ client.sendKeys('div[class^="Panel"] smart-search input', client.Keys.ENTER);
+
+ client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element('.List-titleBadge').text.equal('1');
+ client.expect.element(itemName).visible;
+
+ client.moveToElement(itemName, 0, 0, () => {
+ client.expect.element(itemName).attribute('aria-describedby');
+
+ client.getAttribute(itemName, 'aria-describedby', ({ value }) => {
+ const tooltip = `#${value}`;
+
+ client.expect.element(tooltip).present;
+ client.expect.element(tooltip).visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ client.expect.element(tooltip).attribute('innerHTML')
+ .contains('<div id="xss" class="xss">test</div>');
+ });
+ });
+
+ client.click(`${itemRow} i[class*="trash"]`);
+
+ client.expect.element('#prompt-header').visible;
+ client.expect.element('#prompt_cancel_btn').enabled;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.click('#prompt_cancel_btn');
+
+ client.expect.element('#prompt-header').not.visible;
+ },
+ 'check organization form for unsanitized content': client => {
+ client.navigateTo(urls.organization);
+
+ client.expect.element('#organization_form').visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ },
+ 'check organization list for unsanitized content': client => {
+ const itemName = '#OrgCards h3[class*="-label"]';
+
+ client.expect.element('div[class^="Panel"] smart-search').visible;
+ client.expect.element('div[class^="Panel"] smart-search input').enabled;
+
+ client.sendKeys('div[class^="Panel"] smart-search input', `id:${data.organization.id}`);
+ client.sendKeys('div[class^="Panel"] smart-search input', client.Keys.ENTER);
+
+ client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element(itemName).visible;
+
+ client.moveToElement(itemName, 0, 0, () => {
+ client.expect.element(itemName).attribute('aria-describedby');
+
+ client.getAttribute(itemName, 'aria-describedby', ({ value }) => {
+ const tooltip = `#${value}`;
+
+ client.expect.element(tooltip).present;
+ client.expect.element(tooltip).visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ client.expect.element(tooltip).attribute('innerHTML')
+ .contains('<div id="xss" class="xss">test</div>');
+ });
+ });
+
+ client.click('#OrgCards i[class*="trash"]');
+
+ client.expect.element('#prompt-header').visible;
+ client.expect.element('#prompt_cancel_btn').enabled;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.click('#prompt_cancel_btn');
+
+ client.expect.element('#prompt-header').not.visible;
+ },
+ 'check inventory form for unsanitized content': client => {
+ client.navigateTo(urls.inventory);
+
+ client.expect.element('#inventory_form').visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ },
+ 'check inventory list for unsanitized content': client => {
+ const itemRow = `#inventories_table tr[id="${data.inventory.id}"]`;
+ const itemName = `${itemRow} td[class*="name-"] a`;
+
+ client.expect.element('div[class^="Panel"] smart-search').visible;
+ client.expect.element('div[class^="Panel"] smart-search input').enabled;
+
+ client.sendKeys('div[class^="Panel"] smart-search input', `id:${data.inventory.id}`);
+ client.sendKeys('div[class^="Panel"] smart-search input', client.Keys.ENTER);
+
+ client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+
+ // client.expect.element('.List-titleBadge').text.equal('1');
+ client.expect.element(itemName).visible;
+
+ client.moveToElement(itemName, 0, 0, () => {
+ client.expect.element(itemName).attribute('aria-describedby');
+
+ client.getAttribute(itemName, 'aria-describedby', ({ value }) => {
+ const tooltip = `#${value}`;
+
+ client.expect.element(tooltip).present;
+ client.expect.element(tooltip).visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ client.expect.element(tooltip).attribute('innerHTML')
+ .contains('<div id="xss" class="xss">test</div>');
+ });
+ });
+
+ client.click(`${itemRow} i[class*="trash"]`);
+
+ client.expect.element('#prompt-header').visible;
+ client.expect.element('#prompt_cancel_btn').enabled;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.click('#prompt_cancel_btn');
+
+ client.expect.element('#prompt-header').not.visible;
+ },
+ 'check smart inventory form for unsanitized content': client => {
+ client.url(urls.smartInventory);
+
+ client.expect.element('#smartinventory_form').visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ },
+ 'check inventory script form for unsanitized content': client => {
+ client.navigateTo(urls.inventoryScript);
+
+ client.expect.element('#inventory_script_form').visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ },
+ 'check inventory script list for unsanitized content': client => {
+ const itemRow = `#inventory_scripts_table tr[id="${data.inventoryScript.id}"]`;
+ const itemName = `${itemRow} td[class*="name-"] a`;
+
+ client.expect.element('div[class^="Panel"] smart-search').visible;
+ client.expect.element('div[class^="Panel"] smart-search input').enabled;
+
+ client.sendKeys('div[class^="Panel"] smart-search input', `id:${data.inventoryScript.id}`);
+ client.sendKeys('div[class^="Panel"] smart-search input', client.Keys.ENTER);
+
+ client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element('.List-titleBadge').text.equal('1');
+ client.expect.element(itemName).visible;
+
+ client.moveToElement(itemName, 0, 0, () => {
+ client.expect.element(itemName).attribute('aria-describedby');
+
+ client.getAttribute(itemName, 'aria-describedby', ({ value }) => {
+ const tooltip = `#${value}`;
+
+ client.expect.element(tooltip).present;
+ client.expect.element(tooltip).visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ client.expect.element(tooltip).attribute('innerHTML')
+ .contains('<div id="xss" class="xss">test</div>');
+ });
+ });
+
+ client.click(`${itemRow} i[class*="trash"]`);
+
+ client.expect.element('#prompt-header').visible;
+ client.expect.element('#prompt_cancel_btn').enabled;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.click('#prompt_cancel_btn');
+
+ client.expect.element('#prompt-header').not.visible;
+ },
+ 'check project form for unsanitized content': client => {
+ client.navigateTo(urls.project);
+
+ client.expect.element('#project_form').visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ },
+ 'check project list for unsanitized content': client => {
+ const itemRow = `#projects_table tr[id="${data.project.id}"]`;
+ const itemName = `${itemRow} td[class*="name-"] a`;
+
+ client.expect.element('div[class^="Panel"] smart-search').visible;
+ client.expect.element('div[class^="Panel"] smart-search input').enabled;
+
+ client.sendKeys('div[class^="Panel"] smart-search input', `id:${data.project.id}`);
+ client.sendKeys('div[class^="Panel"] smart-search input', client.Keys.ENTER);
+
+ client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element('.List-titleBadge').text.equal('1');
+ client.expect.element(itemName).visible;
+
+ client.moveToElement(itemName, 0, 0, () => {
+ client.expect.element(itemName).attribute('aria-describedby');
+
+ client.getAttribute(itemName, 'aria-describedby', ({ value }) => {
+ const tooltip = `#${value}`;
+
+ client.expect.element(tooltip).present;
+ client.expect.element(tooltip).visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ client.expect.element(tooltip).attribute('innerHTML')
+ .contains('<div id="xss" class="xss">test</div>');
+ });
+ });
+
+ client.click(`${itemRow} i[class*="trash"]`);
+
+ client.expect.element('#prompt-header').visible;
+ client.expect.element('#prompt_cancel_btn').enabled;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.click('#prompt_cancel_btn');
+
+ client.expect.element('#prompt-header').not.visible;
+ },
+ 'check credential form for unsanitized content': client => {
+ client.navigateTo(urls.credential);
+
+ client.expect.element('div[ui-view="edit"] form').visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ },
+ 'check credential list for unsanitized content': client => {
+ const itemRow = `#credentials_table tr[id="${data.credential.id}"]`;
+ const itemName = `${itemRow} td[class*="name-"] a`;
+
+ client.expect.element('div[ui-view="list"] smart-search').visible;
+ client.expect.element('div[ui-view="list"] smart-search input').enabled;
+
+ client.sendKeys('div[ui-view="list"] smart-search input', `id:${data.credential.id}`);
+ client.sendKeys('div[ui-view="list"] smart-search input', client.Keys.ENTER);
+
+ client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element('.List-titleBadge').text.equal('1');
+ client.expect.element(itemName).visible;
+
+ client.moveToElement(itemName, 0, 0, () => {
+ client.expect.element(itemName).attribute('aria-describedby');
+
+ client.getAttribute(itemName, 'aria-describedby', ({ value }) => {
+ const tooltip = `#${value}`;
+
+ client.expect.element(tooltip).present;
+ client.expect.element(tooltip).visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ client.expect.element(tooltip).attribute('innerHTML')
+ .contains('<div id="xss" class="xss">test</div>');
+ });
+ });
+
+ client.click(`${itemRow} i[class*="trash"]`);
+
+ client.expect.element('#prompt-header').visible;
+ client.expect.element('#prompt_cancel_btn').enabled;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.click('#prompt_cancel_btn');
+
+ client.expect.element('#prompt-header').not.visible;
+ },
+ 'check team form for unsanitized content': client => {
+ client.navigateTo(urls.team);
+
+ client.expect.element('#team_form').visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ },
+ 'check team list for unsanitized content': client => {
+ const itemRow = `#teams_table tr[id="${data.team.id}"]`;
+ const itemName = `${itemRow} td[class*="name-"] a`;
+
+ client.expect.element('div[class^="Panel"] smart-search').visible;
+ client.expect.element('div[class^="Panel"] smart-search input').enabled;
+
+ client.sendKeys('div[class^="Panel"] smart-search input', `id:${data.team.id}`);
+ client.sendKeys('div[class^="Panel"] smart-search input', client.Keys.ENTER);
+
+ client.expect.element('div.spinny').visible;
+ client.expect.element('div.spinny').not.visible;
+
+ client.expect.element('.List-titleBadge').text.equal('1');
+ client.expect.element(itemName).visible;
+
+ client.moveToElement(itemName, 0, 0, () => {
+ client.expect.element(itemName).attribute('aria-describedby');
+
+ client.getAttribute(itemName, 'aria-describedby', ({ value }) => {
+ const tooltip = `#${value}`;
+
+ client.expect.element(tooltip).present;
+ client.expect.element(tooltip).visible;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ client.expect.element(tooltip).attribute('innerHTML')
+ .contains('<div id="xss" class="xss">test</div>');
+ });
+ });
+
+ client.click(`${itemRow} i[class*="trash"]`);
+
+ client.expect.element('#prompt-header').visible;
+ client.expect.element('#prompt_cancel_btn').enabled;
+
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+
+ client.click('#prompt_cancel_btn');
+
+ client.expect.element('#prompt-header').not.visible;
+ },
+ 'check inventory source schedule view for unsanitized content': client => {
+ client.navigateTo(urls.sourceSchedule);
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ },
+ 'check job template schedule view for unsanitized content': client => {
+ client.navigateTo(urls.jobTemplateSchedule);
+ client.expect.element('#xss').not.present;
+ client.expect.element('[class=xss]').not.present;
+ client.end();
+ },
+};