xebia / Xebium

Xebium provides Selenium (webdriver) bindings for FitNesse, with Selenium-IDE support
http://xebia.github.com/Xebium/
Apache License 2.0
76 stars 62 forks source link

Is there a way to hide rows in a table? #119

Closed jeremymv2 closed 10 years ago

jeremymv2 commented 10 years ago

I've figured out how to use environment variables passed into the JVM to populate my password variable in a fitnesse table, but the actual password value still gets stored in the output results of the test. Is there a way to hide the table row so that the password variable value never gets displayed?

raboof commented 10 years ago

First off: I think it's kind of suspicious that your test system needs access to (apparently) sensitive information.

The only way I can think of is by keeping the variable outside of your FitNesse test entirely, and get it directly from the system properties inside a fixture. You'd want to have access to some Xebium internals for that I guess though, filed issue #120 for that.

Kind regards,

Arnout

On Wed, Apr 30, 2014 at 12:16 AM, jeremymv2 notifications@github.comwrote:

I've figured out how to use environment variables passed into the JVM to populate my password variable in a fitnesse table, but the actual password value still gets stored in the output results of the test. Is there a way to hide the table row so that the password variable value never gets displayed?

— Reply to this email directly or view it on GitHubhttps://github.com/xebia/Xebium/issues/119 .

wilcokoorn commented 10 years ago

Hi,

Perhaps another option is to abstract the password away from the table and let a Fixture handle it.

Some like this:

Instead of: | connect | | | |

just use: | script | MyConnectionFixture | | connect |

Now the fixture is in control and you can let the fixture retrieve the password in some safe way without any logging.

Just my 2c ;-)

Regards, Wilco.

On 30 Apr 2014, at 14:37, Arnout Engelen notifications@github.com<mailto:notifications@github.com> wrote:

First off: I think it's kind of suspicious that your test system needs access to (apparently) sensitive information.

The only way I can think of is by keeping the variable outside of your FitNesse test entirely, and get it directly from the system properties inside a fixture. You'd want to have access to some Xebium internals for that I guess though, filed issue #120 for that.

Kind regards,

Arnout

On Wed, Apr 30, 2014 at 12:16 AM, jeremymv2 notifications@github.com<mailto:notifications@github.com>wrote:

I've figured out how to use environment variables passed into the JVM to populate my password variable in a fitnesse table, but the actual password value still gets stored in the output results of the test. Is there a way to hide the table row so that the password variable value never gets displayed?

— Reply to this email directly or view it on GitHubhttps://github.com/xebia/Xebium/issues/119 .

— Reply to this email directly or view it on GitHubhttps://github.com/xebia/Xebium/issues/119#issuecomment-41791196.

jguglielmi commented 10 years ago

I ran into this issue a little bit ago. While dealing with exposed passwords especially, we preferred Encryption over direct exposure. To encrypt, we prefixed a String with a "decrypt:" tag, passed it into our fixture, and then handled it internally. Below is the OASIS implementation of our augmented SeleniumDriverFixture:

