[Rt-commit] rtir branch, 4.2/rss-feed-reader, repushed
Aaron Trevena
ast at bestpractical.com
Tue Jun 2 06:55:57 EDT 2020
The branch 4.2/rss-feed-reader was deleted and repushed:
was ed6039631bb382ff4ed0910790f35f2c610926c3
now be35374d8a51ab565e5a444aca4b7890b3252f39
1: 4652c318 ! 1: be35374d Added external feeds to tools to create incidents from RSS
@@ -1,4 +1,4 @@
-Author: Aaron Trevena <aaron at aarontrevena.co.uk>
+Author: Aaron Trevena <ast at bestpractical.com>
Added external feeds to tools to create incidents from RSS
@@ -20,6 +20,47 @@
build_requires('Test::More');
build_requires('File::Find');
+diff --git a/etc/RTIR_Config.pm b/etc/RTIR_Config.pm
+--- a/etc/RTIR_Config.pm
++++ b/etc/RTIR_Config.pm
+@@
+
+ Set($RunWhoisRequestByDefault, 0);
+
++=item %ExternalFeeds
++
++Sources for the External Feeds tool, currently RSS is supported. Provide a Name and URI for each source and you can also provide an optional Description.
++
++
++Set( %ExternalFeeds, (
++ 'RSS' => [
++ { Name => 'US Cert Alerts', URI => 'https://www.us-cert.gov/ncas/alerts.xml', Description => 'US Cert Alerts' },
++ ....
++ ]
++ )
++);
++
++The initial list is US Cert Alerts, UK NCSC Security News, Security Focus Vulnerability Alerts, Threatpost Vulnerability Alerts and Bugtraq.
++
++=cut
++
++Set( %ExternalFeeds, (
++ 'RSS' => [
++ { Name => 'US Cert Alerts', URI => 'https://www.us-cert.gov/ncas/alerts.xml', Description => 'US Cert Alerts' },
++ { Name => 'UK NCSC Security News', URI => 'https://www.ncsc.gov.uk/api/1/services/v1/all-rss-feed.xml', Description => 'UK NCSC Security News' },
++ { Name => 'Security Focus Vulnerability Alerts', URI => 'https://www.securityfocus.com/rss/vulnerabilities.xml', Description => 'Security Focus Vulnerability Alerts' },
++ { Name => 'Threatpost Vulnerability Alerts', URI => 'https://threatpost.com/category/vulnerabilities/feed/', Description => 'Threatpost Vulnerability Alerts' },
++ { Name => 'Bugtraq', URI => 'https://seclists.org/rss/bugtraq.rss', Description => 'Bugtraq feed' },
++ ]
++));
++
+ =back
+
++
+ =head1 Service Level Agreements (SLA)
+
+ Read F<docs/AdministrationTutorial.pod>.
+
diff --git a/html/Callbacks/RTIR/Elements/Tabs/Privileged b/html/Callbacks/RTIR/Elements/Tabs/Privileged
--- a/html/Callbacks/RTIR/Elements/Tabs/Privileged
+++ b/html/Callbacks/RTIR/Elements/Tabs/Privileged
@@ -31,6 +72,27 @@
my $request_path = $HTML::Mason::Commands::r->path_info;
$request_path =~ s!/{2,}!/!g;
+
+diff --git a/html/RTIR/Incident/Create.html b/html/RTIR/Incident/Create.html
+--- a/html/RTIR/Incident/Create.html
++++ b/html/RTIR/Incident/Create.html
+@@
+ % }
+
+ <input type="hidden" name="id" value="new" />
++<input type="hidden" class="hidden" name="new-RefersTo" value="<% $ARGS{'new-RefersTo'} %>" />
+ <input type="hidden" class="hidden" name="Token" value="<% $ARGS{'Token'} %>" />
+ <input type="hidden" name="QueueChanged" value="0" />
+ % if ( $ChildObj ) {
+@@
+ $checks_failure = 1;
+ }
+
++ my $links = ProcessLinksForCreate(ARGSRef => \%ARGS);
++
+ $checks_failure += RT::IR->FilterRTAddresses(
+ ARGSRef => \%ARGS,
+ Fields => { Requestors => 'Requestor', Cc => 'Cc', AdminCc => 'AdminCc' },
diff --git a/html/RTIR/Tools/ExternalFeeds.html b/html/RTIR/Tools/ExternalFeeds.html
new file mode 100644
@@ -86,15 +148,20 @@
+%# END BPS TAGGED BLOCK }}}
+
+<& /RTIR/Elements/Header, Title => $title &>
-+<& /Elements/Tabs &>
++ <& /Elements/Tabs &>
++<div class="col-12">
+% my $i = 0;
+% if ($FeedName) {
+% my $feed = $ExternalFeeds->fetch_rss_feed($FeedName);
+<&|/Widgets/TitleBox,
+ title => $feed->{Title},
-+ class => "fullwidth",
++ class => "external-feeds fullwidth",
+ bodyclass => "",
-+&>
++ &>
++% if ($feed->{__error}) {
++<p> <% $feed->{__error} %> </p>
++% }
++% else {
+<div class="table-responsive">
+ <% $feed->{Description} %>
+% if ( $feed->{PubDate} || $feed->{LastBuildDate}) {
@@ -104,11 +171,14 @@
+ <tr class="collection-as-table">
+ <th class="collection-as-table"><&|/l&>Name</&></th>
+ <th class="collection-as-table"><&|/l&>Created</&></th>
-+ <th class="collection-as-table"><% loc('Create a new [_1]', $ticket_type) %></th>
++ <th class="collection-as-table">
++ <% loc('Create a new [_1]', $ticket_type) %>
++ <span class="far fa-question-circle icon-helper" data-toggle="tooltip" data-placement="top" data-original-title="<&|/l&>This will take you to a partially prefilled creation form.</&>"></span>
++ </th>
+</tr>
+<tbody class="list-item">
+% foreach my $item (@{ $feed->{items} }) {
-+% my $GeneratedSubject = sprintf('Incident from RSS feed %s : %s', $feed->{Title}, $item->{Title});
++% my $GeneratedSubject = sprintf('%s : %s', $feed->{Title}, $item->{Title});
+% my $GeneratedMessage = join("\n",
+% sprintf('Incident created from RSS feed %s : %s', $feed->{Title}, $item->{Title}),
+% sprintf('Source : %s on %s', $item->{Link} , $item->{PubDate} || $item->{LastBuildDate} || '-'),
@@ -120,36 +190,32 @@
+ <form action="<% $CreateURI %>" name="CreateIncident-<% $i %>" id="CreateIncident-<% $i %>" method="post">
+ <input type="hidden" value="<% $GeneratedSubject %>" name="Subject">
+ <input type="hidden" value="<% $GeneratedMessage %>" name="Content">
-+ <& /RTIR/Elements/SelectRTIRQueue,
-+ Name => 'Queue',
-+ ShowNullOption => 0,
-+ Lifecycle => $Lifecycle,
-+ LimitToConstituency => 1,
-+ Constituency => $m->{'RTIR_ConstituencyFilter'} &>
-+ <& /Elements/Submit, Label => loc("Go"), Caption => loc("This will take you to a partially prefilled [_1] creation form.", $ticket_type) &>
++ <input type="hidden" value="<% $item->{Link} %>" Name="new-RefersTo">
++ <input type="hidden" value="<% $Lifecycle %>" Name="Lifecycle">
++ <input type="submit" class="button btn btn-primary form-control" value="<&|/l&>Create new ticket</&>" />
+ </form>
+ </td>
+ </tr>
+
-+
+ <tr class="<% $i%2 ? 'oddline' : 'evenline' %>">
-+ <td class="collection-as-table" colspan="3"><small><% $item->{Description} %></small></td>
++ <td class="collection-as-table" colspan="3"><small><% $item->{scrubbed_description} |n%></small></td>
+ </tr>
+% $i++;
+% }
+</tbody>
+</table>
+</div>
++% }
+</&>
-+% }
-+% else {
++% } else {
+<&|/Widgets/TitleBox,
+ title => loc("RSS"),
+ class => "fullwidth",
+ bodyclass => ""
-+&>
-+<table border="0" cellspacing="0" cellpadding="1" width="100%" class="table queue-summary">
-+<tr>
++ &>
++% if ($ExternalFeeds->have_rss_feeds) {
++<table cellspacing="0" class="table collection collection-as-table">
++<tr class="collection-as-table">
+<th class="collection-as-table"><&|/l&>Name</&></th>
+<th class="collection-as-table"><&|/l&>Description</&></th>
+</tr>
@@ -161,8 +227,17 @@
+% $i++;
+% }
+</table>
++% }
++% else {
++<p>
++ No RSS feeds currently configured, you can configure feeds in the %ExternalFeeds option of etc/RT_SiteConfig.pm or etc/RT_SiteConfig.d/RT_SiteConfig.pm, a default set of security feeds is included in the inital RTIR configuration.
++</p>
++
++% }
+</&>
+% }
++</div>
++
+<%INIT>
+use URI::Escape;
+use RT::IR::ExternalFeeds;
@@ -253,21 +328,35 @@
+ @_,
+ );
+ $self->{ua} = LWP::UserAgent->new(timeout => 20);
-+ $self->{rss_feeds} = {
-+ map { $_->{Name} => $_ }
-+ @{RT->Config->Get('ExternalFeeds')->{RSS}}
-+ };
-+ $self->{_rss_parser} = XML::RSS->new();
++ $self->{rss_feeds} = { };
++ $self->{have_rss_feeds} = 0;
++
++ if (RT->Config->Get('ExternalFeeds')->{RSS}) {
++ my $i = 1;
++ foreach my $rss_feed ( @{RT->Config->Get('ExternalFeeds')->{RSS}} ) {
++ next unless (ref $rss_feed eq 'HASH');
++ $rss_feed->{index} = $i++;
++ $self->{rss_feeds}{$rss_feed->{Name}} = $rss_feed;
++ $self->{have_rss_feeds} ||= 1;
++ }
++ }
++ $self->{_rss_parser} = XML::RSS->new();
++
+}
+
+sub rss_feeds {
+ my $self = shift;
-+ return values %{$self->{rss_feeds}};
++ return sort { $a->{index} <=> $b->{index} } values %{$self->{rss_feeds}};
++}
++
++sub have_rss_feeds {
++ return shift()->{have_rss_feeds};
+}
+
+sub fetch_rss_feed {
+ my ($self, $name) = @_;
+ my $url = $self->{rss_feeds}{$name}{URI};
++ # make sure we have a fairly short timeout so page doesn't get apache timeout.
+ my $response = $self->{ua}->get($url);
+ return $self->_parse_rss_feed($response);
+}
@@ -276,8 +365,12 @@
+
+sub _parse_rss_feed {
+ my ($self, $response) = @_;
-+ return { } unless ($response->is_success);
-+ $self->{_rss_parser}->parse($response->content);
++ return { __error => "Can't reach feed : " . $response->status_line } unless ($response->is_success);
++ eval { $self->{_rss_parser}->parse($response->content); };
++ unless ( $self->{_rss_parser}{channel}{title} && $self->{_rss_parser}{items}[0] ) {
++ return { __error => "Couldn't parse RSS response "};
++ }
++
+ my $parsed_feed = { map { ucfirst($_) => $self->{_rss_parser}{channel}{$_} }
+ ( qw(title description pubDate lastBuildDate) ) };
+ foreach my $item (@{$self->{_rss_parser}{items}}) {
@@ -288,13 +381,48 @@
+ $item_values->{Link} //= $item_values->{Url};
+ if (defined( $item->{'description'} ) ) {
+ $item_values->{Description} = decode_entities($item->{'description'});
++ $item_values->{scrubbed_description} = $self->_scrub_html($item_values->{Description});
+ } else {
-+ $item_values->{Description} = 'No content/description for this item';
++ $item_values->{scrubbed_description} = $item_values->{Description} = 'No content/description for this item';
+ }
+ push (@{$parsed_feed->{items}}, $item_values);
+ }
+ return $parsed_feed;
+}
+
++sub _scrub_html {
++ my ($self, $html) = @_;
++ unless ($self->{_scrubber}) {
++ my $scrubber = HTML::Scrubber->new( script => 0, allow => [ qw[ p b i u br ] ] );
++ $scrubber->rules(
++ a => {
++ 'href' => qr{^(?:http|https)://}i,
++ '*' => 0
++ },
++ '*' => 0
++ );
++ $self->{_scrubber} = $scrubber;
++ }
++ my $scrubbed_html = $self->{_scrubber}->scrub($html);
++ $scrubbed_html =~ s|<\/?p>|<br>|gi;
++ return $scrubbed_html;
++}
++
+1;
+diff --git a/static/css/rtir-styles.css b/static/css/rtir-styles.css
+--- a/static/css/rtir-styles.css
++++ b/static/css/rtir-styles.css
+@@
+ body.rtir table.lookup-tool-forms td {
+ vertical-align: middle;
+ }
++
++body.rtir .titlebox.external-feeds tr.oddline+.oddline .collection-as-table,
++body.rtir .titlebox.external-feeds tr.evenline+.evenline .collection-as-table {
++ padding-bottom: 0.7rem;
++}
++
++body.rtir .titlebox.external-feeds .collection-as-table .unclip {
++ margin-top: 0.3rem;
++}
2: c0b4b717 < -: ------- Code review improvements to RSS Feeds
3: ed603963 < -: ------- Small cleanup of External Feeds HTML
More information about the rt-commit
mailing list