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;
|
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 {
|
pub fn generateFeed(allocator: Allocator, releases: []const Release) ![]u8 {
|
||||||
var buffer = ArrayList(u8).init(allocator);
|
var buffer = ArrayList(u8).init(allocator);
|
||||||
defer buffer.deinit();
|
defer buffer.deinit();
|
||||||
|
@ -32,13 +45,37 @@ pub fn generateFeed(allocator: Allocator, releases: []const Release) ![]u8 {
|
||||||
// Add entries
|
// Add entries
|
||||||
for (releases) |release| {
|
for (releases) |release| {
|
||||||
try writer.writeAll("<entry>\n");
|
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.writeAll(" <title>");
|
||||||
try writer.print(" <id>{s}</id>\n", .{release.html_url});
|
try escapeXml(writer, release.repo_name);
|
||||||
try writer.print(" <updated>{s}</updated>\n", .{release.published_at});
|
try writer.writeAll(" - ");
|
||||||
try writer.print(" <author><name>{s}</name></author>\n", .{release.provider});
|
try escapeXml(writer, release.tag_name);
|
||||||
try writer.print(" <summary>{s}</summary>\n", .{release.description});
|
try writer.writeAll("</title>\n");
|
||||||
try writer.print(" <category term=\"{s}\"/>\n", .{release.provider});
|
|
||||||
|
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");
|
try writer.writeAll("</entry>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +84,22 @@ pub fn generateFeed(allocator: Allocator, releases: []const Release) ![]u8 {
|
||||||
return buffer.toOwnedSlice();
|
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" {
|
test "Atom feed generation" {
|
||||||
const allocator = std.testing.allocator;
|
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, "v1.0.0") != null);
|
||||||
try std.testing.expect(std.mem.indexOf(u8, atom_content, "<feed xmlns=\"http://www.w3.org/2005/Atom\">") != 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