immunio 0.15.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +234 -0
  3. data/README.md +147 -0
  4. data/bin/immunio +5 -0
  5. data/lib/immunio.rb +29 -0
  6. data/lib/immunio/agent.rb +260 -0
  7. data/lib/immunio/authentication.rb +96 -0
  8. data/lib/immunio/blocked_app.rb +38 -0
  9. data/lib/immunio/channel.rb +432 -0
  10. data/lib/immunio/cli.rb +39 -0
  11. data/lib/immunio/context.rb +114 -0
  12. data/lib/immunio/errors.rb +43 -0
  13. data/lib/immunio/immunio_ca.crt +45 -0
  14. data/lib/immunio/logger.rb +87 -0
  15. data/lib/immunio/plugins/action_dispatch.rb +45 -0
  16. data/lib/immunio/plugins/action_view.rb +431 -0
  17. data/lib/immunio/plugins/active_record.rb +707 -0
  18. data/lib/immunio/plugins/active_record_relation.rb +370 -0
  19. data/lib/immunio/plugins/authlogic.rb +80 -0
  20. data/lib/immunio/plugins/csrf.rb +24 -0
  21. data/lib/immunio/plugins/devise.rb +40 -0
  22. data/lib/immunio/plugins/environment_reporter.rb +69 -0
  23. data/lib/immunio/plugins/eval.rb +51 -0
  24. data/lib/immunio/plugins/exception_handler.rb +55 -0
  25. data/lib/immunio/plugins/gems_tracker.rb +5 -0
  26. data/lib/immunio/plugins/haml.rb +36 -0
  27. data/lib/immunio/plugins/http_finisher.rb +50 -0
  28. data/lib/immunio/plugins/http_tracker.rb +203 -0
  29. data/lib/immunio/plugins/io.rb +96 -0
  30. data/lib/immunio/plugins/redirect.rb +42 -0
  31. data/lib/immunio/plugins/warden.rb +66 -0
  32. data/lib/immunio/processor.rb +234 -0
  33. data/lib/immunio/rails.rb +26 -0
  34. data/lib/immunio/request.rb +139 -0
  35. data/lib/immunio/rufus_lua_ext/ref.rb +27 -0
  36. data/lib/immunio/rufus_lua_ext/state.rb +157 -0
  37. data/lib/immunio/rufus_lua_ext/table.rb +137 -0
  38. data/lib/immunio/rufus_lua_ext/utils.rb +13 -0
  39. data/lib/immunio/version.rb +5 -0
  40. data/lib/immunio/vm.rb +291 -0
  41. data/lua-hooks/ext/all.c +78 -0
  42. data/lua-hooks/ext/bitop/README +22 -0
  43. data/lua-hooks/ext/bitop/bit.c +189 -0
  44. data/lua-hooks/ext/extconf.rb +38 -0
  45. data/lua-hooks/ext/libinjection/COPYING +37 -0
  46. data/lua-hooks/ext/libinjection/libinjection.h +65 -0
  47. data/lua-hooks/ext/libinjection/libinjection_html5.c +847 -0
  48. data/lua-hooks/ext/libinjection/libinjection_html5.h +54 -0
  49. data/lua-hooks/ext/libinjection/libinjection_sqli.c +2301 -0
  50. data/lua-hooks/ext/libinjection/libinjection_sqli.h +295 -0
  51. data/lua-hooks/ext/libinjection/libinjection_sqli_data.h +9349 -0
  52. data/lua-hooks/ext/libinjection/libinjection_xss.c +531 -0
  53. data/lua-hooks/ext/libinjection/libinjection_xss.h +21 -0
  54. data/lua-hooks/ext/libinjection/lualib.c +109 -0
  55. data/lua-hooks/ext/lpeg/HISTORY +90 -0
  56. data/lua-hooks/ext/lpeg/lpcap.c +537 -0
  57. data/lua-hooks/ext/lpeg/lpcap.h +43 -0
  58. data/lua-hooks/ext/lpeg/lpcode.c +986 -0
  59. data/lua-hooks/ext/lpeg/lpcode.h +34 -0
  60. data/lua-hooks/ext/lpeg/lpeg-128.gif +0 -0
  61. data/lua-hooks/ext/lpeg/lpeg.html +1429 -0
  62. data/lua-hooks/ext/lpeg/lpprint.c +244 -0
  63. data/lua-hooks/ext/lpeg/lpprint.h +35 -0
  64. data/lua-hooks/ext/lpeg/lptree.c +1238 -0
  65. data/lua-hooks/ext/lpeg/lptree.h +77 -0
  66. data/lua-hooks/ext/lpeg/lptypes.h +149 -0
  67. data/lua-hooks/ext/lpeg/lpvm.c +355 -0
  68. data/lua-hooks/ext/lpeg/lpvm.h +58 -0
  69. data/lua-hooks/ext/lpeg/makefile +55 -0
  70. data/lua-hooks/ext/lpeg/re.html +498 -0
  71. data/lua-hooks/ext/lpeg/test.lua +1409 -0
  72. data/lua-hooks/ext/lua-cmsgpack/CMakeLists.txt +45 -0
  73. data/lua-hooks/ext/lua-cmsgpack/README.md +115 -0
  74. data/lua-hooks/ext/lua-cmsgpack/lua_cmsgpack.c +957 -0
  75. data/lua-hooks/ext/lua-cmsgpack/test.lua +570 -0
  76. data/lua-hooks/ext/lua-snapshot/LICENSE +7 -0
  77. data/lua-hooks/ext/lua-snapshot/Makefile +12 -0
  78. data/lua-hooks/ext/lua-snapshot/README.md +18 -0
  79. data/lua-hooks/ext/lua-snapshot/dump.lua +15 -0
  80. data/lua-hooks/ext/lua-snapshot/snapshot.c +455 -0
  81. data/lua-hooks/ext/lua/COPYRIGHT +34 -0
  82. data/lua-hooks/ext/lua/lapi.c +1087 -0
  83. data/lua-hooks/ext/lua/lapi.h +16 -0
  84. data/lua-hooks/ext/lua/lauxlib.c +652 -0
  85. data/lua-hooks/ext/lua/lauxlib.h +174 -0
  86. data/lua-hooks/ext/lua/lbaselib.c +659 -0
  87. data/lua-hooks/ext/lua/lcode.c +831 -0
  88. data/lua-hooks/ext/lua/lcode.h +76 -0
  89. data/lua-hooks/ext/lua/ldblib.c +398 -0
  90. data/lua-hooks/ext/lua/ldebug.c +638 -0
  91. data/lua-hooks/ext/lua/ldebug.h +33 -0
  92. data/lua-hooks/ext/lua/ldo.c +519 -0
  93. data/lua-hooks/ext/lua/ldo.h +57 -0
  94. data/lua-hooks/ext/lua/ldump.c +164 -0
  95. data/lua-hooks/ext/lua/lfunc.c +174 -0
  96. data/lua-hooks/ext/lua/lfunc.h +34 -0
  97. data/lua-hooks/ext/lua/lgc.c +710 -0
  98. data/lua-hooks/ext/lua/lgc.h +110 -0
  99. data/lua-hooks/ext/lua/linit.c +38 -0
  100. data/lua-hooks/ext/lua/liolib.c +556 -0
  101. data/lua-hooks/ext/lua/llex.c +463 -0
  102. data/lua-hooks/ext/lua/llex.h +81 -0
  103. data/lua-hooks/ext/lua/llimits.h +128 -0
  104. data/lua-hooks/ext/lua/lmathlib.c +263 -0
  105. data/lua-hooks/ext/lua/lmem.c +86 -0
  106. data/lua-hooks/ext/lua/lmem.h +49 -0
  107. data/lua-hooks/ext/lua/loadlib.c +705 -0
  108. data/lua-hooks/ext/lua/loadlib_rel.c +760 -0
  109. data/lua-hooks/ext/lua/lobject.c +214 -0
  110. data/lua-hooks/ext/lua/lobject.h +381 -0
  111. data/lua-hooks/ext/lua/lopcodes.c +102 -0
  112. data/lua-hooks/ext/lua/lopcodes.h +268 -0
  113. data/lua-hooks/ext/lua/loslib.c +243 -0
  114. data/lua-hooks/ext/lua/lparser.c +1339 -0
  115. data/lua-hooks/ext/lua/lparser.h +82 -0
  116. data/lua-hooks/ext/lua/lstate.c +214 -0
  117. data/lua-hooks/ext/lua/lstate.h +169 -0
  118. data/lua-hooks/ext/lua/lstring.c +111 -0
  119. data/lua-hooks/ext/lua/lstring.h +31 -0
  120. data/lua-hooks/ext/lua/lstrlib.c +871 -0
  121. data/lua-hooks/ext/lua/ltable.c +588 -0
  122. data/lua-hooks/ext/lua/ltable.h +40 -0
  123. data/lua-hooks/ext/lua/ltablib.c +287 -0
  124. data/lua-hooks/ext/lua/ltm.c +75 -0
  125. data/lua-hooks/ext/lua/ltm.h +54 -0
  126. data/lua-hooks/ext/lua/lua.c +392 -0
  127. data/lua-hooks/ext/lua/lua.def +131 -0
  128. data/lua-hooks/ext/lua/lua.h +388 -0
  129. data/lua-hooks/ext/lua/lua.rc +28 -0
  130. data/lua-hooks/ext/lua/lua_dll.rc +26 -0
  131. data/lua-hooks/ext/lua/luac.c +200 -0
  132. data/lua-hooks/ext/lua/luac.rc +1 -0
  133. data/lua-hooks/ext/lua/luaconf.h +763 -0
  134. data/lua-hooks/ext/lua/luaconf.h.in +724 -0
  135. data/lua-hooks/ext/lua/luaconf.h.orig +763 -0
  136. data/lua-hooks/ext/lua/lualib.h +53 -0
  137. data/lua-hooks/ext/lua/lundump.c +227 -0
  138. data/lua-hooks/ext/lua/lundump.h +36 -0
  139. data/lua-hooks/ext/lua/lvm.c +767 -0
  140. data/lua-hooks/ext/lua/lvm.h +36 -0
  141. data/lua-hooks/ext/lua/lzio.c +82 -0
  142. data/lua-hooks/ext/lua/lzio.h +67 -0
  143. data/lua-hooks/ext/lua/print.c +227 -0
  144. data/lua-hooks/ext/luautf8/README.md +152 -0
  145. data/lua-hooks/ext/luautf8/lutf8lib.c +1274 -0
  146. data/lua-hooks/ext/luautf8/unidata.h +3064 -0
  147. data/lua-hooks/lib/boot.lua +254 -0
  148. data/lua-hooks/lib/encode.lua +4 -0
  149. data/lua-hooks/lib/lexers/LICENSE +21 -0
  150. data/lua-hooks/lib/lexers/bash.lua +134 -0
  151. data/lua-hooks/lib/lexers/bash_dqstr.lua +62 -0
  152. data/lua-hooks/lib/lexers/css.lua +216 -0
  153. data/lua-hooks/lib/lexers/html.lua +106 -0
  154. data/lua-hooks/lib/lexers/javascript.lua +68 -0
  155. data/lua-hooks/lib/lexers/lexer.lua +1575 -0
  156. data/lua-hooks/lib/lexers/markers.lua +33 -0
  157. metadata +308 -0
