diff --git a/ghostwriter/oplog/forms.py b/ghostwriter/oplog/forms.py index d945e1664..21ced2994 100644 --- a/ghostwriter/oplog/forms.py +++ b/ghostwriter/oplog/forms.py @@ -134,9 +134,13 @@ def __init__(self, *args, **kwargs): self.helper.layout = Layout( Row( - Column(Field("start_date", step=1), css_class="form-group col-4 mb-0"), - Column(Field("end_date", step=1), css_class="form-group col-4 mb-0"), - Column("operator_name", css_class="form-group col-4 mb-0"), + Column(Field("start_date", step=1), css_class="form-group col-6 mb-0"), + Column(Field("end_date", step=1), css_class="form-group col-6 mb-0"), + css_class="form-row", + ), + Row( + Column("entry_identifier", css_class="form-group col-6 mb-0"), + Column("operator_name", css_class="form-group col-6 mb-0"), css_class="form-row", ), Row( diff --git a/ghostwriter/oplog/migrations/0012_auto_20231211_2154.py b/ghostwriter/oplog/migrations/0012_auto_20231211_2154.py new file mode 100644 index 000000000..59fb5a2e6 --- /dev/null +++ b/ghostwriter/oplog/migrations/0012_auto_20231211_2154.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.19 on 2023-12-11 21:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('oplog', '0011_auto_20230323_2248'), + ] + + operations = [ + migrations.AddField( + model_name='oplogentry', + name='entry_identifier', + field=models.CharField(blank=True, help_text='Integrations may use this to track log entries.', max_length=65535, null=True, verbose_name='Identifier'), + ), + migrations.AddIndex( + model_name='oplogentry', + index=models.Index(fields=['oplog_id', 'entry_identifier'], name='oplog_oplog_oplog_i_0e03f5_idx'), + ), + ] diff --git a/ghostwriter/oplog/models.py b/ghostwriter/oplog/models.py index d33f77880..93b92c91f 100644 --- a/ghostwriter/oplog/models.py +++ b/ghostwriter/oplog/models.py @@ -44,6 +44,13 @@ def __str__(self): class OplogEntry(models.Model): """Stores an individual log entry, related to :model:`oplog.Oplog`.""" + entry_identifier = models.CharField( + "Identifier", + null=True, + blank=True, + help_text="Integrations may use this to track log entries.", + max_length=65535, + ) start_date = models.DateTimeField( "Start Date", null=True, @@ -135,6 +142,9 @@ class Meta: ordering = ["-start_date", "-end_date", "oplog_id"] verbose_name = "Activity log entry" verbose_name_plural = "Activity log entries" + indexes = [ + models.Index(fields=["oplog_id", "entry_identifier"]), + ] def clean(self, *args, **kwargs): if isinstance(self.start_date, str): diff --git a/ghostwriter/oplog/tests/test_views.py b/ghostwriter/oplog/tests/test_views.py index f7baa5edd..fab05b980 100644 --- a/ghostwriter/oplog/tests/test_views.py +++ b/ghostwriter/oplog/tests/test_views.py @@ -157,6 +157,7 @@ def test_view_uses_correct_template(self): def test_post_data_and_permissions(self): filename = "oplog_import_test.csv" fieldnames = [ + "entry_identifier", "start_date", "end_date", "source_ip", @@ -207,6 +208,7 @@ def test_oplog_id_override(self): """Test that the ``oplog_id`` field is overridden when importing.""" filename = "oplog_import_test.csv" fieldnames = [ + "entry_identifier", "oplog_id", "start_date", "end_date", diff --git a/ghostwriter/static/js/oplog.js b/ghostwriter/static/js/oplog.js index e5bf9ad50..eb58040b3 100644 --- a/ghostwriter/static/js/oplog.js +++ b/ghostwriter/static/js/oplog.js @@ -6,8 +6,10 @@ let hiddenLogTblColumns = JSON.parse((localStorage.getItem('hiddenLogTblColumns' // Assemble the array of column information for the table let columnInfo = [] columnInfo = [ - ['startDateCheckBox', 'startDateColumn', 'Start Date', 'start_date'], - ['endDateCheckbox', 'endDateColumn', 'End Date', 'end_date'], + // [checkBoxID, columnClass, Pretty Name, name_in_json, toHtmlFunc (default `jsescape`), shownByDefault (default true)] + ['identifierCheckBox', 'identifierColumn', 'Identifier', 'entry_identifier', undefined, false], + ['startDateCheckBox', 'startDateColumn', 'Start Date', 'start_date', entry => jsEscape(entry).replace(/\.\d+/, "").replace("Z", "").replace("T", " ")], + ['endDateCheckbox', 'endDateColumn', 'End Date', 'end_date', entry => jsEscape(entry).replace(/\.\d+/, "").replace("Z", "").replace("T", " ")], ['sourceIPCheckbox', 'sourceIPColumn', 'Source', 'source_ip'], ['destIPCheckbox', 'destIPColumn', 'Destination', 'dest_ip'], ['toolNameCheckbox', 'toolNameColumn', 'Tool Name', 'tool'], @@ -17,25 +19,17 @@ columnInfo = [ ['outputCheckbox', 'outputColumn', 'Output', 'output'], ['commentsCheckbox', 'commentsColumn', 'Comments', 'comments'], ['operatorCheckbox', 'operatorColumn', 'Operator', 'operator_name'], - ['tagsCheckbox', 'tagsColumn', 'Tags', 'tags'], + ['tagsCheckbox', 'tagsColumn', 'Tags', 'tags', entry => stylizeTags(jsEscape(entry))], ['optionsCheckbox', 'optionsColumn', 'Options', ''], ] // Generate a table row based on a log entry function generateTableHeaders() { - return `${columnInfo[0][2]} - ${columnInfo[1][2]} - ${columnInfo[2][2]} - ${columnInfo[3][2]} - ${columnInfo[4][2]} - ${columnInfo[5][2]} - ${columnInfo[6][2]} - ${columnInfo[7][2]} - ${columnInfo[8][2]} - ${columnInfo[9][2]} - ${columnInfo[10][2]} - ${columnInfo[11][2]} - ${columnInfo[12][2]}` + let out = ""; + columnInfo.forEach(column => { + out += `${column[2]}` + }); + return out; } // Convert a table row to JSON and copy it to the clipboard @@ -84,25 +78,21 @@ function convertRowToJSON(row_id) { // Generate a table row based on a log entry function generateRow(entry) { - return ` - ${jsEscape(entry["start_date"]).replace(/\.\d+/, "").replace("Z", "").replace("T", " ")} - ${jsEscape(entry["end_date"]).replace(/\.\d+/, "").replace("Z", "").replace("T", " ")} - ${jsEscape(entry["source_ip"])} - ${jsEscape(entry["dest_ip"])} - ${jsEscape(entry["tool"])} - ${jsEscape(entry["user_context"])} -
${jsEscape(entry["command"])}
-
${jsEscape(entry["description"])}
-
${jsEscape(entry["output"])}
-
${jsEscape(entry["comments"])}
- ${jsEscape(entry["operator_name"])} - ${stylizeTags(jsEscape(entry["tags"]))} - + let out = ``; + columnInfo.forEach(column => { + if(column[0] == "optionsCheckbox") { + out += ` - - ` + ` + } else { + let value = entry[column[3]]; + let filter = column[4] ?? jsEscape; + out += `${filter(value)}`; + } + }); + return out + ""; } // Add a placeholder row that spans the entire table @@ -156,10 +146,11 @@ function coupleCheckboxColumn(checkboxId, columnClass) { // Build the column show/hide checkboxes function buildColumnsCheckboxes() { columnInfo.forEach(function (value, _, _) { + let checked = (value[5] === undefined || value[5]) ? "checked" : ""; let checkboxEntry = `
- +
diff --git a/hasura-docker/metadata/databases/default/tables/public_oplog_oplogentry.yaml b/hasura-docker/metadata/databases/default/tables/public_oplog_oplogentry.yaml index 693025095..ace1b81cd 100644 --- a/hasura-docker/metadata/databases/default/tables/public_oplog_oplogentry.yaml +++ b/hasura-docker/metadata/databases/default/tables/public_oplog_oplogentry.yaml @@ -41,6 +41,7 @@ insert_permissions: - description - dest_ip - end_date + - entry_identifier - operator_name - oplog_id_id - output @@ -70,30 +71,32 @@ insert_permissions: - description - dest_ip - end_date + - entry_identifier + - operator_name - oplog_id_id - output - source_ip - start_date - tool - user_context - - operator_name select_permissions: - role: manager permission: columns: - - id - - oplog_id_id - - operator_name - command - comments - description - dest_ip + - end_date + - entry_identifier + - id + - operator_name + - oplog_id_id - output - source_ip + - start_date - tool - user_context - - end_date - - start_date filter: {} - role: user permission: @@ -121,6 +124,7 @@ update_permissions: - description - dest_ip - end_date + - entry_identifier - operator_name - oplog_id_id - output @@ -138,13 +142,14 @@ update_permissions: - description - dest_ip - end_date + - entry_identifier + - operator_name - oplog_id_id - output - source_ip - start_date - tool - user_context - - operator_name filter: log: project: diff --git a/hasura-docker/metadata/databases/default/tables/public_reporting_reportfindinglink.yaml b/hasura-docker/metadata/databases/default/tables/public_reporting_reportfindinglink.yaml index 993e65241..b1f6051db 100644 --- a/hasura-docker/metadata/databases/default/tables/public_reporting_reportfindinglink.yaml +++ b/hasura-docker/metadata/databases/default/tables/public_reporting_reportfindinglink.yaml @@ -297,9 +297,9 @@ event_triggers: value_from_env: HASURA_ACTION_SECRET - name: DeleteReportFinding definition: - enable_manual: false delete: columns: '*' + enable_manual: false retry_conf: interval_sec: 10 num_retries: 0