handle escaping xml data
This commit is contained in:
parent
eb16d2396a
commit
7a5d10bc82
1 changed files with 88 additions and 7 deletions
95
src/atom.zig
95
src/atom.zig
|
@ -5,6 +5,19 @@ const zeit = @import("zeit");
|
|||
|
||||
const Release = @import("main.zig").Release;
|
||||
|
||||
fn escapeXml(writer: anytype, input: []const u8) !void {
|
||||
for (input) |char| {
|
||||
switch (char) {
|
||||
'<' => try writer.writeAll("<"),
|
||||
'>' => try writer.writeAll(">"),
|
||||
'&' => try writer.writeAll("&"),
|
||||
'"' => try writer.writeAll("""),
|
||||
'\'' => try writer.writeAll("'"),
|
||||
else => try writer.writeByte(char),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generateFeed(allocator: Allocator, releases: []const Release) ![]u8 {
|
||||
var buffer = ArrayList(u8).init(allocator);
|
||||
defer buffer.deinit();
|
||||
|
@ -32,13 +45,37 @@ pub fn generateFeed(allocator: Allocator, releases: []const Release) ![]u8 {
|
|||
// Add entries
|
||||
for (releases) |release| {
|
||||
try writer.writeAll("<entry>\n");
|
||||
try writer.print(" <title>{s} - {s}</title>\n", .{ release.repo_name, release.tag_name });
|
||||
try writer.print(" <link href=\"{s}\"/>\n", .{release.html_url});
|
||||
try writer.print(" <id>{s}</id>\n", .{release.html_url});
|
||||
try writer.print(" <updated>{s}</updated>\n", .{release.published_at});
|
||||
try writer.print(" <author><name>{s}</name></author>\n", .{release.provider});
|
||||
try writer.print(" <summary>{s}</summary>\n", .{release.description});
|
||||
try writer.print(" <category term=\"{s}\"/>\n", .{release.provider});
|
||||
|
||||
try writer.writeAll(" <title>");
|
||||
try escapeXml(writer, release.repo_name);
|
||||
try writer.writeAll(" - ");
|
||||
try escapeXml(writer, release.tag_name);
|
||||
try writer.writeAll("</title>\n");
|
||||
|
||||
try writer.writeAll(" <link href=\"");
|
||||
try escapeXml(writer, release.html_url);
|
||||
try writer.writeAll("\"/>\n");
|
||||
|
||||
try writer.writeAll(" <id>");
|
||||
try escapeXml(writer, release.html_url);
|
||||
try writer.writeAll("</id>\n");
|
||||
|
||||
try writer.writeAll(" <updated>");
|
||||
try escapeXml(writer, release.published_at);
|
||||
try writer.writeAll("</updated>\n");
|
||||
|
||||
try writer.writeAll(" <author><name>");
|
||||
try escapeXml(writer, release.provider);
|
||||
try writer.writeAll("</name></author>\n");
|
||||
|
||||
try writer.writeAll(" <summary>");
|
||||
try escapeXml(writer, release.description);
|
||||
try writer.writeAll("</summary>\n");
|
||||
|
||||
try writer.writeAll(" <category term=\"");
|
||||
try escapeXml(writer, release.provider);
|
||||
try writer.writeAll("\"/>\n");
|
||||
|
||||
try writer.writeAll("</entry>\n");
|
||||
}
|
||||
|
||||
|
@ -47,6 +84,22 @@ pub fn generateFeed(allocator: Allocator, releases: []const Release) ![]u8 {
|
|||
return buffer.toOwnedSlice();
|
||||
}
|
||||
|
||||
test "XML escaping" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
var buffer = ArrayList(u8).init(allocator);
|
||||
defer buffer.deinit();
|
||||
|
||||
const input = "Test <tag> & \"quotes\" & 'apostrophes'";
|
||||
try escapeXml(buffer.writer(), input);
|
||||
|
||||
const result = try buffer.toOwnedSlice();
|
||||
defer allocator.free(result);
|
||||
|
||||
const expected = "Test <tag> & "quotes" & 'apostrophes'";
|
||||
try std.testing.expectEqualStrings(expected, result);
|
||||
}
|
||||
|
||||
test "Atom feed generation" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
|
@ -68,3 +121,31 @@ test "Atom feed generation" {
|
|||
try std.testing.expect(std.mem.indexOf(u8, atom_content, "v1.0.0") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, atom_content, "<feed xmlns=\"http://www.w3.org/2005/Atom\">") != null);
|
||||
}
|
||||
|
||||
test "Atom feed with special characters" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
const releases = [_]Release{
|
||||
Release{
|
||||
.repo_name = "test/repo<script>",
|
||||
.tag_name = "v1.0.0 & more",
|
||||
.published_at = "2024-01-01T00:00:00Z",
|
||||
.html_url = "https://github.com/test/repo/releases/tag/v1.0.0",
|
||||
.description = "Test \"release\" with <special> chars & symbols",
|
||||
.provider = "github",
|
||||
},
|
||||
};
|
||||
|
||||
const atom_content = try generateFeed(allocator, &releases);
|
||||
defer allocator.free(atom_content);
|
||||
|
||||
// Verify special characters are properly escaped
|
||||
try std.testing.expect(std.mem.indexOf(u8, atom_content, "<script>") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, atom_content, "& more") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, atom_content, ""release"") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, atom_content, "<special>") != null);
|
||||
|
||||
// Verify raw special characters are not present
|
||||
try std.testing.expect(std.mem.indexOf(u8, atom_content, "<script>") == null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, atom_content, "\"release\"") == null);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue