SafeInput.java

Download SafeInput.java

  1: //package safehello;
  2: 
  3: import java.text.Normalizer;
  4: import static java.text.Normalizer.Form.*;
  5: 
  6: import javafx.application.Application;
  7: import javafx.event.*;
  8: import javafx.geometry.*;
  9: import javafx.scene.Scene;
 10: import javafx.scene.control.*;
 11: import javafx.scene.layout.*;
 12: import javafx.scene.paint.Color;
 13: import javafx.scene.text.*;
 14: import javafx.stage.Stage;
 15: 
 16: /**
 17:  * Main class for safe I18N coding practices, JavaFX demo.
 18:  * This code shows (in a simple JavaFX GUI) how to normalize, sanitize,
 19:  * and validate user input.
 20:  *
 21:  * @author Wayne Pollock, Tampa Florida USA
 22:  */
 23: public class SafeInput extends Application {
 24: 
 25:     public static void main(String[] args) {
 26:         launch(args);
 27:     }
 28: 
 29:     @Override
 30:     public void start(Stage primaryStage) {
 31:         primaryStage.setTitle("Safe I18N Coding Demo");
 32: 
 33:         // Use JavaFX Grid Layout:
 34:         GridPane grid = new GridPane();
 35:         grid.setAlignment(Pos.CENTER);
 36:         grid.setHgap(10);
 37:         grid.setVgap(24);
 38:         grid.setPadding(new Insets(25, 25, 25, 25));
 39:         Scene scene = new Scene(grid, 350, 275);
 40:         primaryStage.setScene(scene);
 41: 
 42:         // Add components to the stage, via the layout object:
 43:         Text title = new Text("Safe I18N Coding Demo");
 44:         title.setFont(Font.font("sans-serif", FontWeight.NORMAL, 20));
 45:         grid.add(title, 0, 0, 2, 1);
 46: 
 47:         Label userNameLbl = new Label("User Name:");
 48:         grid.add(userNameLbl, 0, 1);
 49: 
 50: 
 51:         final ComboBox<String> userName = new ComboBox<>();
 52:         userName.getItems().addAll(
 53:                 "",
 54:                 "Joe-Bob Jr., 3rd.",
 55:                 "touch" + "\u00e9", // Accent
 56:                 "a" + "\ufb03" + "ance", // Ligature
 57:                 "fa" + "\u00e7" + "ade", // Cedilla
 58:                 "\uff81\uff6e\uff7a\uff9a\uff70\uff84", //half-width Katakana
 59:                 "<em>HTML</em>",  // illegal characters
 60:                 "Hubert Blaine Wolfeschlegelsteinhausenbergerdorff"
 61:                 );
 62:         userName.setEditable(true);
 63:         userName.setValue("");
 64:         grid.add(userName, 1, 1);
 65: 
 66:         Text greeting = new Text();
 67:         greeting.setFill(Color.BLACK);
 68:         grid.add(greeting, 0, 3, 2, 1);
 69: 
 70:         Button btn = new Button( "Greet");
 71:         btn.setDefaultButton(true);
 72: 
 73:         grid.add(btn, 1, 2);
 74: 
 75:         // Hook up event handling:
 76:         btn.setOnAction(new EventHandler<ActionEvent>() {
 77:             @Override
 78:             public void handle(ActionEvent e) {
 79:                 String name = nsv( userName.getValue().toString() );
 80:                 if ( name == null || name.length() == 0 ) {
 81:                     greeting.setText("<no valid name selected>");
 82:                 } else {
 83:                     greeting.setText("Howdy, " + name + "!");
 84:                 }
 85:             }
 86:         });
 87: 
 88:         // Make UI visible:
 89:         primaryStage.show();
 90:     }
 91: 
 92:     /**
 93:      * nsv will Normalize Unicode, then Sanitize via a whitelist regular
 94:      * expression, and finally validate the data (starts with a letter and
 95:      * is less than 20 characters long.  The resulting String
 96:      * (which may be empty) is returned.  In a secure context (user name
 97:      * for login purposes), this method should throw an exception for bad
 98:      * input, which should include an encoded (safe) version to include in
 99:      * log messages.
100:      *
101:      * @param rawInput - The untrusted String
102:      * @return a normalized, sanitized, and validated String
103:      */
104:     public String nsv ( String rawInput ) {
105:         // Normalize Unicode first: use NFC; for identifiers (such as
106:         // user names for logins) and passwords, use NFKC:
107:         if ( ! Normalizer.isNormalized(rawInput, NFC) ) {
108:             rawInput = Normalizer.normalize(rawInput, NFC);
109:         }
110: 
111:         // Sanitize: Remove any illegal (not a letter, digit, dash, space,
112:         //           period, or comma) characters:
113:         String clean = rawInput.replaceAll("[^-\\p{Alnum} .,]", "");
114: 
115:         // Validate: Ensure userName has letters, and length is <20:
116:         if ( clean.length() > 20) {
117:             clean = clean.substring(0, 20);
118:         }
119:         if ( ! clean.matches("^\\p{Alpha}.*")) {
120:             clean = ""; // String should start with a letter
121:         }
122:         return clean;
123:     }
124: }