Skip to content

Commit fa2dd3f

Browse files
committed
MQE-1070: Hide Sensitive Creds in Allure Report
- encrypt creds for display and decrypt on execution
1 parent 712ab3e commit fa2dd3f

File tree

13 files changed

+134
-28
lines changed

13 files changed

+134
-28
lines changed

dev/tests/functional/_bootstrap.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* See COPYING.txt for license details.
55
*/
66

7-
define('PROJECT_ROOT', dirname(dirname(dirname(__DIR__))));
7+
defined('PROJECT_ROOT') || define('PROJECT_ROOT', dirname(dirname(dirname(__DIR__))));
88
require_once realpath(PROJECT_ROOT . '/vendor/autoload.php');
99

1010
//Load constants from .env file

dev/tests/unit/Magento/FunctionalTestFramework/Test/Util/ActionObjectExtractorTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ public function testInvalidMergeOrderReference()
4848
try {
4949
$this->testActionObjectExtractor->extractActions($invalidArray, 'TestWithSelfReferencingStepKey');
5050
} catch (\Exception $e) {
51-
TestLoggingUtil::getInstance()->validateMockLogStatement(
51+
TestLoggingUtil::getInstance()->validateMockLogStatmentRegex(
5252
'error',
53-
'Line 108: Invalid ordering configuration in test',
53+
'/Line \d*: Invalid ordering configuration in test/',
5454
[
5555
'test' => 'TestWithSelfReferencingStepKey',
5656
'stepKey' => ['invalidTestAction1']

dev/tests/unit/Util/TestLoggingUtil.php

+9
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ public function validateMockLogStatement($type, $message, $context)
8282
$this->assertEquals($context, $record['context']);
8383
}
8484

85+
public function validateMockLogStatmentRegex($type, $regex, $context)
86+
{
87+
$records = $this->testLogHandler->getRecords();
88+
$record = $records[count($records)-1]; // we assume the latest record is what requires validation
89+
$this->assertEquals(strtoupper($type), $record['level_name']);
90+
$this->assertRegExp($regex, $record['message']);
91+
$this->assertEquals($context, $record['context']);
92+
}
93+
8594
/**
8695
* Function which clears the test logger context from the LogginUtil class. Should be run after a test class has
8796
* executed.

dev/tests/verification/Resources/PersistedReplacementTest.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class PersistedReplacementTestCest
5353
$I->fillField("#selector", "StringBefore " . $createdData->getCreatedDataByName('firstname') . " StringAfter");
5454
$I->fillField("#" . $createdData->getCreatedDataByName('firstname'), "input");
5555
$I->fillField("#" . getenv("MAGENTO_BASE_URL") . "#" . $createdData->getCreatedDataByName('firstname'), "input");
56-
$I->fillField("#" . CredentialStore::getInstance()->getSecret("SECRET_PARAM") . "#" . $createdData->getCreatedDataByName('firstname'), "input");
56+
$I->fillSecretField("#" . CredentialStore::getInstance()->getSecret("SECRET_PARAM") . "#" . $createdData->getCreatedDataByName('firstname'), "input");
5757
$I->dragAndDrop("#" . $createdData->getCreatedDataByName('firstname'), $createdData->getCreatedDataByName('lastname'));
5858
$I->conditionalClick($createdData->getCreatedDataByName('lastname'), "#" . $createdData->getCreatedDataByName('firstname'), true);
5959
$I->amOnUrl($createdData->getCreatedDataByName('firstname') . ".html");

src/Magento/FunctionalTestingFramework/Console/RunTestCommand.php

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Symfony\Component\Console\Input\InputInterface;
1414
use Symfony\Component\Console\Input\InputOption;
1515
use Symfony\Component\Console\Output\OutputInterface;
16+
use Symfony\Component\Debug\Debug;
1617
use Symfony\Component\Process\Process;
1718

1819
class RunTestCommand extends Command

src/Magento/FunctionalTestingFramework/DataGenerator/Handlers/CredentialStore.php

+21-2
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,19 @@
1414
class CredentialStore
1515
{
1616
/**
17-
* Singletone instnace
17+
* Singleton instance
1818
*
1919
* @var CredentialStore
2020
*/
2121
private static $INSTANCE = null;
2222

23+
/**
24+
* Initial vector for open_ssl encryption.
25+
*
26+
* @var string
27+
*/
28+
private $iv = null;
29+
2330
/**
2431
* Key/Value paris of credential names and their corresponding values
2532
*
@@ -47,6 +54,7 @@ public static function getInstance()
4754
private function __construct()
4855
{
4956
$this->readInCredentialsFile();
57+
$this->encryptionKey = openssl_random_pseudo_bytes(16);
5058
}
5159

5260
/**
@@ -103,8 +111,19 @@ private function readInCredentialsFile()
103111

104112
list($key, $value) = explode("=", $credValue);
105113
if (!empty($value)) {
106-
$this->credentials[$key] = $value;
114+
$this->credentials[$key] = openssl_encrypt($value, "AES-128-ECB", 0, $this->iv);
107115
}
108116
}
109117
}
118+
119+
/**
120+
* Takes a value encrypted at runtime and descrypts using the object's initial vector.
121+
*
122+
* @param string $value
123+
* @return string
124+
*/
125+
public function decryptSecretValue($value)
126+
{
127+
return openssl_decrypt($value, "AES-128-ECB", 0, $this->iv);
128+
}
110129
}

src/Magento/FunctionalTestingFramework/Module/MagentoWebDriver.php

+18-8
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,18 @@
66

77
namespace Magento\FunctionalTestingFramework\Module;
88

9-
use Codeception\Events;
109
use Codeception\Module\WebDriver;
1110
use Codeception\Test\Descriptor;
1211
use Codeception\TestInterface;
13-
use Facebook\WebDriver\WebDriverSelect;
14-
use Facebook\WebDriver\WebDriverBy;
15-
use Facebook\WebDriver\Exception\NoSuchElementException;
1612
use Facebook\WebDriver\Interactions\WebDriverActions;
17-
use Codeception\Exception\ElementNotFound;
1813
use Codeception\Exception\ModuleConfigException;
1914
use Codeception\Exception\ModuleException;
2015
use Codeception\Util\Uri;
21-
use Codeception\Util\ActionSequence;
16+
use Magento\FunctionalTestingFramework\DataGenerator\Handlers\CredentialStore;
2217
use Magento\FunctionalTestingFramework\DataGenerator\Persist\Curl\WebapiExecutor;
2318
use Magento\FunctionalTestingFramework\Util\Protocol\CurlTransport;
2419
use Magento\FunctionalTestingFramework\Util\Protocol\CurlInterface;
25-
use Magento\Setup\Exception;
2620
use Magento\FunctionalTestingFramework\Util\ConfigSanitizerUtil;
27-
use Yandex\Allure\Adapter\Event\TestCaseFinishedEvent;
2821
use Yandex\Allure\Adapter\Support\AttachmentSupport;
2922

3023
/**
@@ -44,6 +37,8 @@
4437
* password: admin_password
4538
* browser: chrome
4639
* ```
40+
*
41+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
4742
*/
4843
class MagentoWebDriver extends WebDriver
4944
{
@@ -61,6 +56,8 @@ class MagentoWebDriver extends WebDriver
6156
'//div[@data-role="spinner"]'
6257
];
6358

59+
const STEP_OBJ_BACKTRACE_POS = 2;
60+
6461
/**
6562
* The module required fields, to be set in the suite .yml configuration file.
6663
*
@@ -596,6 +593,19 @@ public function dragAndDrop($source, $target, $xOffset = null, $yOffset = null)
596593
}
597594
}
598595

596+
/**
597+
* @param $field
598+
* @param $value
599+
*/
600+
public function fillSecretField($field, $value)
601+
{
602+
// to protect any secrets from being printed to console the values are executed only at the webdriver level as a
603+
// decrypted value
604+
605+
$decryptedValue = CredentialStore::getInstance()->decryptSecretValue($value);
606+
$this->fillField($field, $decryptedValue);
607+
}
608+
599609
/**
600610
* Override for _failed method in Codeception method. Adds png and html attachments to allure report
601611
* following parent execution of test failure processing.

src/Magento/FunctionalTestingFramework/Test/Objects/TestObject.php

+6-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Magento\FunctionalTestingFramework\Test\Handlers\ActionGroupObjectHandler;
1010
use Magento\FunctionalTestingFramework\Test\Util\ActionMergeUtil;
1111
use Magento\FunctionalTestingFramework\Test\Util\ActionObjectExtractor;
12+
use Magento\FunctionalTestingFramework\Test\Util\TestHookObjectExtractor;
13+
use Magento\FunctionalTestingFramework\Test\Util\TestObjectExtractor;
1214

1315
/**
1416
* Class TestObject
@@ -183,12 +185,10 @@ public function getEstimatedDuration()
183185
}
184186

185187
$hookTime = 0;
186-
if (array_key_exists('before', $this->hooks)) {
187-
$hookTime += $this->calculateWeightedActionTimes($this->hooks['before']->getActions());
188-
}
189-
190-
if (array_key_exists('after', $this->hooks)) {
191-
$hookTime += $this->calculateWeightedActionTimes($this->hooks['after']->getActions());
188+
foreach ([TestObjectExtractor::TEST_BEFORE_HOOK, TestObjectExtractor::TEST_AFTER_HOOK] as $hookName) {
189+
if (array_key_exists($hookName, $this->hooks)) {
190+
$hookTime += $this->calculateWeightedActionTimes($this->hooks[$hookName]->getActions());
191+
}
192192
}
193193

194194
$testTime = $this->calculateWeightedActionTimes($this->getOrderedActions());

src/Magento/FunctionalTestingFramework/Test/Util/ActionMergeUtil.php

+60-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,66 @@ public function resolveActionSteps($parsedSteps, $skipActionGroupResolution = fa
8383
return $this->orderedSteps;
8484
}
8585

86-
return $this->resolveActionGroups($this->orderedSteps);
86+
$resolvedActions = $this->resolveActionGroups($this->orderedSteps);
87+
return $this->resolveSecretFieldAccess($resolvedActions);
88+
}
89+
90+
/**
91+
* Takes an array of actions and resolves any references to secret fields. The function then validates whether the
92+
* refernece is valid and replaces the function name accordingly to hide arguments at runtime.
93+
*
94+
* @param ActionObject[] $resolvedActions
95+
* @return ActionObject[]
96+
* @throws TestReferenceException
97+
*/
98+
private function resolveSecretFieldAccess($resolvedActions)
99+
{
100+
$actions = [];
101+
foreach ($resolvedActions as $resolvedAction) {
102+
$action = $resolvedAction;
103+
$hasSecretRef = $this->actionAttributeContainsSecretRef($resolvedAction->getCustomActionAttributes());
104+
105+
if ($resolvedAction->getType() !== 'fillField' && $hasSecretRef) {
106+
throw new TestReferenceException("You cannot reference secret data outside of fill field actions");
107+
}
108+
109+
if ($resolvedAction->getType() === 'fillField' && $hasSecretRef) {
110+
$action = new ActionObject(
111+
$action->getStepKey(),
112+
'fillSecretField',
113+
$action->getCustomActionAttributes(),
114+
$action->getLinkedAction(),
115+
$action->getActionOrigin()
116+
);
117+
}
118+
119+
$actions[$action->getStepKey()] = $action;
120+
}
121+
122+
return $actions;
123+
}
124+
125+
/**
126+
* Returns a boolean based on whether or not the action attributes contain a reference to a secret field.
127+
*
128+
* @param array $actionAttributes
129+
* @return bool
130+
*/
131+
private function actionAttributeContainsSecretRef($actionAttributes)
132+
{
133+
foreach ($actionAttributes as $actionAttribute) {
134+
if (is_array($actionAttribute)) {
135+
return $this->actionAttributeContainsSecretRef($actionAttribute);
136+
}
137+
138+
preg_match_all("/{{_CREDS\.([\w]+)}}/", $actionAttribute, $matches);
139+
140+
if (!empty($matches[0])) {
141+
return true;
142+
}
143+
}
144+
145+
return false;
87146
}
88147

89148
/**

src/Magento/FunctionalTestingFramework/Test/Util/ActionObjectExtractor.php

+9-6
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public function extractActions($testActions, $testName = null)
5959

6060
foreach ($testActions as $actionName => $actionData) {
6161
$stepKey = $actionData[self::TEST_STEP_MERGE_KEY];
62+
$actionType = $actionData[self::NODE_NAME];
6263

6364
if (empty($stepKey)) {
6465
throw new XmlException(sprintf(self::STEP_KEY_EMPTY_ERROR_MSG, $actionData['nodeName']));
@@ -79,10 +80,7 @@ public function extractActions($testActions, $testName = null)
7980
$actionAttributes['parameterArray'] = $actionData['array']['value'];
8081
}
8182

82-
if ($actionData[self::NODE_NAME] === self::ACTION_GROUP_TAG) {
83-
$actionAttributes = $this->processActionGroupArgs($actionAttributes);
84-
}
85-
83+
$actionAttributes = $this->processActionGroupArgs($actionType, $actionAttributes);
8684
$linkedAction = $this->processLinkedActions($actionName, $actionData);
8785
$actions = $this->extractFieldActions($actionData, $actions);
8886
$actionAttributes = $this->extractFieldReferences($actionData, $actionAttributes);
@@ -98,7 +96,7 @@ public function extractActions($testActions, $testName = null)
9896

9997
$actions[$stepKey] = new ActionObject(
10098
$stepKey,
101-
$actionData[self::NODE_NAME],
99+
$actionType,
102100
$actionAttributes,
103101
$linkedAction['stepKey'],
104102
$linkedAction['order']
@@ -142,11 +140,16 @@ private function processLinkedActions($actionName, $actionData)
142140
* Takes the action group reference and parses out arguments as an array that can be passed to override defaults
143141
* defined in the action group xml.
144142
*
143+
* @param string $actionType
145144
* @param array $actionAttributeData
146145
* @return array
147146
*/
148-
private function processActionGroupArgs($actionAttributeData)
147+
private function processActionGroupArgs($actionType, $actionAttributeData)
149148
{
149+
if ($actionType !== self::ACTION_GROUP_TAG) {
150+
return $actionAttributeData;
151+
}
152+
150153
$actionAttributeArgData = [];
151154
foreach ($actionAttributeData as $attributeDataKey => $attributeDataValues) {
152155
if ($attributeDataKey == self::ACTION_GROUP_REF) {

src/Magento/FunctionalTestingFramework/Test/etc/actionTypeTags.xsd

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<xs:element type="executeInSeleniumType" name="executeInSelenium" minOccurs="0" maxOccurs="unbounded"/>
3838
<xs:element type="executeJSType" name="executeJS" minOccurs="0" maxOccurs="unbounded"/>
3939
<xs:element type="fillFieldType" name="fillField" minOccurs="0" maxOccurs="unbounded"/>
40+
<xs:element type="fillFieldType" name="fillSecretField" minOccurs="0" maxOccurs="unbounded"/>
4041
<xs:element type="loadSessionSnapshotType" name="loadSessionSnapshot" minOccurs="0" maxOccurs="unbounded"/>
4142
<xs:element type="makeScreenshotType" name="makeScreenshot" minOccurs="0" maxOccurs="unbounded"/>
4243
<xs:element type="maximizeWindowType" name="maximizeWindow" minOccurs="0" maxOccurs="unbounded"/>

src/Magento/FunctionalTestingFramework/Util/Iterator/File.php

+4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ public function __construct(array $paths)
3737
*/
3838
public function getFilename()
3939
{
40+
if ($this->current == null) {
41+
return null;
42+
}
43+
4044
return $this->data[$this->key()];
4145
}
4246

src/Magento/FunctionalTestingFramework/Util/TestGenerator.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ private function assembleTestPhp($testObject)
210210
$hookPhp = $this->generateHooksPhp($testObject->getHooks());
211211
$testsPhp = $this->generateTestPhp($testObject);
212212
} catch (TestReferenceException $e) {
213-
throw new TestReferenceException($e->getMessage() . " in Test \"" . $testObject->getName() . "\"");
213+
throw new TestReferenceException($e->getMessage() . "\n" . $testObject->getFilename());
214214
}
215215

216216
$cestPhp = "<?php\n";

0 commit comments

Comments
 (0)