authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2021-08-07 00:45:36 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2021-08-07 00:45:36 -07:00
log728e82a8626115567eaf898c66f767763c0e6cce
tree1a0b2411dece4c256ce00dcc6ad029d238ebb4b3
parenta135ca3268d90709492056b008b20238da4dccc0

add html entity filtering to prevent tag injection


4 files changed, 25 insertions(+), 1 deletions(-)

src/lib.zig+21-1
...@@ -24,6 +24,7 @@...@@ -24,6 +24,7 @@
2424
25const std = @import("std");25const std = @import("std");
26const range = @import("range").range;26const range = @import("range").range;
27const htmlentities = @import("htmlentities");
2728
28const tokenize = @import("./tokenize.zig");29const tokenize = @import("./tokenize.zig");
29const astgen = @import("./astgen.zig");30const astgen = @import("./astgen.zig");
...@@ -75,7 +76,14 @@ fn do(writer: anytype, comptime value: astgen.Value, data: anytype, ctx: anytype...@@ -75,7 +76,14 @@ fn do(writer: anytype, comptime value: astgen.Value, data: anytype, ctx: anytype
75 const TO = @TypeOf(x);76 const TO = @TypeOf(x);
7677
77 if (comptime std.meta.trait.isZigString(TO)) {78 if (comptime std.meta.trait.isZigString(TO)) {
78 try writer.print("{s}", .{x});79 const s: []const u8 = x;
80 for (s) |c| {
81 if (entityLookupBefore(&[_]u8{c})) |ent| {
82 try writer.writeAll(ent.entity);
83 } else {
84 try writer.writeAll(&[_]u8{c});
85 }
86 }
79 return;87 return;
80 }88 }
81 @compileError("pek: compile: unsupported type: " ++ @typeName(TO));89 @compileError("pek: compile: unsupported type: " ++ @typeName(TO));
...@@ -101,3 +109,15 @@ fn do(writer: anytype, comptime value: astgen.Value, data: anytype, ctx: anytype...@@ -101,3 +109,15 @@ fn do(writer: anytype, comptime value: astgen.Value, data: anytype, ctx: anytype
101fn search(comptime T: anytype, comptime args: []const []const u8) @TypeOf(if (args.len == 1) @field(T, args[0]) else search(@field(T, args[0]), args[1..])) {109fn search(comptime T: anytype, comptime args: []const []const u8) @TypeOf(if (args.len == 1) @field(T, args[0]) else search(@field(T, args[0]), args[1..])) {
102 return if (args.len == 1) @field(T, args[0]) else search(@field(T, args[0]), args[1..]);110 return if (args.len == 1) @field(T, args[0]) else search(@field(T, args[0]), args[1..]);
103}111}
112
113fn entityLookupBefore(in: []const u8) ?htmlentities.Entity {
114 for (htmlentities.ENTITIES) |e| {
115 if (!std.mem.endsWith(u8, e.entity, ";")) {
116 continue;
117 }
118 if (std.mem.eql(u8, e.characters, in)) {
119 return e;
120 }
121 }
122 return null;
123}
src/main.zig+2
...@@ -21,6 +21,7 @@ const example_document =...@@ -21,6 +21,7 @@ const example_document =
21 \\ li({this.name}", "{this.state.code})21 \\ li({this.name}", "{this.state.code})
22 \\ /each/22 \\ /each/
23 \\ )23 \\ )
24 \\ p("Spooky text: "{spooky})
24 \\ )25 \\ )
25 \\)26 \\)
26;27;
...@@ -39,5 +40,6 @@ pub fn main() !void {...@@ -39,5 +40,6 @@ pub fn main() !void {
39 .{ .name = "Houston", .state = .{ .code = "TX" } },40 .{ .name = "Houston", .state = .{ .code = "TX" } },
40 .{ .name = "Phoenix", .state = .{ .code = "AZ" } },41 .{ .name = "Phoenix", .state = .{ .code = "AZ" } },
41 },42 },
43 .spooky = "<strong>I better not be in bold.</strong>",
42 });44 });
43}45}
zig.mod+1
...@@ -5,3 +5,4 @@ license: AGPL-3.0...@@ -5,3 +5,4 @@ license: AGPL-3.0
5description: An HTML preprocessor with a builtin template engine.5description: An HTML preprocessor with a builtin template engine.
6dependencies:6dependencies:
7 - src: git https://github.com/nektro/zig-range7 - src: git https://github.com/nektro/zig-range
8 - src: git https://github.com/kivikakk/htmlentities.zig
zigmod.lock+1
...@@ -1,2 +1,3 @@...@@ -1,2 +1,3 @@
1212
2git https://github.com/nektro/zig-range commit-890ca308fe09b3d5c866d5cfb3b3d7a95dbf939f2git https://github.com/nektro/zig-range commit-890ca308fe09b3d5c866d5cfb3b3d7a95dbf939f
3git https://github.com/kivikakk/htmlentities.zig commit-c3cbe4cb3a9a9bd60e7c9fedcf9b97e087b98d6c