immunio 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+