
翻蚳è
ããïŒ CUBAãã©ãããã©ãŒã ã®éçºæã«ããã®ãã¬ãŒã ã¯ãŒã¯ã«ã«ã¹ã¿ã ã¹ã¯ãªãããå®è¡ããŠãã¢ããªã±ãŒã·ã§ã³ã®ããžãã¹ããžãã¯ãããæè»ã«æ§æã§ããããã«ããŸããã ãã®æ©äŒãè¯ããæªããïŒãããŠç§ãã¡ã¯CUBAã«ã€ããŠè©±ããŠããã ãã§ã¯ãããŸããïŒã¯é·ãéè°è«ãããŠããŸããããŠãŒã¶ãŒã¹ã¯ãªããã®å®è¡ãå¶åŸ¡ããå¿
èŠããããšããäºå®ã¯åé¡ã«ãªããŸããã ã«ã¹ã¿ã ã¹ã¯ãªããã®å®è¡ã管çããããã®Groovyã®äŸ¿å©ãªæ©èœã®1ã€ããã»ããªãã¯ã·ã£ã³ããŒã®ãã®ç¿»èš³ã§ç޹ä»ãããŠããŸãã 圌ãæè¿GroovyéçºããŒã ãèŸãããšããäºå®ã«ãããããããããã°ã©ããŒã®ã³ãã¥ããã£ã¯ä»åŸé·ãé圌ã®åŽåãå©çšããã§ãããã
Groovyã䜿çšãããšã©ã³ã¿ã€ã ã§ã³ãŒããç°¡åã«åçã«å®è¡ã§ãããããGroovyã䜿çšããæãäžè¬çã«äœ¿çšãããæ¹æ³ã®1ã€ã¯ã¹ã¯ãªããã§ãã ã¢ããªã±ãŒã·ã§ã³ã«å¿ããŠãã¹ã¯ãªããã¯ãã¡ã€ã«ã·ã¹ãã ãããŒã¿ããŒã¹ããªã¢ãŒããµãŒãã¹ãªã©ã®ããŸããŸãªå Žæã«é
眮ã§ããŸãããæãéèŠãªã®ã¯ãã¹ã¯ãªãããå®è¡ããã¢ããªã±ãŒã·ã§ã³ã®éçºè
ãå¿
ãããããããèšè¿°ããå¿
èŠããªãããšã§ãã ããã«ãã¹ã¯ãªããã¯éãããç°å¢ïŒéãããã¡ã¢ãªããã¡ã€ã«èšè¿°åã®æ°ã®å¶éãã©ã³ã¿ã€ã ...ïŒã§åäœãããããŠãŒã¶ãŒãã¹ã¯ãªããã®ãã¹ãŠã®èšèªæ©èœã䜿çšã§ããªãããã«ããããšãã§ããŸãã
ãã®æçš¿ã¯ããªãã«æããŠãããŸãã
- Groovyãå
éšDSLãæžãã®ã«é©ããŠããçç±
- ã¢ããªã±ãŒã·ã§ã³ã®ã»ãã¥ãªãã£ã®é¢ã§ãã®æ©èœã¯äœã§ãã
- DSLãæ¹åããããã«ã³ã³ãã€ã«ãæ§æããæ¹æ³
SecureASTCustomizer
ã®äŸ¡å€ã«ã€ããŠ- åå¶åŸ¡æ¡åŒµæ©èœã«ã€ããŠ
- åå¶åŸ¡æ¡åŒµæ©èœã䜿çšããŠãµã³ãããã¯ã¹ã广çã«ããæ¹æ³
ããšãã°ããŠãŒã¶ãŒãæ°åŒãèšç®ã§ããããã«ããããã«å¿
èŠãªããšãæ³åããŠãã ããã å®è£
ãªãã·ã§ã³ã®1ã€ã¯ãå
éšDSLãåã蟌ã¿ãããŒãµãŒãäœæããæåŸã«ãããã®åŒã®ã€ã³ã¿ãŒããªã¿ãŒãäœæããããšã§ãã ãã¡ããããããè¡ãã«ã¯äœæ¥ãå¿
èŠã«ãªããŸãããããšãã°ãã€ã³ã¿ãŒããªã¿ãŒã§åŒãèšç®ããããã©ã³ã¿ã€ã ã§çæãããã¯ã©ã¹ã®ãã£ãã·ã³ã°ã䜿çšãã代ããã«åŒã®ãã€ãã³ãŒããçæãããªã©ãããã©ãŒãã³ã¹ãæ¹åããå¿
èŠãããå ŽåãGroovyã¯çŽ æŽããããªãã·ã§ã³ã§ã
ããã¥ã¡ã³ãã«ã¯å€ãã®ãªãã·ã§ã³ã説æãããŠããŸãããæãç°¡åãªäŸã¯Eval
ã¯ã©ã¹ã䜿çšããããšã§ãã
Example.java
int sum = (Integer) Eval.me("1+1");
1+1
ã³ãŒãã¯è§£æããããã€ãã³ãŒãã«ã³ã³ãã€ã«ãããå®è¡æã«Groovyã«ãã£ãŠããŒãããã³å®è¡ãããŸãã ãã¡ããããã®ãµã³ãã«ã®ã³ãŒãã¯éåžžã«åçŽã§ããããã©ã¡ãŒã¿ãŒã远å ããå¿
èŠããããŸãããèãã¯å®è¡å¯èœã³ãŒãã¯ä»»æã§ãããšããããšã§ãã ãããŠãããã¯ãŸãã«ããªããå¿
èŠãšãããã®ã§ã¯ãªããããããŸããã èšç®æ©ã§ã¯ã次ã®ãããªãã®ãèš±å¯ããå¿
èŠããããŸãã
1+1 x+y 1+(2*x)**y cos(alpha)*r v=1+x
確ãã«ããã§ã¯ãªã
println 'Hello' (0..100).each { println 'Blah' } Pong p = new Pong() println(new File('/etc/passwd').text) System.exit(-1) Eval.me('System.exit(-1)') // a script within a script!
ããããå°é£ãå§ãŸããããã€ãã®åé¡ã解決ããå¿
èŠãããããšãæããã«ãªããŸãã
- èšèªã®ææ³ããã®æ©èœã®ãµãã»ããã«å¶éãã
- ãŠãŒã¶ãŒãã³ãŒããå®è¡ã§ããªãããã«ãã
- æªæã®ããã³ãŒãã®å®è¡ãé²ã
é»åã®äŸã¯éåžžã«åçŽã§ãããããè€éãªDSLã®å Žåãç¹ã«DSLãéçºè
ã䜿çšã§ããªãã»ã©åçŽãªå Žåãåé¡ã®ããã³ãŒããæžããŠããããšã«æ°ä»ããªããããããŸããã
æ°å¹Žåãç§ã¯ãã®ãããªç¶æ³ã«ãããŸããã èšèªåŠè
ã«ãã£ãŠæžãããGroovyãã¹ã¯ãªããããå®è¡ãããšã³ãžã³ãéçºããŸããã ããšãã°ãåé¡ã®1ã€ã¯ããšã³ãã¬ã¹ã«ãŒãã誀ã£ãŠäœæãããå¯èœæ§ãããããšã§ãã ãµãŒããŒã§ã³ãŒããå®è¡ãããCPUã100ïŒ
æ¶è²»ããã¹ã¬ãããçŸããåŸãã¢ããªã±ãŒã·ã§ã³ãµãŒããŒãåèµ·åããå¿
èŠããããŸããã DSLãããŒã«ãã¢ããªã±ãŒã·ã§ã³ã®ããã©ãŒãã³ã¹ã«åœ±é¿ãäžããã«åé¡ã解決ããæ¹æ³ã暡玢ããå¿
èŠããããŸããã
å®éãå€ãã®äººã
ãåæ§ã®ããŒãºãæã£ãŠããŸãã éå»4幎éãç§ã¯åã質åãããŠããå€ãã®äººã
ãšè©±ããŠããŸãããGroovyã¹ã¯ãªããã§ãŠãŒã¶ãŒããã³ã»ã³ã¹ãªããšãããªãããã«ããã«ã¯ã©ãããã°ããã§ããããïŒ
ã«ã¹ã¿ãã€ãºã³ã³ãã€ã©
åœæãç§ã¯ãã§ã«èªåã§æ±ºæãäžããä»ã®äººãåæ§ã®äœããéçºããããšãç¥ã£ãŠããŸããã æåŸã«ãGuillaume Laforgeã¯ããããã®åé¡ã®è§£æ±ºã«åœ¹ç«ã€ã¡ã«ããºã ãGroovyã«ãŒãã«ã«äœæããããšãææ¡ããŸããã Groovy 1.8.0ã§ã¯ã ã³ã³ãã€ã«ã«ã¹ã¿ãã€ã¶ãŒãšããŠç»å ŽããŸããã
ã³ã³ãã€ã«ã«ã¹ã¿ãã€ã¶ãŒã¯ãGroovyã¹ã¯ãªããã®ã³ã³ãã€ã«ããã»ã¹ã倿Žããäžé£ã®ã¯ã©ã¹ã§ãã ç¬èªã®ã«ã¹ã¿ãã€ã¶ãŒãäœæã§ããŸãããGroovyã¯ä»¥äžãæäŸããŸãã
- ãŠãŒã¶ãŒãã€ã³ããŒãã®èª¬æã远å ããå¿
èŠããªãããã«ãã€ã³ããŒãã«ã¹ã¿ãã€ã¶ãŒãã¹ã¯ãªããã«æé»çã«ã€ã³ããŒãã远å ãã
- ã«ã¹ã¿ãã€ã¶ãŒASTïŒæœè±¡æ§æããªãŒïŒå€æãAST倿ãã¹ã¯ãªããã«çŽæ¥è¿œå ã§ããŸãã
- èšèªã®ææ³ãšæ§ææ§æãå¶éããå®å
šãªASTã«ã¹ã¿ãã€ã¶ãŒ
AST倿ã®ã«ã¹ã¿ãã€ã¶ãŒã¯ã @ThreadInterrupt
倿ã«ããç¡éã«ãŒãã®åé¡ã解決ããã®ã«åœ¹ç«ã¡ãŸããããã»ãšãã©ã®å Žåã SecureASTCustomizerã¯ããããæã誀解ããããã®ã§ãã
ç³ãèš³ãããŸããã ããããç§ã¯ããããååãæãä»ãããšãã§ããªãã£ãã ãSecureASTCustomizerããšããååã®æãéèŠãªéšåã¯ASTã§ãã ãã®ã¡ã«ããºã ã®ç®çã¯ãç¹å®ã®AST颿°ãžã®ã¢ã¯ã»ã¹ãå¶éããããšã§ããã ã¿ã€ãã«ã®ãã»ãã¥ã¢ããšããèšèã¯äžè¬ã«äžèŠã§ãããã®çç±ã説æããŸãã ãDeadly Groovy SecureASTCustomizerããšããã¿ã€ãã«ã®Jenkinsã§æåãªå·å£åº·ä»ã«ããããã°æçš¿ããããŸãã ãããŠããã¹ãŠãéåžžã«æ£ããããã«æžãããŠããŸãã SecureASTCustomizerã¯ããµã³ãããã¯ã¹çšã«èšèšãããŠããŸããã ã³ã³ãã€ã«æã®èšèªãå¶éããããã«äœæãããŸããããå®è¡ã¯å¶éãããŠããŸããã ä»ãç§ã¯æé«ã®ååãGrammarCustomizerã«ãªããšæããŸãã ãããããåãã®ãšãããã³ã³ãã¥ãŒã¿ãŒãµã€ãšã³ã¹ã«ã¯3ã€ã®å°é£ããããŸãããã£ãã·ã¥ã®ç¡å¹åãååã®çºæããŠãããããšã®ãšã©ãŒã§ãã
ããã§ãã¹ã¯ãªããã®ã»ãã¥ãªãã£ã確ä¿ããææ®µãšããŠå®å
šãªASTã«ã¹ã¿ãã€ã¶ãŒãæ€èšããŠããããšãæ³åããŠãã ãããã¿ã¹ã¯ã¯ããŠãŒã¶ãŒãã¹ã¯ãªããããSystem.exit
ãSystem.exit
ãªãããã«ããSystem.exit
ã ããã¥ã¡ã³ããŒã·ã§ã³ã«ã¯ããã©ãã¯ãªã¹ããŸãã¯ãã¯ã€ããªã¹ããäœæããããšã«ãããç¹å¥ãªåä¿¡è
ã§é話ãçŠæ¢ã§ããããšãèšèŒãããŠããŸãã ã»ãã¥ãªãã£ãå¿
èŠãªå Žåã¯ãèš±å¯ããããã®ãå³å¯ã«èšèŒãããã¯ã€ããªã¹ããåžžã«ãå§ãããŸãããäœãçŠæ¢ãããã©ãã¯ãªã¹ãã¯ãå§ãããŸããã ããã«ãŒã¯åžžã«ããªããèæ
®ããªãã£ããããããªãããšã«ã€ããŠèããããã§ãã äŸãæããŸãããã
SecureASTCustomizer
ã䜿çšããŠããªããã£ããµã³ãããã¯ã¹ã¹ã¯ãªãããšã³ãžã³ãSecureASTCustomizer
æ¹æ³ã¯SecureASTCustomizer
ã§ãã Groovyã§äœæããããšãã§ããŸãããçµ±åã³ãŒããšã¹ã¯ãªããã®éããããæç¢ºã«ãªãããã«ãJavaã®æ§æäŸã瀺ããŸãã
public class Sandbox { public static void main(String[] args) { CompilerConfiguration conf = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); customizer.setReceiversBlackList(Arrays.asList(System.class.getName())); conf.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(conf); Object v = shell.evaluate("System.exit(-1)"); System.out.println("Result = " +v); } }
- ã³ã³ãã€ã©ãŒæ§æãäœæãã
- å®å
šãªASTã«ã¹ã¿ãã€ã¶ãŒãäœæãã
- ã¡ãœããåŒã³åºãã®åä¿¡è
ãšããŠã®
System
ã¯ã©ã¹ããã©ãã¯ãªã¹ãã«ç»é²ãããŠããããšã宣èšãã - ã³ã³ãã€ã©ãŒæ§æã«ã«ã¹ã¿ãã€ã¶ãŒã远å ãã
- æ§æãã·ã§ã«ã¹ã¯ãªããã«ãã€ã³ãããŸããã€ãŸãããµã³ãããã¯ã¹ãäœæããããšããŸãã
- ãæªããã¹ã¯ãªãããå®è¡ãã
- ã¹ã¯ãªããã®å®è¡çµæã衚瀺ãã
ãã®ã¯ã©ã¹ãå®è¡ãããšãã¹ã¯ãªããã®å®è¡äžã«ãšã©ãŒãçºçããŸãã
General error during canonicalization: Method calls not allowed on [java.lang.System] java.lang.SecurityException: Method calls not allowed on [java.lang.System]
ãã®çµè«ã¯ã System
ã¯ã©ã¹ã®ã¡ãœããã®å®è¡ãèš±å¯ããªãå®å
šãªASTã«ã¹ã¿ãã€ã¶ãåããã¢ããªã±ãŒã·ã§ã³ã«ãã£ãŠçºè¡ãããŸãã æåïŒ ã¹ã¯ãªãããä¿è·ããŸããïŒ ããããã¡ãã£ãšåŸ
ã£ãŠ...
SecureASTCustomizerããããã³ã°ãããŠããŸãïŒ
ä¿è·ãèšãïŒ ãããããããè¡ããšã©ããªããŸããïŒ
def c = System c.exit(-1)
ããã°ã©ã ãå床å®è¡ãããšããšã©ãŒãªãã§ã¯ã©ãã·ã¥ããç»é¢ã«çµæã衚瀺ãããªãããšãããããŸãã ããã»ã¹çµäºã³ãŒãã¯-1ã§ããããã¯ããŠãŒã¶ãŒã¹ã¯ãªãããå®è¡ãããããšãæå³ããŸãã ã©ããã ã³ã³ãã€ã«æã«ãå®å
šãªASTã«ã¹ã¿ãc.exit
ã¯ã c.exit
ãASTã¬ãã«ã§æ©èœãããããååãšããŠSystem
ã¡ãœããã®åŒã³åºãã§ããããšãèªèã§ããŸããïŒ ã¡ãœããåŒã³åºããåæããŸãããã®å Žåãã¡ãœããåŒã³åºãã¯c.exit(-1)
ã§ãåä¿¡è
ãæ±ºå®ããåä¿¡è
ããã¯ã€ãïŒãŸãã¯ãã©ãã¯ïŒãªã¹ãã«ãããã©ããã確èªããŸãã ãã®å Žåãåä¿¡è
ã¯c
ããã®å€æ°ã¯defãä»ããŠå®£èšãããããã¯Object
ãšããŠå®£èšããã®ãšåãã§ãããã»ãã¥ã¢ASTã«ã¹ã¿ãã€ã¶ãŒã¯å€æ°c
åãSystem
ã§ã¯ãªãObject
ã§ãããšèãSystem
ïŒ
äžè¬ã«ãã»ãã¥ã¢ASTã«ã¹ã¿ãã€ã¶ãŒã§äœæãããããŸããŸãªæ§æãåé¿ããæ¹æ³ã¯å€æ°ãããŸãã ããã«ããã€ãã®ã¯ãŒã«ãªãã®ããããŸãïŒ
((Object)System).exit(-1) Class.forName('java.lang.System').exit(-1) ('java.lang.System' as Class).exit(-1) import static java.lang.System.exit exit(-1)
ããã«å€ãã®å¯èœæ§ããããŸãã Groovyã®åçãªæ§è³ªã«ãããã³ã³ãã€ã«æã«ãããã®åé¡ãä¿®æ£ããããšãã§ããŸããã ãã ãããœãªã¥ãŒã·ã§ã³ãååšããŸãã 1ã€ã®ãªãã·ã§ã³ã¯ãæšæºã®JVMã»ãã¥ãªãã£ãããŒãžã£ãŒã«äŸåããããšã§ãã ãã ããããã¯ã·ã¹ãã å
šäœã§ããã«äœ¿çšã§ããééã®ãã倧éã®ãœãªã¥ãŒã·ã§ã³ã§ãããã¹ãºã¡ã§å€§ç ²ãçºå°ããã®ãšåãã§ãã ããã«ããã¹ãŠã®å Žåã«æ©èœããããã§ã¯ãããŸãããããšãã°ããã¡ã€ã«ã®èªã¿åããçŠæ¢ãããäœæã¯ããªãå Žåãªã©ã§ãã
ãã®å¶éã¯ããããç§ãã¡ã®å€ãã«ãšã£ãŠã¯æããã§ããã å®è¡äžã®ãã§ãã¯ã«åºã¥ãããœãªã¥ãŒã·ã§ã³ã®äœæã«ã€ãªãããŸããã ãã®ã¿ã€ãã®ãã§ãã¯ã«ã¯ããã®ãããªåé¡ã¯ãããŸããã ããšãã°ãã¡ãœããåŒã³åºãã®æ€èšŒãéå§ããåã«ãã¡ãã»ãŒãžã®å®éã®åä¿¡è
ã¿ã€ãããããããã§ãã ç¹ã«è峿·±ãã®ã¯ã次ã®å®è£
ã§ãã
ãã ãããããã®å®è£
ã¯ã©ããå®å
šã«ä¿¡é Œã§ãå®å
šã§ã¯ãããŸããã ããšãã°ãKosukeã®ããŒãžã§ã³ã¯ãå
éšã³ãŒã«ãµã€ããã£ãã·ã³ã°å®è£
ã®ããã¯ã«åºã¥ããŠããŸãã åé¡ã¯ãintrodynamicããŒãžã§ã³ã®Groovyãšäºææ§ããªãããããã®å
éšã¯ã©ã¹ãGroovyã®å°æ¥ã®ããŒãžã§ã³ã«å«ãŸããªãããšã§ãã äžæ¹ãSimonã®ããŒãžã§ã³ã¯AST倿ã«åºã¥ããŠããŸãããå€ãã®æœåšçãªããŒã«ãæ®ããŠããŸãã
ãã®çµæãç§ã®å人Corinne CrischãFabrizia MatratãSebastian BlancããããŠç§ã¯ã©ã³ã¿ã€ã ã§æ°ãããµã³ãããã¯ã¹ã¡ã«ããºã ãäœæããããšã«æ±ºããŸããããããã®ãããžã§ã¯ãã®ãããªåé¡ã¯ãããŸããã ããŒã¹ã®ããã«ãœã³ã§å®è£
ãéå§ããæšå¹Žã®Greachã«ã³ãã¡ã¬ã³ã¹ã§å ±åããŸãã ã ãã®ã¡ã«ããºã ã¯AST倿ã«åºã¥ããŠãããåã¡ãœããã®åŒã³åºããã¯ã©ã¹ãã£ãŒã«ããžã®ã¢ã¯ã»ã¹ã倿°ã®ã€ã³ã¯ãªã¡ã³ãããã€ããªåŒã®åã«ãã§ãã¯ããã³ãŒããæ¬è³ªçã«æžãæããŸãããã®å®è£
ã¯ãŸã æºåãæŽã£ãŠãããããŸã å€ãã®äœæ¥ãè¡ãããŠããŸããã ãæé»ã®ããããä»ããŠåŒã³åºãããã¡ãœãããšãã©ã¡ãŒã¿ãŒã®åé¡ã¯ãããšãã°ãã«ããŒã®ããã«ãŸã 解決ãããŠããªãããšã«æ°ä»ããŸããã
xml { cars { // cars is a method call on an implicit this: "this".cars(...) car(make:'Renault', model: 'Clio') } }
ãããŸã§ã®ãšãããGroovyã®ã¡ã¿ãªããžã§ã¯ããããã³ã«ã®ã¢ãŒããã¯ãã£ã«ããããã®åé¡ã解決ããæ¹æ³ã¯ãŸã èŠã€ãããŸããã§ãããããã¯ãå¥ã®ã¬ã·ãŒããŒã«åãæ¿ããåã«ã¡ãœãããèŠã€ãããªããšãã«ã¬ã·ãŒããŒãäŸå€ãã¹ããŒãããšããäºå®ã«åºã¥ããŠããŸãã ã€ãŸããããã¯ãå®éã®ã¡ãœããåŒã³åºãã®åã«ã¬ã·ãŒããŒã®ã¿ã€ããç¥ãããšãã§ããªãããšãæå³ããŸãã ãããŠãåŒã³åºããåæ Œããå Žåãæé
ãã§ã...
ãããŠãæè¿ãŸã§ãå®è¡å¯èœã¹ã¯ãªãããèšèªã®åçããããã£ã䜿çšããå Žåããã®åé¡ã«å¯Ÿããæé©ãªè§£æ±ºçããããŸããã§ããã ããããä»ãããèšèªã®ãã€ãããºã ãå°ãç ç²ã«ããæºåãã§ããŠããå Žåã«ãç¶æ³ã倧å¹
ã«æ¹åããæ¹æ³ã説æãããšãã§ãã
åãã§ãã¯
SecureASTCustomizerã®äž»ãªåé¡ã«æ»ããŸããããããã¯æœè±¡çãªæ§æããªãŒã§åäœããç¹å®ã®ã¿ã€ããã¡ãã»ãŒãžã®åä¿¡è
ã«é¢ããæ
å ±ã¯ãããŸããã ããããGroovyã¯Groovy 2ããã³ã³ãã€ã«ã远å ããGroovy 2.1 ã§ã¯åãã§ãã¯çšã®æ¡åŒµæ©èœã远å ããŸããã
åãã§ãã¯ã®æ¡åŒµæ©èœã¯éåžžã«åŒ·åã§ããGroovyDSLéçºè
ã¯ã³ã³ãã€ã©ãŒã®åæšè«ãæ¯æŽããéåžžã¯çºçããªãå Žåã«ã³ã³ãã€ã«ãšã©ãŒãçæããããšãã§ããŸãã ãããã®æ¡åŒµæ©èœã¯ãããšãã°ç¹æ§ãããŒã¯ã¢ãããã³ãã¬ãŒããšã³ãžã³ãå®è£
ããå Žåãªã©ãéçã³ã³ãã€ã©ããµããŒãããããã«Groovyã«ãã£ãŠå
éšçã«äœ¿çšãããŸãã
ããŒãµãŒã®çµæã䜿çšãã代ããã«ãåãã§ãã¯ã¡ã«ããºã ããã®æ
å ±ã«äŸåã§ãããšãããã©ãã§ããããã ããã«ãŒãèšè¿°ããããšããã³ãŒããååŸããŸãã
((Object)System).exit(-1)
åãã§ãã¯ãæå¹ã«ãããšãã³ãŒãã¯ã³ã³ãã€ã«ãããŸããã
1 compilation error: [Static type checking] - Cannot find matching method java.lang.Object#exit(java.lang.Integer). Please check if the declared type is right and if the method exists.
ãã®ããããã®ã³ãŒãã¯ã³ã³ãã€ã«ãããªããªããŸããã ãããŠããã®ã³ãŒããååŸããå ŽåïŒ
def c = System c.exit(-1)
ã芧ã®ãšãããåãã§ãã¯ã«åæ Œããã¡ãœããã«ã©ããããã groovy
ã³ãã³ãã䜿çšããŠå®è¡ãããŸãã
@groovy.transform.TypeChecked
åãã§ãã«ãŒã¯ã exit
ã¡ãœãããSystem
ã¯ã©ã¹ããåŒã³åºãããæå¹ã§ããããšãæ€åºããŸãã ããã¯ããã§ã¯åœ¹ã«ç«ã¡ãŸããã ãããããã®ã³ãŒããåãã§ãã¯ã«åæ Œããå Žåãã³ã³ãã€ã©ãåSystem
ã¬ã·ãŒããŒãžã®åŒã³åºããèªèããããšãç¥ã£ãŠãSystem
ã äžè¬ã«ãã¢ã€ãã¢ã¯ãåãã§ãã¯ã®ããã®å
ç·ä»ãã®åŒã³åºããçŠæ¢ããããšã§ãã
åãã§ãã¯ã®ç°¡åãªæ¡åŒµ
ãµã³ãããã¯ã¹ã®è©³çްã調ã¹ãåã«ãåãã§ãã¯çšã®æšæºæ¡åŒµæ©èœã䜿çšããŠã¹ã¯ãªããããä¿è·ãããŠã¿ãŸãããã ãã®ãããªæ¡åŒµæ©èœã®ç»é²ã¯ç°¡åã§ãã @CompileStatic
泚éïŒãŸãã¯éçã³ã³ãã€ã«ã䜿çšããŠããå Žåã¯@CompileStatic
ã®extensions
ãã©ã¡ãŒã¿ãŒãèšå®ããã ãã§ãã
@TypeChecked(extensions=['SecureExtension1.groovy']) void foo() { def c = System c.exit(-1) } foo()
æ¡åŒµæ©èœã¯ããœãŒã¹ã³ãŒã圢åŒã®ã¯ã©ã¹ãã¹ã§æ€çŽ¢ãããŸãïŒåãã§ãã¯çšã«ããªã³ã³ãã€ã«æžã¿ã®æ¡åŒµæ©èœãäœæã§ããŸããããã®èšäºã§ã¯èæ
®ããŸããïŒã
SecureExtension1.groovy
onMethodSelection { expr, methodNode -> if (methodNode.declaringClass.name=='java.lang.System') { addStaticTypeError("Method call is not allowed!", expr) } }
- åãã§ãã«ãŒãåŒã³åºãã¡ãœãããéžæãããšã
- ã¡ãœãããã¯ã©ã¹
System
å±ããå Žå - ã¿ã€ããã§ãã«ãŒã«ãšã©ãŒãçæãããŸã
å¿
èŠãªã®ã¯ããã ãã§ãã ã³ãŒããå床å®è¡ãããšãã³ã³ãã€ã«ãšã©ãŒã衚瀺ãããŸãïŒ
/home/cchampeau/tmp/securetest.groovy: 6: [Static type checking] - Method call is not allowed! @ line 6, column 3. c.exit(-1) ^ 1 error
ä»åã¯ãåãã§ãã«ãŒã®ãããã§ã c
System
ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãšããŠèªèãããåŒã³åºããçŠæ¢ã§ããŸãã ããã¯éåžžã«åçŽãªäŸã§ãããæ§æã«é¢ããŠã»ãã¥ã¢ASTã«ã¹ã¿ãã€ã¶ãŒã䜿çšããŠå®è¡ã§ãããã¹ãŠã瀺ããã®ã§ã¯ãããŸããã ç§ãã¡ãæžããæ¡åŒµæ©èœã§ã¯ ããã§ãã¯ã¯ããŒãã³ãŒãã£ã³ã°ãããŠããŸãããã«ã¹ã¿ãã€ãºå¯èœã«ããæ¹ãè¯ããããããŸããã ããã§ã¯ãäŸããã£ãšè€éã«ããŸãããã
ã¢ããªã±ãŒã·ã§ã³ãããã¥ã¡ã³ãã®ç¹å®ã®ã¡ããªãã¯ãèšç®ãããŠãŒã¶ãŒãããããã«ã¹ã¿ãã€ãºã§ãããšä»®å®ããŸãã ãã®å ŽåãDSLïŒ
- ïŒå°ãªããšãïŒå€æ°
score
ãæäœããŸã - ãŠãŒã¶ãŒãæ°åŠæŒç®ãå®è¡ã§ããããã«ããŸãïŒ cos ã abs ã...ã¡ãœããã®åŒã³åºããå«ãïŒ
- ä»ã®ãã¹ãŠã®æ¹æ³ãçŠæ¢ããå¿
èŠããããŸã
ãµã³ãã«ãŠãŒã¶ãŒã¹ã¯ãªããïŒ
abs(cos(1+score))
ãã®DSLã¯ç°¡åã«èšå®ã§ããŸãã ããã¯äžã§å®çŸ©ãããã®ã®å€åœ¢ã§ãã
Sandbox.java
CompilerConfiguration conf = new CompilerConfiguration(); ImportCustomizer customizer = new ImportCustomizer(); customizer.addStaticStars("java.lang.Math"); conf.addCompilationCustomizers(customizer); Binding binding = new Binding(); binding.setVariable("score", 2.0d); GroovyShell shell = new GroovyShell(binding,conf); Double userScore = (Double) shell.evaluate("abs(cos(1+score))"); System.out.println("userScore = " + userScore);
import static java.lang.Math.*
ããã¹ãŠã®ã¹ã¯ãªããã«è¿œå ããimportã«ã¹ã¿ãã€ã¶ãŒã远å ãimport static java.lang.Math.*
- ã¹ã¯ãªããã§
score
倿°ã䜿çšå¯èœã«ãã - ã¹ã¯ãªãããå®è¡ãã
ã¹ã¯ãªãããæ¯åè§£æããã³ã³ã³ãã€ã«ãã代ããã«ãã¹ã¯ãªããããã£ãã·ã¥ããæ¹æ³ããããŸãã 詳现ã«ã€ããŠã¯ãããã¥ã¡ã³ããåç
§ããŠãã ããã
ãããã£ãŠãã¹ã¯ãªããã¯æ©èœããŸãããããã«ãŒãæªæã®ããã³ãŒããèµ·åããããšã劚ãããã®ã¯äœããããŸããã åãã§ãã¯ã䜿çšããäºå®ãªã®ã§ã @CompileStatic
倿ã®äœ¿çšããå§ãããŸãã
- ã¹ã¯ãªããã§åãã§ãã¯ãæå¹ã«ããåãã§ãã¯ã®æ¡åŒµæ©èœã®ãããã§è¿œå ã®ãã§ãã¯ãå®è¡ã§ããŸã
- ã¹ã¯ãªããã®ããã©ãŒãã³ã¹ãæ¹åãã
æé»çã«@CompileStatic
ã¢ãããŒã·ã§ã³ãã¹ã¯ãªããã«è¿œå ããã®ã¯éåžžã«ç°¡åã§ãã ã³ã³ãã€ã©ãŒæ§æãæŽæ°ããã ãã§ãã
ASTTransformationCustomizer astcz = new ASTTransformationCustomizer(CompileStatic.class); conf.addCompilationCustomizers(astcz);
ã¹ã¯ãªãããå床å®è¡ããããšãããšãã³ã³ãã€ã«ãšã©ãŒã衚瀺ãããŸãã
Script1.groovy: 1: [Static type checking] - The variable [score] is undeclared. @ line 1, column 11. abs(cos(1+score)) ^ Script1.groovy: 1: [Static type checking] - Cannot find matching method int#plus(java.lang.Object). Please check if the declared type is right and if the method exists. @ line 1, column 9. abs(cos(1+score)) ^ 2 errors
ã©ããã ã³ã³ãã€ã©ã®èгç¹ããã¹ã¯ãªãããèªããšã倿°ãã¹ã³ã¢ãã«ã€ããŠäœãç¥ããªãããšãæããã«ãªããŸãã ããããéçºè
ãšããŠã ããªãã¯ãããdouble
倿°ã§ããããšãç¥ã£ãŠããŸãããã³ã³ãã€ã©ã¯ãããåºåã§ããŸããã ãã®ããã«ãåãã§ãã¯çšã®æ¡åŒµæ©èœãäœæãããŸããã³ã³ãã€ã©ãŒã«è¿œå æ
å ±ãäžãããšãã³ã³ãã€ã«ã¯æ£åžžã«æ©èœããŸãã ãã®å Žåã score
倿°ã®ã¿ã€ããdouble
ããšã瀺ãå¿
èŠããããŸãã
ãããã£ãŠã @CompileStatic
ã¢ãããŒã·ã§ã³ã®@CompileStatic
æ¹æ³ããããã«å€æŽã§ããŸãã
ASTTransformationCustomizer astcz = new ASTTransformationCustomizer( singletonMap("extensions", singletonList("SecureExtension2.groovy")), CompileStatic.class);
ããã¯ã @CompileStatic(extensions=['SecureExtension2.groovy'])
泚éãããã³ãŒããããšãã¥ã¬ãŒããããŸãã ãã¡ããã score
倿°ãèªèããæ¡åŒµæ©èœãäœæããå¿
èŠããããŸãã
SecureExtension2.groovy
unresolvedVariable { var -> if (var.name=='score') { return makeDynamic(var, double_TYPE) } }
- åãã§ãã«ãŒã倿°ãå®çŸ©ã§ããªãå Žå
- 倿°åã
score
- ã³ã³ãã€ã©ãŒã«
double
åã®å€æ°ãåçã«å®çŸ©ãããŸã
åãã§ãã¯çšã®DSLæ¡åŒµæ©èœã®è©³çްãªèª¬æã¯ãããã¥ã¡ã³ãã®ãã®ã»ã¯ã·ã§ã³ã«ãããŸãããè€åã³ã³ãã€ã«ã¢ãŒãã®äŸããããŸããã³ã³ãã€ã©ã¯score
倿°ã決å®ã§ããŸããã ããªãã¯ãDSLéçºè
ãšããŠã倿°ãå®éã«ãã®åmakeDynamic
ããšãç¥ã£ãŠããã®ã§ã makeDynamic
ãžã®makeDynamic
ããã«ãããŸãïŒãOKãå¿é
ããªãã§ãç§ã¯äœãããŠããã®ãç¥ã£ãŠããŸãããã®å€æ°ã¯double
åã 以äžã§ãïŒ
æåã«å®æãããå®å
šãªãæ¡åŒµæ©èœ
ããã§ã¯ããã¹ãŠããŸãšããŸãããã System
ã¯ã©ã¹ã®ã¡ãœããã®åŒã³åºãã鲿¢ããåãã§ãã¯æ¡åŒµæ©èœãšã score
倿°ãå®çŸ©ããå¥ã®åãã§ãã¯æ¡åŒµæ©èœãäœæããŸããã ãããã£ãŠãããããæ¥ç¶ãããšãåãã§ãã¯çšã®æåã®å®å
šãªæ¡åŒµæ©èœãåŸãããŸãã
SecureExtension3.groovy
Javaã¯ã©ã¹ã®æ§æãæŽæ°ããŠãåãã§ãã¯ã«æ°ããæ¡åŒµæ©èœã䜿çšããããšãå¿ããªãã§ãã ããã
ASTTransformationCustomizer astcz = new ASTTransformationCustomizer( singletonMap("extensions", singletonList("SecureExtension3.groovy")), CompileStatic.class);
ã³ãŒããå床å®è¡ããŸã-ããã§ãåäœããŸãã ä»ããã詊ããŠãã ããïŒ
abs(cos(1+score)) System.exit(-1)
ã¹ã¯ãªããã®ã³ã³ãã€ã«ã¯ãšã©ãŒã§ã¯ã©ãã·ã¥ããŸãïŒ
Script1.groovy: 1: [Static type checking] - Method call is not allowed! @ line 1, column 19. abs(cos(1+score));System.exit(-1) ^ 1 error
ããã§ãšãããããŸããããªãã¯æªæã®ããã³ãŒãã®å®è¡ãé²ãæåã®åãã§ãã¯æ¡åŒµæ©èœãæžããŸããïŒ
æ¡åŒµèšå®ã®åŒ·å
ãããã£ãŠããã¹ãŠãé 調ã«é²ãã§ããã System
ã¯ã©ã¹ã®ã¡ãœããã®åŒã³åºããçŠæ¢ã§ããŸãããæ°ããè匱æ§ãããã«çºèŠãããããã§ãããæªæã®ããã³ãŒãã®èµ·åãé²ãå¿
èŠããããŸãã ãã®ãããæ¡åŒµæ©èœã®ãã¹ãŠãããŒãã³ãŒãã£ã³ã°ãã代ããã«ãæ¡åŒµæ©èœããŠãããŒãµã«ã§ã«ã¹ã¿ãã€ãºå¯èœã«ããããšããŸãã åãã§ãã¯ã®ããã«æ¡åŒµæ©èœã«ã³ã³ããã¹ããæž¡ãçŽæ¥çãªæ¹æ³ããªããããããã¯ããããæãå°é£ã§ãã ãããã£ãŠããã®èãæ¹ã¯ãã¹ã¬ããããŒã«ã«å€æ°ïŒæ²ç·ã¡ãœãããã¯ãïŒã䜿çšããŠæ§æããŒã¿ãåãã§ãã«ãŒã«æž¡ãããšã«åºã¥ããŠããŸãã
ãŸãã倿°ã®ãªã¹ããã«ã¹ã¿ãã€ãºå¯èœã«ããŸãã Javaã³ãŒãã¯æ¬¡ã®ããã«ãªããŸãã
Sandbox.java
public class Sandbox { public static final String VAR_TYPES = "sandboxing.variable.types"; public static final ThreadLocal<Map<String, Object>> COMPILE_OPTIONS = new ThreadLocal<>(); public static void main(String[] args) { CompilerConfiguration conf = new CompilerConfiguration(); ImportCustomizer customizer = new ImportCustomizer(); customizer.addStaticStars("java.lang.Math"); ASTTransformationCustomizer astcz = new ASTTransformationCustomizer( singletonMap("extensions", singletonList("SecureExtension4.groovy")), CompileStatic.class); conf.addCompilationCustomizers(astcz); conf.addCompilationCustomizers(customizer); Binding binding = new Binding(); binding.setVariable("score", 2.0d); try { Map<String,ClassNode> variableTypes = new HashMap<String, ClassNode>(); variableTypes.put("score", ClassHelper.double_TYPE); Map<String,Object> options = new HashMap<String, Object>(); options.put(VAR_TYPES, variableTypes); COMPILE_OPTIONS.set(options); GroovyShell shell = new GroovyShell(binding, conf); Double userScore = (Double) shell.evaluate("abs(cos(1+score));System.exit(-1)"); System.out.println("userScore = " + userScore); } finally { COMPILE_OPTIONS.remove(); } } }
ThreadLocal
,- â
SecureExtension4.groovy
variableTypes
â â â âscore
options
â- "variable types" VAR_TYPES
- thread local
- , , thread local
:
import static Sandbox.* def typesOfVariables = COMPILE_OPTIONS.get()[VAR_TYPES] unresolvedVariable { var -> if (typesOfVariables[var.name]) { return makeDynamic(var, typesOfVariables[var.name]) } }
- thread local
- , ,
- type checker
thread local, , type checker . , unresolvedVariable
, , , type checker, . , . !
. , .
. , . , , . , System.exit
, :
java.lang.System#exit(int)
, Java, :
public class Sandbox { public static final String WHITELIST_PATTERNS = "sandboxing.whitelist.patterns";
java.lang.Math
:
import groovy.transform.CompileStatic import org.codehaus.groovy.ast.ClassNode import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.Parameter import org.codehaus.groovy.transform.stc.ExtensionMethodNode import static Sandbox.* @CompileStatic private static String prettyPrint(ClassNode node) { node.isArray()?"${prettyPrint(node.componentType)}[]":node.toString(false) } @CompileStatic private static String toMethodDescriptor(MethodNode node) { if (node instanceof ExtensionMethodNode) { return toMethodDescriptor(node.extensionMethodNode) } def sb = new StringBuilder() sb.append(node.declaringClass.toString(false)) sb.append("#") sb.append(node.name) sb.append('(') sb.append(node.parameters.collect { Parameter it -> prettyPrint(it.originType) }.join(',')) sb.append(')') sb } def typesOfVariables = COMPILE_OPTIONS.get()[VAR_TYPES] def whiteList = COMPILE_OPTIONS.get()[WHITELIST_PATTERNS] onMethodSelection { expr, MethodNode methodNode -> def descr = toMethodDescriptor(methodNode) if (!whiteList.any { descr =~ it }) { addStaticTypeError("You tried to call a method which is not allowed, what did you expect?: $descr", expr) } } unresolvedVariable { var -> if (typesOfVariables[var.name]) { return makeDynamic(var, typesOfVariables[var.name]) } }
MethodNode
- thread local
- ,
, :
Script1.groovy: 1: [Static type checking] - You tried to call a method which is not allowed, what did you expect?: java.lang.System#exit(int) @ line 1, column 19. abs(cos(1+score));System.exit(-1) ^ 1 error
, ! , , . , ! , , . , ( foo.text
, foo.getText()
).
ãã¹ãŠããŸãšãã
, type checker' "property selection", , . , , . , , â . .
SandboxingTypeCheckingExtension.groovy
import groovy.transform.CompileStatic import org.codehaus.groovy.ast.ClassCodeVisitorSupport import org.codehaus.groovy.ast.ClassHelper import org.codehaus.groovy.ast.ClassNode import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.Parameter import org.codehaus.groovy.ast.expr.PropertyExpression import org.codehaus.groovy.control.SourceUnit import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys import org.codehaus.groovy.transform.stc.ExtensionMethodNode import org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport import static Sandbox.* class SandboxingTypeCheckingExtension extends GroovyTypeCheckingExtensionSupport.TypeCheckingDSL { @CompileStatic private static String prettyPrint(ClassNode node) { node.isArray()?"${prettyPrint(node.componentType)}[]":node.toString(false) } @CompileStatic private static String toMethodDescriptor(MethodNode node) { if (node instanceof ExtensionMethodNode) { return toMethodDescriptor(node.extensionMethodNode) } def sb = new StringBuilder() sb.append(node.declaringClass.toString(false)) sb.append("#") sb.append(node.name) sb.append('(') sb.append(node.parameters.collect { Parameter it -> prettyPrint(it.originType) }.join(',')) sb.append(')') sb } @Override Object run() {
ãããã«
Groovy JVM. , . , , , . , Groovy, sandboxing' (, , ).
, , . , . , , .
, sandboxing', , â SecureASTCustomizer
. , , : secure AST customizer , (, ), ( , ).
, : , , . Groovy . Groovy, , - pull request, - !