1 20 21 package net.innig.macker.rule; 22 23 import net.innig.macker.rule.filter.*; 24 25 import java.io.*; 26 import java.util.*; 27 28 import net.innig.util.EnumeratedType; 29 import net.innig.util.OrderedType; 30 import net.innig.io.NullOutputStream; 31 32 import org.jdom.*; 33 import org.jdom.input.SAXBuilder; 34 import org.jdom.output.XMLOutputter; 35 36 public class RuleSetBuilder 37 { 38 public RuleSetBuilder() 39 { 40 saxBuilder = new SAXBuilder(false); 41 saxBuilderVerify = new SAXBuilder(true); 42 xmlOut = new XMLOutputter(); 43 44 PrintStream realErr = System.err; 46 try { 47 System.setErr(new PrintStream(new NullOutputStream())); 48 dtdUrlS = getClass().getClassLoader() 49 .getResource("net/innig/macker/macker.dtd") 50 .toExternalForm(); 51 } 52 finally 53 { System.setErr(realErr); } 54 } 55 56 public Collection build(InputStream is) 57 throws RulesException 58 { 59 try { return build(saxBuilder.build(is)); } 60 catch(JDOMException jdome) 61 { throw new RulesDocumentException(jdome); } 62 catch(IOException ioe) 63 { throw new RulesDocumentException(ioe); } 64 } 65 66 public Collection build(Reader reader) 67 throws RulesException 68 { 69 try { return build(saxBuilder.build(reader)); } 70 catch(JDOMException jdome) 71 { throw new RulesDocumentException(jdome); } 72 catch(IOException ioe) 73 { throw new RulesDocumentException(ioe); } 74 } 75 76 public Collection build(File file) 77 throws RulesException 78 { 79 try { return build(saxBuilder.build(file)); } 80 catch(JDOMException jdome) 81 { throw new RulesDocumentException(jdome); } 82 catch(IOException ioe) 83 { throw new RulesDocumentException(ioe); } 84 } 85 86 public Collection build(String fileName) 87 throws RulesException 88 { 89 try { return build(saxBuilder.build(fileName)); } 90 catch(JDOMException jdome) 91 { throw new RulesDocumentException(jdome); } 92 catch(IOException ioe) 93 { throw new RulesDocumentException(ioe); } 94 } 95 96 public Collection build(Document doc) 97 throws RulesException 98 { 99 validateAgainstDTD(doc); 100 return build(doc.getRootElement()); 101 } 102 103 public Collection build(Element elem) 104 throws RulesException 105 { 106 Collection ruleSets = new ArrayList(); 107 for(Iterator rsIter = elem.getChildren("ruleset").iterator(); rsIter.hasNext(); ) 108 ruleSets.add(buildRuleSet((Element) rsIter.next(), RuleSet.getMackerDefaults())); 109 return ruleSets; 110 } 111 112 private void validateAgainstDTD(Document doc) 113 throws RulesDocumentException 114 { 115 doc.setDocType(new DocType("macker", dtdUrlS)); 116 117 StringWriter out = new StringWriter(); 118 try { xmlOut.output(doc, out); } 119 catch(IOException ioe) 120 { 121 ioe.printStackTrace(); 122 throw new RuntimeException ("Unexpected output exception: " + ioe); 123 } 124 Reader in = new StringReader(out.toString()); 125 try { saxBuilderVerify.build(in); } 126 catch(JDOMException jdome) 127 { throw new RulesDocumentException(jdome); } 128 catch(IOException ioe) 129 { throw new RulesDocumentException(ioe); } 130 } 131 132 public RuleSet buildRuleSet(Element ruleSetElem, RuleSet parent) 133 throws RulesException 134 { 135 RuleSet ruleSet = new RuleSet(parent); 136 137 String name = ruleSetElem.getAttributeValue("name"); 138 if(name != null) 139 ruleSet.setName(name); 140 141 buildSeverity(ruleSet, ruleSetElem); 142 143 for(Iterator patIter = ruleSetElem.getChildren().iterator(); patIter.hasNext(); ) 144 { 145 Element subElem = (Element) patIter.next(); 146 String subElemName = subElem.getName(); 147 if(subElemName.equals("pattern")) 148 { 149 String patternName = subElem.getAttributeValue("name"); 150 if(ruleSet.declaresPattern(patternName)) 151 throw new RulesDocumentException( 152 subElem, 153 "Pattern named \"" + patternName + "\" is already defined in this context"); 154 155 ruleSet.setPattern(patternName, buildPattern(subElem, ruleSet)); 156 } 157 else if(subElemName.equals("subset")) 158 { 159 if(ruleSet.getSubsetPattern() != null) 160 throw new RulesDocumentException( 161 subElem, 162 "<ruleset> may only contain a single <subset> element"); 163 ruleSet.setSubsetPattern(buildPattern(subElem, ruleSet)); 164 } 165 else if(subElemName.equals("access-rule")) 166 ruleSet.addRule(buildAccessRule(subElem, ruleSet)); 167 else if(subElemName.equals("var")) 168 ruleSet.addRule(buildVariable(subElem, ruleSet)); 169 else if(subElemName.equals("foreach")) 170 ruleSet.addRule(buildForEach(subElem, ruleSet)); 171 else if(subElemName.equals("ruleset")) 172 ruleSet.addRule(buildRuleSet(subElem, ruleSet)); 173 else if(subElemName.equals("message")) 174 ruleSet.addRule(buildMessage(subElem, ruleSet)); 175 } 176 177 return ruleSet; 178 } 179 180 public Pattern buildPattern(Element patternElem, RuleSet ruleSet) 181 throws RulesException 182 { return buildPattern(patternElem, ruleSet, true, null); } 183 184 public Pattern buildPattern( 185 Element patternElem, 186 RuleSet ruleSet, 187 boolean isTopElem, 188 Pattern nextPat) 189 throws RulesException 190 { 191 193 String otherPatName = patternElem.getAttributeValue("pattern"); 194 String className = getClassNameAttributeValue(patternElem); 195 String filterName = patternElem.getAttributeValue("filter"); 196 197 CompositePatternType patType; 198 if(patternElem.getName().equals("include")) 199 patType = CompositePatternType.INCLUDE; 200 else if(patternElem.getName().equals("exclude")) 201 patType = (filterName == null) 202 ? CompositePatternType.EXCLUDE 203 : CompositePatternType.INCLUDE; 204 else if(isTopElem) 205 patType = CompositePatternType.INCLUDE; 206 else 207 throw new RulesDocumentException( 208 patternElem, 209 "Invalid element <" + patternElem.getName() + "> --" 210 + " expected <include> or <exclude>"); 211 212 if(otherPatName != null && className != null) 213 throw new RulesDocumentException( 214 patternElem, 215 "patterns cannot have both a \"pattern\" and a \"class\" attribute"); 216 217 219 Pattern head = null; 220 if(className != null) 221 head = new RegexPattern(className); 222 else if(otherPatName != null) 223 { 224 head = ruleSet.getPattern(otherPatName); 225 if(head == null) 226 throw new UndeclaredPatternException(otherPatName); 227 } 228 229 231 Pattern childrenPat = null; 232 List children = new ArrayList(patternElem.getChildren()); for(ListIterator childIter = children.listIterator(children.size()); childIter.hasPrevious(); ) 235 { 236 Element subElem = (Element) childIter.previous(); 237 if(subElem.getName().equals("message")) 238 continue; 239 240 childrenPat = buildPattern(subElem, ruleSet, false, childrenPat); 241 } 242 243 245 if(filterName != null) 246 { 247 Map options = new HashMap(); 248 for(Iterator i = patternElem.getAttributes().iterator(); i.hasNext(); ) 249 { 250 Attribute attr = (Attribute) i.next(); 251 options.put(attr.getName(), attr.getValue()); 252 } 253 options.remove("name"); 254 options.remove("pattern"); 255 options.remove("class"); 256 options.remove("regex"); 257 258 Filter filter = FilterFinder.findFilter(filterName); 259 head = filter.createPattern( 260 ruleSet, 261 (head == null) 262 ? Collections.EMPTY_LIST 263 : Collections.singletonList(head), 264 options); 265 266 if(patternElem.getName().equals("exclude")) 267 head = CompositePattern.create(CompositePatternType.EXCLUDE, head, null, null); 268 } 269 270 272 return CompositePattern.create(patType, head, childrenPat, nextPat); 273 } 274 275 public Variable buildVariable(Element forEachElem, RuleSet parent) 276 throws RulesException 277 { 278 String varName = forEachElem.getAttributeValue("name"); 279 if(varName == null) 280 throw new RulesDocumentException( 281 forEachElem, 282 "<var> is missing the \"name\" attribute"); 283 284 String value = forEachElem.getAttributeValue("value"); 285 if(value == null) 286 throw new RulesDocumentException( 287 forEachElem, 288 "<var> is missing the \"value\" attribute"); 289 290 return new Variable(parent, varName, value); 291 } 292 293 public Message buildMessage(Element messageElem, RuleSet parent) 294 throws RulesException 295 { 296 Message message = new Message(parent, messageElem.getText()); 297 buildSeverity(message, messageElem); 298 return message; 299 } 300 301 public ForEach buildForEach(Element forEachElem, RuleSet parent) 302 throws RulesException 303 { 304 String varName = forEachElem.getAttributeValue("var"); 305 if(varName == null) 306 throw new RulesDocumentException( 307 forEachElem, 308 "<foreach> is missing the \"var\" attribute"); 309 310 String className = getClassNameAttributeValue(forEachElem); 311 if(className == null) 312 throw new RulesDocumentException( 313 forEachElem, 314 "<foreach> is missing the \"class\" attribute"); 315 316 ForEach forEach = new ForEach(parent); 317 forEach.setVariableName(varName); 318 forEach.setRegex(className); 319 forEach.setRuleSet(buildRuleSet(forEachElem, parent)); 320 return forEach; 321 } 322 323 public AccessRule buildAccessRule(Element ruleElem, RuleSet ruleSet) 324 throws RulesException 325 { 326 AccessRule prevRule = null, topRule = null; 327 for(Iterator childIter = ruleElem.getChildren().iterator(); childIter.hasNext(); ) 328 { 329 Element subElem = (Element) childIter.next(); 330 AccessRule accRule = new AccessRule(ruleSet); 331 332 if(subElem.getName().equals("allow")) 333 accRule.setType(AccessRuleType.ALLOW); 334 else if(subElem.getName().equals("deny")) 335 accRule.setType(AccessRuleType.DENY); 336 else if(subElem.getName().equals("from") 337 || subElem.getName().equals("to") 338 || subElem.getName().equals("message")) 339 continue; 340 else 341 throw new RulesDocumentException( 342 subElem, 343 "Invalid element <" + subElem.getName() + "> --" 344 + " expected an access rule (<deny> or <allow>)"); 345 346 Element fromElem = subElem.getChild("from"); 347 if(fromElem != null) 348 accRule.setFrom(buildPattern(fromElem, ruleSet)); 349 350 Element toElem = subElem.getChild("to"); 351 if(toElem != null) 352 accRule.setTo(buildPattern(toElem, ruleSet)); 353 354 if(!subElem.getChildren().isEmpty()) 355 accRule.setChild(buildAccessRule(subElem, ruleSet)); 356 357 if(topRule == null) 358 topRule = accRule; 359 else 360 prevRule.setNext(accRule); 361 prevRule = accRule; 362 } 363 if(topRule != null) 364 { 365 topRule.setMessage(ruleElem.getChildText("message")); 366 buildSeverity(topRule, ruleElem); 367 } 368 return topRule; 369 } 370 371 public void buildSeverity(Rule rule, Element elem) 372 throws RulesDocumentException 373 { 374 String severityS = elem.getAttributeValue("severity"); 375 if(severityS != null && !"".equals(severityS)) 376 { 377 RuleSeverity severity; 378 try { severity = RuleSeverity.fromName(severityS); } 379 catch(IllegalArgumentException iae) 380 { throw new RulesDocumentException(elem, iae.getMessage()); } 381 rule.setSeverity(severity); 382 } 383 } 384 385 private String getClassNameAttributeValue(Element elem) 386 { 387 String value = elem.getAttributeValue("class"); 388 if(value == null) 389 { 390 value = elem.getAttributeValue("regex"); 391 if(value != null) 392 System.err.println("WARNING: The \"regex\" attribute is deprecated, and will be removed in v1.0. Use \"class\" instead"); 393 } 394 return value; 395 } 396 397 private SAXBuilder saxBuilder, saxBuilderVerify; 398 private XMLOutputter xmlOut; 399 private String dtdUrlS; 400 } 401 402 403 404 | Popular Tags |