@@ -0,0 +1,21 @@
1
+ #ifndef LIBINJECTION_XSS
2
+ #define LIBINJECTION_XSS
3
+
4
+ #ifdef __cplusplus
5
+ extern "C" {
6
+ #endif
7
+
8
+ /**
9
+ * HEY THIS ISN'T DONE
10
+ */
11
+
12
+ /* pull in size_t */
13
+
14
+ #include <string.h>
15
+
16
+ int libinjection_is_xss(const char* s, size_t len, int flags);
17
+
18
+ #ifdef __cplusplus
19
+ }
20
+ #endif
21
+ #endif
@@ -0,0 +1,109 @@
1
+ #define LUA_LIB
2
+ #include "lua/lua.h"
3
+ #include "lua/lauxlib.h"
4
+
5
+ #include "libinjection.h"
6
+ #include "libinjection_sqli.h"
7
+
8
+ #define MAX_FINGERPRINT_SIZE 4096
9
+
10
+ int sqli(lua_State *L) {
11
+ sfilter state;
12
+
13
+ size_t slen = 0;
14
+ const char *input = luaL_checklstring(L, 1, &slen);
15
+
16
+ libinjection_sqli_init(&state, input, slen, FLAG_NONE);
17
+ int issqli = libinjection_is_sqli(&state);
18
+
19
+ lua_pushboolean(L, issqli);
20
+
21
+ return 1;
22
+ }
23
+
24
+ int fingerprint(lua_State *L) {
25
+ char result[MAX_FINGERPRINT_SIZE + 1];
26
+ struct libinjection_sqli_state state;
27
+ size_t slen = 0;
28
+ const char *input = luaL_checklstring(L, 1, &slen);
29
+
30
+ libinjection_sqli_init(&state, input, slen, 0);
31
+
32
+ // This is some ugly code, but that's how libinjection works...
33
+ int fp_idx = 0;
34
+ while (fp_idx < (sizeof(result) - 1) && state.pos < state.slen) {
35
+ libinjection_sqli_tokenize(&state);
36
+ result[fp_idx++] = state.tokenvec[0].type;
37
+ }
38
+ result[fp_idx] = '\0';
39
+
40
+ lua_pushstring(L, result);
41
+
42
+ return 1;
43
+ }
44
+
45
+ int xss(lua_State *L) {
46
+ size_t slen = 0;
47
+ const char *input = luaL_checklstring(L, 1, &slen);
48
+
49
+ int is_xss = libinjection_xss(input, slen);
50
+
51
+ lua_pushboolean(L, is_xss);
52
+ return 1;
53
+ }
54
+
55
+ /*
56
+ * Tokenize the SQL into an array where each element
57
+ * is a table with a type and value. For example:
58
+ * {type: "E", value: "SELECT"}
59
+ */
60
+ int sqli_tokenize(lua_State *L) {
61
+ int token_cnt = 1;
62
+ size_t slen = 0;
63
+ const char *input = luaL_checklstring(L, 1, &slen);
64
+ struct libinjection_sqli_state state;
65
+ int var_symbol_count;
66
+
67
+ libinjection_sqli_init(&state, input, slen, 0);
68
+
69
+ lua_newtable(L); /* Tokens array */
70
+ while (state.pos < state.slen) {
71
+ libinjection_sqli_tokenize(&state);
72
+
73
+ lua_newtable(L); /* Inner token table */
74
+
75
+ /* Token.type = type */
76
+ lua_pushlstring(L, &state.tokenvec[0].type, 1);
77
+ lua_setfield(L, -2, "type");
78
+
79
+ /* Token.var_symbol_count = count for variable tokens */
80
+ if (state.tokenvec[0].type == 'v') {
81
+ lua_pushinteger(L, state.tokenvec[0].count);
82
+ lua_setfield(L, -2, "var_symbol_count");
83
+ }
84
+
85
+ /* Token.value = value */
86
+ lua_pushlstring(L, &input[state.tokenvec[0].pos],
87
+ state.tokenvec[0].len);
88
+ lua_setfield(L, -2, "value");
89
+
90
+ /* Tokens.append(Token) */
91
+ lua_pushinteger(L, token_cnt++);
92
+ lua_insert(L, -2); /* [..., token, index] --> [..., index, token] */
93
+ lua_settable(L, -3);
94
+ }
95
+ return 1;
96
+ }
97
+
98
+ static const luaL_Reg libinjection[] = {
99
+ {"sqli", sqli},
100
+ {"fingerprint", fingerprint},
101
+ {"xss", xss},
102
+ {"sqli_tokenize", sqli_tokenize},
103
+ {NULL, NULL}
104
+ };
105
+
106
+ int luaopen_libinjection(lua_State *L) {
107
+ luaL_register(L, "libinjection", libinjection);
108
+ return 1;
109
+ }
@@ -0,0 +1,90 @@
1
+ HISTORY for LPeg 0.12
2
+
3
+ * Changes from version 0.11 to 0.12
4
+ ---------------------------------
5
+ + no "unsigned short" limit for pattern sizes
6
+ + mathtime captures considered nullable
7
+ + some bugs fixed
8
+
9
+ * Changes from version 0.10 to 0.11
10
+ -------------------------------
11
+ + complete reimplementation of the code generator
12
+ + new syntax for table captures
13
+ + new functions in module 're'
14
+ + other small improvements
15
+
16
+ * Changes from version 0.9 to 0.10
17
+ -------------------------------
18
+ + backtrack stack has configurable size
19
+ + better error messages
20
+ + Notation for non-terminals in 're' back to A instead o <A>
21
+ + experimental look-behind pattern
22
+ + support for external extensions
23
+ + works with Lua 5.2
24
+ + consumes less C stack
25
+
26
+ - "and" predicates do not keep captures
27
+
28
+ * Changes from version 0.8 to 0.9
29
+ -------------------------------
30
+ + The accumulator capture was replaced by a fold capture;
31
+ programs that used the old 'lpeg.Ca' will need small changes.
32
+ + Some support for character classes from old C locales.
33
+ + A new named-group capture.
34
+
35
+ * Changes from version 0.7 to 0.8
36
+ -------------------------------
37
+ + New "match-time" capture.
38
+ + New "argument capture" that allows passing arguments into the pattern.
39
+ + Better documentation for 're'.
40
+ + Several small improvements for 're'.
41
+ + The 're' module has an incompatibility with previous versions:
42
+ now, any use of a non-terminal must be enclosed in angle brackets
43
+ (like <B>).
44
+
45
+ * Changes from version 0.6 to 0.7
46
+ -------------------------------
47
+ + Several improvements in module 're':
48
+ - better documentation;
49
+ - support for most captures (all but accumulator);
50
+ - limited repetitions p{n,m}.
51
+ + Small improvements in efficiency.
52
+ + Several small bugs corrected (special thanks to Hans Hagen
53
+ and Taco Hoekwater).
54
+
55
+ * Changes from version 0.5 to 0.6
56
+ -------------------------------
57
+ + Support for non-numeric indices in grammars.
58
+ + Some bug fixes (thanks to the luatex team).
59
+ + Some new optimizations; (thanks to Mike Pall).
60
+ + A new page layout (thanks to Andre Carregal).
61
+ + Minimal documentation for module 're'.
62
+
63
+ * Changes from version 0.4 to 0.5
64
+ -------------------------------
65
+ + Several optimizations.
66
+ + lpeg.P now accepts booleans.
67
+ + Some new examples.
68
+ + A proper license.
69
+ + Several small improvements.
70
+
71
+ * Changes from version 0.3 to 0.4
72
+ -------------------------------
73
+ + Static check for loops in repetitions and grammars.
74
+ + Removed label option in captures.
75
+ + The implementation of captures uses less memory.
76
+
77
+ * Changes from version 0.2 to 0.3
78
+ -------------------------------
79
+ + User-defined patterns in Lua.
80
+ + Several new captures.
81
+
82
+ * Changes from version 0.1 to 0.2
83
+ -------------------------------
84
+ + Several small corrections.
85
+ + Handles embedded zeros like any other character.
86
+ + Capture "name" can be any Lua value.
87
+ + Unlimited number of captures.
88
+ + Match gets an optional initial position.
89
+
90
+ (end of HISTORY)
@@ -0,0 +1,537 @@
1
+ /*
2
+ ** $Id: lpcap.c,v 1.5 2014/12/12 16:58:47 roberto Exp $
3
+ ** Copyright 2007, Lua.org & PUC-Rio (see 'lpeg.html' for license)
4
+ */
5
+
6
+ #include "../lua/lua.h"
7
+ #include "../lua/lauxlib.h"
8
+
9
+ #include "lpcap.h"
10
+ #include "lptypes.h"
11
+
12
+
13
+ #define captype(cap) ((cap)->kind)
14
+
15
+ #define isclosecap(cap) (captype(cap) == Cclose)
16
+
17
+ #define closeaddr(c) ((c)->s + (c)->siz - 1)
18
+
19
+ #define isfullcap(cap) ((cap)->siz != 0)
20
+
21
+ #define getfromktable(cs,v) lua_rawgeti((cs)->L, ktableidx((cs)->ptop), v)
22
+
23
+ #define pushluaval(cs) getfromktable(cs, (cs)->cap->idx)
24
+
25
+
26
+
27
+ /*
28
+ ** Put at the cache for Lua values the value indexed by 'v' in ktable
29
+ ** of the running pattern (if it is not there yet); returns its index.
30
+ */
31
+ static int updatecache (CapState *cs, int v) {
32
+ int idx = cs->ptop + 1; /* stack index of cache for Lua values */
33
+ if (v != cs->valuecached) { /* not there? */
34
+ getfromktable(cs, v); /* get value from 'ktable' */
35
+ lua_replace(cs->L, idx); /* put it at reserved stack position */
36
+ cs->valuecached = v; /* keep track of what is there */
37
+ }
38
+ return idx;
39
+ }
40
+
41
+
42
+ static int pushcapture (CapState *cs);
43
+
44
+
45
+ /*
46
+ ** Goes back in a list of captures looking for an open capture
47
+ ** corresponding to a close
48
+ */
49
+ static Capture *findopen (Capture *cap) {
50
+ int n = 0; /* number of closes waiting an open */
51
+ for (;;) {
52
+ cap--;
53
+ if (isclosecap(cap)) n++; /* one more open to skip */
54
+ else if (!isfullcap(cap))
55
+ if (n-- == 0) return cap;
56
+ }
57
+ }
58
+
59
+
60
+ /*
61
+ ** Go to the next capture
62
+ */
63
+ static void nextcap (CapState *cs) {
64
+ Capture *cap = cs->cap;
65
+ if (!isfullcap(cap)) { /* not a single capture? */
66
+ int n = 0; /* number of opens waiting a close */
67
+ for (;;) { /* look for corresponding close */
68
+ cap++;
69
+ if (isclosecap(cap)) {
70
+ if (n-- == 0) break;
71
+ }
72
+ else if (!isfullcap(cap)) n++;
73
+ }
74
+ }
75
+ cs->cap = cap + 1; /* + 1 to skip last close (or entire single capture) */
76
+ }
77
+
78
+
79
+ /*
80
+ ** Push on the Lua stack all values generated by nested captures inside
81
+ ** the current capture. Returns number of values pushed. 'addextra'
82
+ ** makes it push the entire match after all captured values. The
83
+ ** entire match is pushed also if there are no other nested values,
84
+ ** so the function never returns zero.
85
+ */
86
+ static int pushnestedvalues (CapState *cs, int addextra) {
87
+ Capture *co = cs->cap;
88
+ if (isfullcap(cs->cap++)) { /* no nested captures? */
89
+ lua_pushlstring(cs->L, co->s, co->siz - 1); /* push whole match */
90
+ return 1; /* that is it */
91
+ }
92
+ else {
93
+ int n = 0;
94
+ while (!isclosecap(cs->cap)) /* repeat for all nested patterns */
95
+ n += pushcapture(cs);
96
+ if (addextra || n == 0) { /* need extra? */
97
+ lua_pushlstring(cs->L, co->s, cs->cap->s - co->s); /* push whole match */
98
+ n++;
99
+ }
100
+ cs->cap++; /* skip close entry */
101
+ return n;
102
+ }
103
+ }
104
+
105
+
106
+ /*
107
+ ** Push only the first value generated by nested captures
108
+ */
109
+ static void pushonenestedvalue (CapState *cs) {
110
+ int n = pushnestedvalues(cs, 0);
111
+ if (n > 1)
112
+ lua_pop(cs->L, n - 1); /* pop extra values */
113
+ }
114
+
115
+
116
+ /*
117
+ ** Try to find a named group capture with the name given at the top of
118
+ ** the stack; goes backward from 'cap'.
119
+ */
120
+ static Capture *findback (CapState *cs, Capture *cap) {
121
+ lua_State *L = cs->L;
122
+ while (cap-- > cs->ocap) { /* repeat until end of list */
123
+ if (isclosecap(cap))
124
+ cap = findopen(cap); /* skip nested captures */
125
+ else if (!isfullcap(cap))
126
+ continue; /* opening an enclosing capture: skip and get previous */
127
+ if (captype(cap) == Cgroup) {
128
+ getfromktable(cs, cap->idx); /* get group name */
129
+ if (lua_equal(L, -2, -1)) { /* right group? */
130
+ lua_pop(L, 2); /* remove reference name and group name */
131
+ return cap;
132
+ }
133
+ else lua_pop(L, 1); /* remove group name */
134
+ }
135
+ }
136
+ luaL_error(L, "back reference '%s' not found", lua_tostring(L, -1));
137
+ return NULL; /* to avoid warnings */
138
+ }
139
+
140
+
141
+ /*
142
+ ** Back-reference capture. Return number of values pushed.
143
+ */
144
+ static int backrefcap (CapState *cs) {
145
+ int n;
146
+ Capture *curr = cs->cap;
147
+ pushluaval(cs); /* reference name */
148
+ cs->cap = findback(cs, curr); /* find corresponding group */
149
+ n = pushnestedvalues(cs, 0); /* push group's values */
150
+ cs->cap = curr + 1;
151
+ return n;
152
+ }
153
+
154
+
155
+ /*
156
+ ** Table capture: creates a new table and populates it with nested
157
+ ** captures.
158
+ */
159
+ static int tablecap (CapState *cs) {
160
+ lua_State *L = cs->L;
161
+ int n = 0;
162
+ lua_newtable(L);
163
+ if (isfullcap(cs->cap++))
164
+ return 1; /* table is empty */
165
+ while (!isclosecap(cs->cap)) {
166
+ if (captype(cs->cap) == Cgroup && cs->cap->idx != 0) { /* named group? */
167
+ pushluaval(cs); /* push group name */
168
+ pushonenestedvalue(cs);
169
+ lua_settable(L, -3);
170
+ }
171
+ else { /* not a named group */
172
+ int i;
173
+ int k = pushcapture(cs);
174
+ for (i = k; i > 0; i--) /* store all values into table */
175
+ lua_rawseti(L, -(i + 1), n + i);
176
+ n += k;
177
+ }
178
+ }
179
+ cs->cap++; /* skip close entry */
180
+ return 1; /* number of values pushed (only the table) */
181
+ }
182
+
183
+
184
+ /*
185
+ ** Table-query capture
186
+ */
187
+ static int querycap (CapState *cs) {
188
+ int idx = cs->cap->idx;
189
+ pushonenestedvalue(cs); /* get nested capture */
190
+ lua_gettable(cs->L, updatecache(cs, idx)); /* query cap. value at table */
191
+ if (!lua_isnil(cs->L, -1))
192
+ return 1;
193
+ else { /* no value */
194
+ lua_pop(cs->L, 1); /* remove nil */
195
+ return 0;
196
+ }
197
+ }
198
+
199
+
200
+ /*
201
+ ** Fold capture
202
+ */
203
+ static int foldcap (CapState *cs) {
204
+ int n;
205
+ lua_State *L = cs->L;
206
+ int idx = cs->cap->idx;
207
+ if (isfullcap(cs->cap++) || /* no nested captures? */
208
+ isclosecap(cs->cap) || /* no nested captures (large subject)? */
209
+ (n = pushcapture(cs)) == 0) /* nested captures with no values? */
210
+ return luaL_error(L, "no initial value for fold capture");
211
+ if (n > 1)
212
+ lua_pop(L, n - 1); /* leave only one result for accumulator */
213
+ while (!isclosecap(cs->cap)) {
214
+ lua_pushvalue(L, updatecache(cs, idx)); /* get folding function */
215
+ lua_insert(L, -2); /* put it before accumulator */
216
+ n = pushcapture(cs); /* get next capture's values */
217
+ lua_call(L, n + 1, 1); /* call folding function */
218
+ }
219
+ cs->cap++; /* skip close entry */
220
+ return 1; /* only accumulator left on the stack */
221
+ }
222
+
223
+
224
+ /*
225
+ ** Function capture
226
+ */
227
+ static int functioncap (CapState *cs) {
228
+ int n;
229
+ int top = lua_gettop(cs->L);
230
+ pushluaval(cs); /* push function */
231
+ n = pushnestedvalues(cs, 0); /* push nested captures */
232
+ lua_call(cs->L, n, LUA_MULTRET); /* call function */
233
+ return lua_gettop(cs->L) - top; /* return function's results */
234
+ }
235
+
236
+
237
+ /*
238
+ ** Select capture
239
+ */
240
+ static int numcap (CapState *cs) {
241
+ int idx = cs->cap->idx; /* value to select */
242
+ if (idx == 0) { /* no values? */
243
+ nextcap(cs); /* skip entire capture */
244
+ return 0; /* no value produced */
245
+ }
246
+ else {
247
+ int n = pushnestedvalues(cs, 0);
248
+ if (n < idx) /* invalid index? */
249
+ return luaL_error(cs->L, "no capture '%d'", idx);
250
+ else {
251
+ lua_pushvalue(cs->L, -(n - idx + 1)); /* get selected capture */
252
+ lua_replace(cs->L, -(n + 1)); /* put it in place of 1st capture */
253
+ lua_pop(cs->L, n - 1); /* remove other captures */
254
+ return 1;
255
+ }
256
+ }
257
+ }
258
+
259
+
260
+ /*
261
+ ** Return the stack index of the first runtime capture in the given
262
+ ** list of captures (or zero if no runtime captures)
263
+ */
264
+ int finddyncap (Capture *cap, Capture *last) {
265
+ for (; cap < last; cap++) {
266
+ if (cap->kind == Cruntime)
267
+ return cap->idx; /* stack position of first capture */
268
+ }
269
+ return 0; /* no dynamic captures in this segment */
270
+ }
271
+
272
+
273
+ /*
274
+ ** Calls a runtime capture. Returns number of captures removed by
275
+ ** the call, including the initial Cgroup. (Captures to be added are
276
+ ** on the Lua stack.)
277
+ */
278
+ int runtimecap (CapState *cs, Capture *close, const char *s, int *rem) {
279
+ int n, id;
280
+ lua_State *L = cs->L;
281
+ int otop = lua_gettop(L);
282
+ Capture *open = findopen(close);
283
+ assert(captype(open) == Cgroup);
284
+ id = finddyncap(open, close); /* get first dynamic capture argument */
285
+ close->kind = Cclose; /* closes the group */
286
+ close->s = s;
287
+ cs->cap = open; cs->valuecached = 0; /* prepare capture state */
288
+ luaL_checkstack(L, 4, "too many runtime captures");
289
+ pushluaval(cs); /* push function to be called */
290
+ lua_pushvalue(L, SUBJIDX); /* push original subject */
291
+ lua_pushinteger(L, s - cs->s + 1); /* push current position */
292
+ n = pushnestedvalues(cs, 0); /* push nested captures */
293
+ lua_call(L, n + 2, LUA_MULTRET); /* call dynamic function */
294
+ if (id > 0) { /* are there old dynamic captures to be removed? */
295
+ int i;
296
+ for (i = id; i <= otop; i++)
297
+ lua_remove(L, id); /* remove old dynamic captures */
298
+ *rem = otop - id + 1; /* total number of dynamic captures removed */
299
+ }
300
+ else
301
+ *rem = 0; /* no dynamic captures removed */
302
+ return close - open; /* number of captures of all kinds removed */
303
+ }
304
+
305
+
306
+ /*
307
+ ** Auxiliary structure for substitution and string captures: keep
308
+ ** information about nested captures for future use, avoiding to push
309
+ ** string results into Lua
310
+ */
311
+ typedef struct StrAux {
312
+ int isstring; /* whether capture is a string */
313
+ union {
314
+ Capture *cp; /* if not a string, respective capture */
315
+ struct { /* if it is a string... */
316
+ const char *s; /* ... starts here */
317
+ const char *e; /* ... ends here */
318
+ } s;
319
+ } u;
320
+ } StrAux;
321
+
322
+ #define MAXSTRCAPS 10
323
+
324
+ /*
325
+ ** Collect values from current capture into array 'cps'. Current
326
+ ** capture must be Cstring (first call) or Csimple (recursive calls).
327
+ ** (In first call, fills %0 with whole match for Cstring.)
328
+ ** Returns number of elements in the array that were filled.
329
+ */
330
+ static int getstrcaps (CapState *cs, StrAux *cps, int n) {
331
+ int k = n++;
332
+ cps[k].isstring = 1; /* get string value */
333
+ cps[k].u.s.s = cs->cap->s; /* starts here */
334
+ if (!isfullcap(cs->cap++)) { /* nested captures? */
335
+ while (!isclosecap(cs->cap)) { /* traverse them */
336
+ if (n >= MAXSTRCAPS) /* too many captures? */
337
+ nextcap(cs); /* skip extra captures (will not need them) */
338
+ else if (captype(cs->cap) == Csimple) /* string? */
339
+ n = getstrcaps(cs, cps, n); /* put info. into array */
340
+ else {
341
+ cps[n].isstring = 0; /* not a string */
342
+ cps[n].u.cp = cs->cap; /* keep original capture */
343
+ nextcap(cs);
344
+ n++;
345
+ }
346
+ }
347
+ cs->cap++; /* skip close */
348
+ }
349
+ cps[k].u.s.e = closeaddr(cs->cap - 1); /* ends here */
350
+ return n;
351
+ }
352
+
353
+
354
+ /*
355
+ ** add next capture value (which should be a string) to buffer 'b'
356
+ */
357
+ static int addonestring (luaL_Buffer *b, CapState *cs, const char *what);
358
+
359
+
360
+ /*
361
+ ** String capture: add result to buffer 'b' (instead of pushing
362
+ ** it into the stack)
363
+ */
364
+ static void stringcap (luaL_Buffer *b, CapState *cs) {
365
+ StrAux cps[MAXSTRCAPS];
366
+ int n;
367
+ size_t len, i;
368
+ const char *fmt; /* format string */
369
+ fmt = lua_tolstring(cs->L, updatecache(cs, cs->cap->idx), &len);
370
+ n = getstrcaps(cs, cps, 0) - 1; /* collect nested captures */
371
+ for (i = 0; i < len; i++) { /* traverse them */
372
+ if (fmt[i] != '%') /* not an escape? */
373
+ luaL_addchar(b, fmt[i]); /* add it to buffer */
374
+ else if (fmt[++i] < '0' || fmt[i] > '9') /* not followed by a digit? */
375
+ luaL_addchar(b, fmt[i]); /* add to buffer */
376
+ else {
377
+ int l = fmt[i] - '0'; /* capture index */
378
+ if (l > n)
379
+ luaL_error(cs->L, "invalid capture index (%d)", l);
380
+ else if (cps[l].isstring)
381
+ luaL_addlstring(b, cps[l].u.s.s, cps[l].u.s.e - cps[l].u.s.s);
382
+ else {
383
+ Capture *curr = cs->cap;
384
+ cs->cap = cps[l].u.cp; /* go back to evaluate that nested capture */
385
+ if (!addonestring(b, cs, "capture"))
386
+ luaL_error(cs->L, "no values in capture index %d", l);
387
+ cs->cap = curr; /* continue from where it stopped */
388
+ }
389
+ }
390
+ }
391
+ }
392
+
393
+
394
+ /*
395
+ ** Substitution capture: add result to buffer 'b'
396
+ */
397
+ static void substcap (luaL_Buffer *b, CapState *cs) {
398
+ const char *curr = cs->cap->s;
399
+ if (isfullcap(cs->cap)) /* no nested captures? */
400
+ luaL_addlstring(b, curr, cs->cap->siz - 1); /* keep original text */
401
+ else {
402
+ cs->cap++; /* skip open entry */
403
+ while (!isclosecap(cs->cap)) { /* traverse nested captures */
404
+ const char *next = cs->cap->s;
405
+ luaL_addlstring(b, curr, next - curr); /* add text up to capture */
406
+ if (addonestring(b, cs, "replacement"))
407
+ curr = closeaddr(cs->cap - 1); /* continue after match */
408
+ else /* no capture value */
409
+ curr = next; /* keep original text in final result */
410
+ }
411
+ luaL_addlstring(b, curr, cs->cap->s - curr); /* add last piece of text */
412
+ }
413
+ cs->cap++; /* go to next capture */
414
+ }
415
+
416
+
417
+ /*
418
+ ** Evaluates a capture and adds its first value to buffer 'b'; returns
419
+ ** whether there was a value
420
+ */
421
+ static int addonestring (luaL_Buffer *b, CapState *cs, const char *what) {
422
+ switch (captype(cs->cap)) {
423
+ case Cstring:
424
+ stringcap(b, cs); /* add capture directly to buffer */
425
+ return 1;
426
+ case Csubst:
427
+ substcap(b, cs); /* add capture directly to buffer */
428
+ return 1;
429
+ default: {
430
+ lua_State *L = cs->L;
431
+ int n = pushcapture(cs);
432
+ if (n > 0) {
433
+ if (n > 1) lua_pop(L, n - 1); /* only one result */
434
+ if (!lua_isstring(L, -1))
435
+ luaL_error(L, "invalid %s value (a %s)", what, luaL_typename(L, -1));
436
+ luaL_addvalue(b);
437
+ }
438
+ return n;
439
+ }
440
+ }
441
+ }
442
+
443
+
444
+ /*
445
+ ** Push all values of the current capture into the stack; returns
446
+ ** number of values pushed
447
+ */
448
+ static int pushcapture (CapState *cs) {
449
+ lua_State *L = cs->L;
450
+ luaL_checkstack(L, 4, "too many captures");
451
+ switch (captype(cs->cap)) {
452
+ case Cposition: {
453
+ lua_pushinteger(L, cs->cap->s - cs->s + 1);
454
+ cs->cap++;
455
+ return 1;
456
+ }
457
+ case Cconst: {
458
+ pushluaval(cs);
459
+ cs->cap++;
460
+ return 1;
461
+ }
462
+ case Carg: {
463
+ int arg = (cs->cap++)->idx;
464
+ if (arg + FIXEDARGS > cs->ptop)
465
+ return luaL_error(L, "reference to absent extra argument #%d", arg);
466
+ lua_pushvalue(L, arg + FIXEDARGS);
467
+ return 1;
468
+ }
469
+ case Csimple: {
470
+ int k = pushnestedvalues(cs, 1);
471
+ lua_insert(L, -k); /* make whole match be first result */
472
+ return k;
473
+ }
474
+ case Cruntime: {
475
+ lua_pushvalue(L, (cs->cap++)->idx); /* value is in the stack */
476
+ return 1;
477
+ }
478
+ case Cstring: {
479
+ luaL_Buffer b;
480
+ luaL_buffinit(L, &b);
481
+ stringcap(&b, cs);
482
+ luaL_pushresult(&b);
483
+ return 1;
484
+ }
485
+ case Csubst: {
486
+ luaL_Buffer b;
487
+ luaL_buffinit(L, &b);
488
+ substcap(&b, cs);
489
+ luaL_pushresult(&b);
490
+ return 1;
491
+ }
492
+ case Cgroup: {
493
+ if (cs->cap->idx == 0) /* anonymous group? */
494
+ return pushnestedvalues(cs, 0); /* add all nested values */
495
+ else { /* named group: add no values */
496
+ nextcap(cs); /* skip capture */
497
+ return 0;
498
+ }
499
+ }
500
+ case Cbackref: return backrefcap(cs);
501
+ case Ctable: return tablecap(cs);
502
+ case Cfunction: return functioncap(cs);
503
+ case Cnum: return numcap(cs);
504
+ case Cquery: return querycap(cs);
505
+ case Cfold: return foldcap(cs);
506
+ default: assert(0); return 0;
507
+ }
508
+ }
509
+
510
+
511
+ /*
512
+ ** Prepare a CapState structure and traverse the entire list of
513
+ ** captures in the stack pushing its results. 's' is the subject
514
+ ** string, 'r' is the final position of the match, and 'ptop'
515
+ ** the index in the stack where some useful values were pushed.
516
+ ** Returns the number of results pushed. (If the list produces no
517
+ ** results, push the final position of the match.)
518
+ */
519
+ int getcaptures (lua_State *L, const char *s, const char *r, int ptop) {
520
+ Capture *capture = (Capture *)lua_touserdata(L, caplistidx(ptop));
521
+ int n = 0;
522
+ if (!isclosecap(capture)) { /* is there any capture? */
523
+ CapState cs;
524
+ cs.ocap = cs.cap = capture; cs.L = L;
525
+ cs.s = s; cs.valuecached = 0; cs.ptop = ptop;
526
+ do { /* collect their values */
527
+ n += pushcapture(&cs);
528
+ } while (!isclosecap(cs.cap));
529
+ }
530
+ if (n == 0) { /* no capture values? */
531
+ lua_pushinteger(L, r - s + 1); /* return only end position */
532
+ n = 1;
533
+ }
534
+ return n;
535
+ }
536
+
537
+