private String encryptString(String password) {

        if (password != null) {
            Cipher aes = null;
            try {
                aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
                aes.init(Cipher.ENCRYPT_MODE, generateKey());
                byte[] ciphertext = aes.doFinal(password.getBytes());
                password = "decrypt:" + bytesToHex(ciphertext);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return password;
    }

    public String bytesToHex(byte[] bytes) {
        final char[] hexArray = "0123456789ABCDEF".toCharArray();
        char[] hexChars = new char[bytes.length * 2];
        int v;
        for ( int j = 0; j < bytes.length; j++ ) {
            v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    public SecretKeySpec generateKey() {
        String passStr="XebiumIsConnectedToTheSikuliBone";
        SecretKey tmp = null;
        try{
            String saltStr = "fijsd@#saltr9jsfizxnv";
            byte[] salt = saltStr.getBytes();
            int iterations = 43210;
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            tmp = factory.generateSecret(new PBEKeySpec(passStr.toCharArray(), salt, iterations, 128));
        }
        catch(Exception e){
            e.printStackTrace();
        }
        return new SecretKeySpec(tmp.getEncoded(), "AES");
    }
}
jguglielmi commented 10 years ago

And to decrypt the String...

public static String processDecryptionString(String val) {
    if (val.startsWith("decrypt:") ) {
        try {
            val = val.substring(val.indexOf(":") + 1);
            SecretKeySpec key = SikuliResponder.generateKey();
            Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
            aes.init(Cipher.DECRYPT_MODE, key);
            String cleartext = new String(aes.doFinal(hexToBytes(val)));
            return cleartext;
        }
        catch (Exception e) {
            //LOG.info("Exception processDecryptionString:" + e.getMessage());
            e.printStackTrace();
            return val;
        }
    }
    else
        return val;
}
jeremymv2 commented 10 years ago

Thanks folks, you've given me some food for thought. Initially I thought I discovered the solution by using wiki markup to "hide" the part of the fitnesse table:

!_< Hide | script | | ensure | do | type | on | id=Password | with | ${pass} | _!

My thought was to pass the value of ${pass} in as a JVM argument via Environment variable to the stand-alone fitness client from our CI engine. Even though the value of ${pass} isn't exposed in the test page nor in the "output" of the test page, it still gets stored in plain text in the xml result of the test run.. Abstracting it away from the table and into the fixture code seems to be the way to do it, which will work for our developers who can write fixtures but probably not for most of our QA's who can't code and will realistically only be able to use the simple Xebium dsl "ensure do ...".

jguglielmi commented 10 years ago

I don't see why not. You can add custom commands to the SeleniumDriverFixture.java file for your testers. They can still use it. We use the old fashioned type command, but just added some conditions prior to passing it into the command processor. You can see for yourself:

private String executeCommand(final ExtendedSeleniumCommand command, final String[] values, long delay) {
    if (LOG.isDebugEnabled()) {
        LOG.debug("executeCommand. Command: " + command.getSeleniumCommand() + " with values: [" + join(values, ", ") +"]");
    }

    if (commandProcessor == null) {
        throw new IllegalStateException("Command processor not running. " +
                        "First start it by invoking startBrowserOnUrl.");
    }

    // Handle special cases first
    if ("pause".equals(command.getSeleniumCommand())) {
        try {
            Thread.sleep(Long.parseLong(values[0]));
        } catch (Exception e) {
            LOG.warn("Pause command interrupted", e);
        }
        return null;
    }

    if ("contextMenu".equals(command.getSeleniumCommand())) {
        try {
            WebElement webElement = getWebDriver().findElement(By.xpath(values[0]));
            new Actions(getWebDriver()).contextClick(webElement).perform();
            LOG.info("Performing | contextMenu | " + values[0] + " | " );
        } catch (Exception e) {
            LOG.warn("contextMenu command interrupted", e);
        }
        return null;
    }

    String output = null;
    try {
        if (command.returnTypeIsArray()) {
            output = executeArrayCommand(command.getSeleniumCommand(), values);
        } else {
            output = executeCommand(command.getSeleniumCommand(), values);
        }
    } catch (final SeleniumException e) {
        output = "Execution of command failed: " + e.getMessage();
        LOG.error(output);
        if (!(command.isAssertCommand() || command.isVerifyCommand() || command.isWaitForCommand())) {
            throw e;
        }
    }

    if (command.isAndWaitCommand()) {
        commandProcessor.doCommand("waitForPageToLoad", new String[] { "" + timeout });
    }

    if (delay > 0) {
        try {
            Thread.sleep(delay);
        } catch (Exception e) {
            LOG.warn("Step delay sleep command interrupted", e);
        }
    }
    return output;
}
jguglielmi commented 10 years ago

You'd just add "type" to the list and specify the methods to decrypt first just like the "contextMenu" example. It'll then get passed to the commandProcessor as plain text.

jeremymv2 commented 10 years ago

Thanks, this is what I needed :)