Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FR] Add source_updated_at to Rule Schema as a Build Time Field #2826

Open
Tracked by #179907
terrancedejesus opened this issue Jun 1, 2023 · 45 comments · May be fixed by #3427
Open
Tracked by #179907

[FR] Add source_updated_at to Rule Schema as a Build Time Field #2826

terrancedejesus opened this issue Jun 1, 2023 · 45 comments · May be fixed by #3427
Assignees
Labels
backlog enhancement New feature or request v8.10

Comments

@terrancedejesus
Copy link
Contributor

terrancedejesus commented Jun 1, 2023

Is your feature request related to a problem? Please describe.
No.

Describe the solution you'd like
Add creation_date and updated_date to rule objects when a release package is created.

Additional context
When we build a rules release package, all rule objects should have a creation_date and updated_date field in them. This will be used by Kibana for the updates review workflow.

@jpdjere @approksiu

Dev branch: https:/elastic/detection-rules/tree/fr-add-dates-to-rule-data

@terrancedejesus terrancedejesus added enhancement New feature or request v8.10 labels Jun 1, 2023
@terrancedejesus terrancedejesus self-assigned this Jun 1, 2023
xcrzx added a commit to elastic/kibana that referenced this issue Jun 14, 2023
…158450)

Addresses: #154614
#154615

Figma designs:
https://www.figma.com/file/gLHm8LpTtSkAUQHrkG3RHU/%5B8.7%5D-%5BRules%5D-Rule-Immutability%2FCustomization?type=design&node-id=2935-577576&t=ziqgnlEJBpowqa7F-0

## Summary

- Removes `prebuiltRulesNewUpgradeAndInstallationWorkflowsEnabled`
feature flag. All new prebuilt endpoints now available by default.
- Creates the UI for the new **rules installation** and **rules
upgrade** workflows.
- Creates new **Add Rules** page, which lists rules available for
installation.
- Creates new **Rule Updates** page, which lists rules which have
available updates.
- Creates new, separate contexts for the **Add Rules** and the **Rule
Updates** page, and the hooks to use them
(`useAddPrebuiltRulesTableContext` and
`useUpgradePrebuiltRulesTableContext` respectively)
    - Creates prebuilt rule hooks, which consume new endpoints:
- `useFetchPrebuiltRulesStatusQuery` and `usePrebuiltRulesStatus`
consume the `/internal/detection_engine/prebuilt_rules/status` endpoint
and provide information about number of rules available for
installation, number of installed rules, and number of rules with
available updates.
- `useFetchPrebuiltRulesInstallReviewQuery` and
`usePrebuiltRulesInstallReview` consume the
`/internal/detection_engine/prebuilt_rules/installation/_review`
endpoint and return the rules available for installation which are
listed in the **Add Rules** page.
- `useFetchPrebuiltRulesUpgradeReviewQuery` and
`usePrebuiltRulesUpgradeReview` consume the
`/internal/detection_engine/prebuilt_rules/upgrade/_review` endpoint and
return the rules which have available updates, and are listed in the
**Rule Updates** page.
- `usePerformInstallAllRules`, `usePerformInstallSpecificRules`, and its
respective mutation hooks `usePerformAllRulesInstallMutation` and
`usePerformSpecificRulesInstallMutation` consume the
`/internal/detection_engine/prebuilt_rules/upgrade/_perform` endpoint in
order to install rules.
- `usePerformUpgradeAllRules`, `usePerformUpgradeSpecificRules` and its
respective mutation hooks `usePerformAllRulesUpgradeMutation` and
`usePerformSpecificRulesUpgradeMutation` consume the
`/internal/detection_engine/prebuilt_rules/upgrade/_perform` endpoint in
order to upgrade rules.

### Deprecated code

**Hooks:**
- `useCreatePrebuiltRulesMutation`
- `useInstallPrePackagedRules`
- `useCreatePrePackagedRules`
- `usePrePackagedRulesInstallationStatus`
- `usePrePackagedTimelinesInstallationStatus`

### Major points to resolve

- **Timeline templates installation**: Since this PR stops using the
`/api/detection_engine/rules/prepackaged` endpoint in favour of the new
ones, we are not currently installing timeline templates. Serverside, we
will need a new endpoint to install them separately from rules? In the
UI, how would this still work: would they get installed in the
background now? Or maybe have a new button for it somewhere?
- **ML Jobs warning**: when updating rules, we currently have a wrapper
to add confirmation modal for users who may be running older ML Jobs
that would be overridden by updating their rules. This PR removes that
code, but we'll need to reintroduce it for the cases of: upgrading
single rules, upgrading a selection of rules, upgrading all rules.


### Deviations from design

This PR includes a reduced scope to the final workflow shown in the
Figma designs.

Most notably, in Milestone 2, to be released in 8.9, we did not build
the flyout that, in the Add Rules page, shows the rule details when the
user clicks on it, so the user can review it before installing. The same
is true in the Rule Updates table, which does not allow, for now,
reviewing the rules. In both cases, the user can only click in "Install
Rule" and "Upgrade Rule".

There are other differences in the UI, for technical reasons:
- Both for the Add Rules page and the Rule Updates table we decided to
use **EUI's InMemoryTable**. Since the endpoint that return the data to
populate both of these tables do not allow for sorting, filtering and
paging, we decided to use the InMemoryTable for both cases, as all of
those functions are handled out-of-the-box by the EUI component. The
relatively low number of items that populate these tables means that we
won't face significant performance issues. However, this meant a number
of deviations from the designs:
- Since filtering, sorting and pagination are handled by the table, the
contexts for these table do not includes any internal state relating to
these functions. This makes it hard to recreate the RuleUtilityBar for
each of these components or make the existing one reusable. We therefore
decided to leave the Utility Bar for the new two tables out of scope,
and deviate from the design by moving the button that the user can click
on o install or upgrade the selected rules to beside the "Install all"
or "Upgrade all" buttons. This button is shown only when at least one
rule of the table is selected.
- The **tags filter box** that comes out-of-the-box with the
InMemoryTable can only be positioned to the right of the search bar,
instead of the left like we have in our main **Installed Rules** table.
Also, clicking on the tabs adds the text to the search bar, and the box
does not allow for negative selection of tabs (exclusion).
- The search bar filters on keystroke rather than on Enter. This
behaviour can be changed, but it feels more useful than the other
behaviour for these new two tables.
- The search bar filters by searching the user's input in any of the
string properties of first order within the rule object. This means that
the search bar can be used to look up rules according to their name,
description, rule_id, etc (but not for example for MITRE techniques,
which are an object.) This behaviour, however, is also customisable.
- Neither the Add Rules table nor the Rule Updates tables display the
_Last updated_ column which is shown in the design. Since the original
intent of the designers is to show when the rule asset (`security-rule`)
was created or updated, this is information we don't currently have
within the SO. After discussion with @ksevasilyeva and @ARWNightingale,
we decided, for now, to remove the column. In the meantime,
@terrancedejesus [created an issue to include `createdAt` and
`updatedAt`
fields](elastic/detection-rules#2826) within
the rule assets, that we can use to display in the table in later
iterations.

#### Other remaining work:

- Introduce confirmation modals when the user clicks on the "Install
all" or "Upgrade all" modal.
- Unit testing for new hooks and components.
- Other component redesign: Rule Filter, Tag Filter 

#### How to test rule upgrade

1. Have at least one rule installed
2. Find its `rule_id` from the Network tab.
3. Make a request to `PATCH /api/detection_engine/rules` with the
`rule_id` in the payload, and also set the `version` to a number lower
than the current version.
4. Reload the page.
5. The `/upgrade/_review` endpoint will now return that rule as
available for upgrade.

### Videos

#### Rule Installation Workflow



https:/elastic/kibana/assets/5354282/5a219625-beb1-48ee-a9fc-ff48b69eeae0

#### Rule Upgrade Workflow



https:/elastic/kibana/assets/5354282/b5f3c23b-004a-462c-bbdd-ed04321f5ce7

### TODO

- [x] Align copy, use "update" instead of "upgrade"
- [ ] Persist user's choice when they dismiss the upgrade/install rules
callouts till the next package release (create a separate task for that)
- [ ] Unify table controls (search bar and tags), use the ones we have
on the rules management table
- [ ] After rule installation, adjust copy, and display that all
available rules have been installed. Add a "Go Back" CTA
- [ ] Add links from the available rules table to docs
- [ ] Rule severity sorting should take semantics into consideration

---------

Co-authored-by: Dmitrii <[email protected]>
Co-authored-by: Dmitrii Shevchenko <[email protected]>
Co-authored-by: Sergi Massaneda <[email protected]>
@terrancedejesus
Copy link
Contributor Author

Update

We have some considerations with adding this. These fields are currently in the rule meta since they do not matter for the SHA256 hash calculation. As a result, typically anything we add to the hash calculation should be moved into the rule data itself and have validation done on the values.

Options:

  1. Add creation_date and updated_date to the API formatted rule object, after it is built and hash has been calculated.
  2. Add creation_date and updated_date to strip_additional_fields and remove them during hash calculation.
  3. Move creation_date and updated_date into rule.contents.data and remove from rule.contents.meta.

Either way we need to add validation to these field value pairs to keep the date values consistent.

@Mikaayenson @eric-forte-elastic @brokensound77 - Any additional thoughts?

@eric-forte-elastic
Copy link
Contributor

I do not see an issue with this approach/solution 👍

Just as a note, we will need to update unit tests to also have creation_data and updated_date in the rule.contents.data and update the following line from packaging.py

    def _package_kibana_index_file(self, save_dir):
        """Convert and save index file with package."""
        sorted_rules = sorted(self.rules, key=lambda k: (k.contents.metadata.creation_date, os.path.basename(k.path)))

None of these should be an issue as the functions/tests have access to the contents object of a rule allowing them access to both metadata and data.

@Mikaayenson
Copy link
Contributor

@terrancedejesus Can you explain why we want to make the change to move these fields at all (or even add to the build)? Was it requested upstream?

@terrancedejesus
Copy link
Contributor Author

@terrancedejesus Can you explain why we want to make the change to move these fields at all (or even add to the build)? Was it requested upstream?

Requested upstream from @jpdjere for UI regarding rule update workflow.

@botelastic
Copy link

botelastic bot commented Sep 14, 2023

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@botelastic botelastic bot added the stale 60 days of inactivity label Sep 14, 2023
@terrancedejesus
Copy link
Contributor Author

@jpdjere is this still a request from your team? If so, I'd like to get it correctly scoped for one of our upcoming sprints. Thank you!

@botelastic botelastic bot removed the stale 60 days of inactivity label Sep 14, 2023
@jpdjere
Copy link

jpdjere commented Sep 14, 2023

Hi @terrancedejesus , yes this is still a valid request. We won't be working on anything that needs this data for 8.11 but probably 8.12

@botelastic
Copy link

botelastic bot commented Nov 13, 2023

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@botelastic botelastic bot added the stale 60 days of inactivity label Nov 13, 2023
@jpdjere
Copy link

jpdjere commented Nov 15, 2023

Hi @terrancedejesus . Do you have bandwidth for this in any upcoming releases?

@botelastic botelastic bot removed the stale 60 days of inactivity label Nov 15, 2023
@terrancedejesus
Copy link
Contributor Author

@jpdjere - Thanks for the follow up! I added this to our teams next sprint cycle which starts Nov 27. With recent adjustments to our current sprint cycle, I will attempt to get started with this to determine if it is relatively straight forward and if so will have it in earlier.

@brokensound77
Copy link
Contributor

brokensound77 commented Nov 16, 2023

Hello @jpdjere 👋 ,

Can you provide more context for this request? We are just trying to understand the reasoning and whether this is the best representation of this information.

Right now, since we do not push this with the rules, the dates are pulled from kibana:

  • created when downloaded
  • updated when updates applied

This is reflective of the rules as they apply to a users stack, which seems accurate and informative.

Our dev cycle creates situations where rules may not be released for a few days or weeks after modification, so there is inconsistency that may cause confusion. More so, I think it may be more valuable understanding when a rule was created and modified within a stack vs when it was developed.

These fields under our metadata are currently used as a means to inform us on changes from a maintenance perspective.

Thoughts?

@jpdjere
Copy link

jpdjere commented Nov 21, 2023

Hi @brokensound77 . Thanks for the follow up.

The idea behind this request is to give the users an idea of how "recent" the updates to a specific rule are, in order to know how long those specific updates for the rule have been pending. Here's a screenshot of the UI as proposed by our designers:
image
By sorting by the "Last updated" column, the user could have a quick understanding of which rules have been recently updated in the Fleet package and know which rule updates have been pending for a long time, i.e. should take their most immediate attention.

This becomes especially important when the user has neglected addressing updates from one (or many) package releases, and after some considerable amount of time sees in our Rule Update table a list of rule updates corresponding to more than one releases.

For example, a user seeing this table in October could see listed 4 rules that have updates coming from a package release made in March (so their updated_by timestamps would maybe be around January, February, March), and 5 more rules that have updates coming from a package released in August (and updated_by timestamps would maybe be around May, June, July). If a rule had updates in both releases, the latest would be displayed, since we always compare to the latest version.

(Sorry if this timelines don't make sense, I don't have in my mind right now what is your release cadence).

Having said that:

Our dev cycle creates situations where rules may not be released for a few days or weeks after modification, so there is inconsistency that may cause confusion.

I think that's a valid concern that can cause confusion to users, given the false impression that updates have been pending for a long time, when the rule updates have just been released. A couple questions:

  • How usual is this discrepancy between the actual update date of a rule on your side and the release of a package?
  • Do you have an approximate idea of how large can the date range be for rule updates within one package release? What I mean is: if a package release includes 10 updates, what is the earliest and the latest updated_at timestamp that we would see?

(This second question, I think, is not that important considering that the user might accumulate updates from many subsequent releases, but it's good to know).

@w0rk3r w0rk3r added the backlog label Dec 6, 2023
@terrancedejesus
Copy link
Contributor Author

@jpdjere - Apologies for the questions going unanswered.

How usual is this discrepancy between the actual update date of a rule on your side and the release of a package?

We release OOB updates bi-weekly. Therefore, updated dated discrepancy could be 1-14 days apart as it would depend on the pull request merge and when the package reaches EPR. This could be expanded more if the release takes longer than expected, but is a rare occurrence.

Do you have an approximate idea of how large can the date range be for rule updates within one package release? What I mean is: if a package release includes 10 updates, what is the earliest and the latest updated_at timestamp that we would see?

Any time a rule is updated, the updated date value is updated. Therefore it could be any date between when the last package was available in EPR to the date when the next package is available in EPR. Again, we release bi-weekly so there is an approximate range of ~14 different dates that could apply.

@terrancedejesus
Copy link
Contributor Author

Update 01/08/2023

We are moving forward with this as it is a requirement upstream for customizing prebuilt detection rules, milestone 3. Below are considerations:

  • Do we add these fields to the Kibana API formatted JSON object after schema loading and validation?
  • Do we need to standardize date formats or what is required upstream?
  • Do we move created and updated dates to BaseRuleData?
  • Do we have current date unit tests or do we need to add these?
  • If we do update the schemas, these will backport and what is the resolution for schema failures on backported branches

@jpdjere or @banderror - Can you provide insight to the following for us. Thank you in advance!

  • What is the target minor release for this and will it backport to previous versions?
  • Is there any specific date format that is required?
  • Is this only for created_date and updated_date or are the keys named differently.

@terrancedejesus
Copy link
Contributor Author

terrancedejesus commented Jan 8, 2024

Option 1 - In this option, we rely on post_dict_conversion to add a new method _convert_add_date_fields(). This method takes the rule metadata and assigns it to the same keys, except inside of the obj dictionary which is already converted to Kibana API format from to_api_format(). We also add a validate_date_format() method with validates_schema marshmallow decorator. This will ensure that the date formats are ISO 8601, if we choose to follow this standard. Notes below:

  • This option removes the need to move the date field:values from rule metadata into rule contents data. Thus no massive overhaul has to be done for ALL rules.
  • The API formatted rule will not fail schema validation as these date fields are added AFTER loading through the rule schema
  • Rule version logic is still applied through these changes so dates are now taken into consideration for SHA256
  • If we export a custom rule and attempt to load it through the schema and it has creation_date and updated_date, it would fail because these fields are not recognized in BaseRuleData. Thus these fields would need to be part of the metadata in the export as they typically are. However, I noticed that there are several fields in the first array object of an exported rule that differ from the rule objects we ship.

Commit Reference: 3bc8df6

@terrancedejesus
Copy link
Contributor Author

Option 2 - We only add meta to the package release files. Prebuilt rule packaging and artifact building rely mainly on to_api_format() method in rule.py. We already have an option include_metadata that is by default False. If True, it will add the RuleMeta as a dictionary to the rule object that will become the rule asset in the prebuilt rules package. Therefore we can avoid altering any data schema's, backport concerns, etc. Instead upstream on the Kibana side, they would handle accessing whatever metadata is shipped with the rule. This also allows us to avoid version bumps as well since we are not altering the rule contents, yet adding the metadata to the dropped files instead.

Commit Reference: 0402dc2

@terrancedejesus
Copy link
Contributor Author

Option 3 - We move the date fields from RuleMeta and include them in BaseRuleData as requirements. We then need to adjust every rule back to 8.3 branch, automatically through backporting and manually. The outcome is that date fields will now be in the rule contents of the API formatted rule and be available explicitly upstream by Kibana. Dates would then affect the rule version as well as any changes to rule contents marks the rule as dirty. This option has the biggest amount of changes and correction across branches that would need to be addressed.

@Mikaayenson
Copy link
Contributor

Option 4 - Similar to option 2, only instead of using metadata, is there any reason why we can't use the date of the release (almost like a build time field)? From the description, it doesn't seem necessary to have the exact date the rule was modified by our rule authors.

@terrancedejesus
Copy link
Contributor Author

terrancedejesus commented Jan 8, 2024

Option 4 - Similar to option 2, only instead of using metadata, is there any reason why we can't use the date of the release (almost like a build time field)? From the description, it doesn't seem necessary to have the exact date the rule was modified by our rule authors.

@Mikaayenson Great alternative with a few caveats. Our source-of-truth is typically the repository since that is where we lock versions. Let's say we lock versions and a rule has changes that cause the SHA256 to change. This "state" of the rule is only noticed during the lock versions, which we also release our commits from. Technically, up until this version lock, our rule could go through several changes and updates, but it is only when we lock versions do we track the current state of the rule. The last updated_date would be inline with this SHA256 change as the exact date when the version change was noticed.

We also have to take into consideration release timing. Releases could take 1-2 days, thus the potential for a divergence of dynamic dates based on building the package could occur not only from the version lock, but also between each package. All packages would have to be released GA on the same day for them to accurately reflect the same updated date.

@jpdjere
Copy link

jpdjere commented Jan 17, 2024

Thanks for the explanation.

Now if we have another package later with another different 10 rules updates, of course the date will be different from the first 10.

This is good enough for us. We don't strictly need different dates within one package release - within that 14 day window. As long as we can distinguish between updates coming from different packages releases we are fine.

@terrancedejesus
Copy link
Contributor Author

terrancedejesus commented Jan 18, 2024

@jpdjere - Thank you for the deets.

I have began adding this into the rule asset creation. One thing I noticed is that when we add historical rules to the package, these will not include an elastic_update_date as these assets are pulled from EPR and are before this addition. Thoughts on this?

Also, below is an example of the rule asset with elastic_update_date, does this work? FYI, we are pulling this date from the rule metadata updated_date - just want to confirm this is fine.

Also we added elastic_updated_date to the root of the rule asset. The other keys here are id and type. - Want to confirm this is fine as well.

Example rule asset
{
    "attributes": {
        "author": [
            "Elastic"
        ],
        "description": "Elastic Endgame detected Malware. Click the Elastic Endgame icon in the event.module column or the link in the rule.reference column for additional information.",
        "from": "now-15m",
        "index": [
            "endgame-*"
        ],
        "interval": "10m",
        "language": "kuery",
        "license": "Elastic License v2",
        "max_signals": 10000,
        "name": "Malware - Detected - Elastic Endgame",
        "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:detection and (event.action:file_classification_event or endgame.event_subtype_full:file_classification_event)\n",
        "required_fields": [
            {
                "ecs": false,
                "name": "endgame.event_subtype_full",
                "type": "unknown"
            },
            {
                "ecs": false,
                "name": "endgame.metadata.type",
                "type": "unknown"
            },
            {
                "ecs": true,
                "name": "event.action",
                "type": "keyword"
            },
            {
                "ecs": true,
                "name": "event.kind",
                "type": "keyword"
            },
            {
                "ecs": true,
                "name": "event.module",
                "type": "keyword"
            }
        ],
        "risk_score": 99,
        "rule_id": "0a97b20f-4144-49ea-be32-b540ecc445de",
        "severity": "critical",
        "tags": [
            "Data Source: Elastic Endgame"
        ],
        "type": "query",
        "version": 101
    },
    "elastic_update_date": "2023-06-22T00:00:00",
    "id": "0a97b20f-4144-49ea-be32-b540ecc445de_101",
    "type": "security-rule"
}

@jpdjere
Copy link

jpdjere commented Jan 22, 2024

@terrancedejesus Sorry for the delay in replying.

I have began adding this into the rule asset creation. One thing I noticed is that when we add historical rules to the package, these will not include an elastic_update_date as these assets are pulled from EPR and are before this addition. Thoughts on this?

Yes, we understand this will be the case. We will add elastic_update_date as an optional field within our Prebuilt rule asset schema and our internal rule schema to accommodate for the fact that some rule will have this info and others won't.

Also, below is an example of the rule asset with elastic_update_date, does this work? FYI, we are pulling this date from the rule metadata updated_date - just want to confirm this is fine.

elastic_update_date pulled from updated_date works for us 👍

Also we added elastic_updated_date to the root of the rule asset. The other keys here are id and type. - Want to confirm this is fine as well.

We strongly prefer to have the elastic_updated_date within the attributes field. Our Prebuilt Rule Assets Client pulls only data living within this subfield (renamed security-rule when the prebuilt rule is installed as a prebuilt rule asset in Elasticsearch), and discards the id and type - and all other data in the "root" level.

Would this be fine by you as well? Or would it have side-effects in hashing, etc? Sorry for the confusion in the discussion above, where we talked about "root-level".

@terrancedejesus
Copy link
Contributor Author

@jpdjere - Thanks for the reply!

We strongly prefer to have the elastic_updated_date within the attributes field. Our Prebuilt Rule Assets Client pulls only data living within this subfield (renamed security-rule when the prebuilt rule is installed as a prebuilt rule asset in Elasticsearch), and discards the id and type - and all other data in the "root" level.

No problem with us, easy to adjust and thank you for clarification.

Here would be an updated rule asset, does this work?

Example
{
    "attributes": {
        "author": [
            "Elastic"
        ],
        "description": "Elastic Endgame detected Malware. Click the Elastic Endgame icon in the event.module column or the link in the rule.reference column for additional information.",
        "elastic_update_date": "2023-06-22T00:00:00",
        "from": "now-15m",
        "index": [
            "endgame-*"
        ],
        "interval": "10m",
        "language": "kuery",
        "license": "Elastic License v2",
        "max_signals": 10000,
        "name": "Malware - Detected - Elastic Endgame",
        "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:detection and (event.action:file_classification_event or endgame.event_subtype_full:file_classification_event)\n",
        "required_fields": [
            {
                "ecs": false,
                "name": "endgame.event_subtype_full",
                "type": "unknown"
            },
            {
                "ecs": false,
                "name": "endgame.metadata.type",
                "type": "unknown"
            },
            {
                "ecs": true,
                "name": "event.action",
                "type": "keyword"
            },
            {
                "ecs": true,
                "name": "event.kind",
                "type": "keyword"
            },
            {
                "ecs": true,
                "name": "event.module",
                "type": "keyword"
            }
        ],
        "risk_score": 99,
        "rule_id": "0a97b20f-4144-49ea-be32-b540ecc445de",
        "severity": "critical",
        "tags": [
            "Data Source: Elastic Endgame"
        ],
        "type": "query",
        "version": 101
    },
    "id": "0a97b20f-4144-49ea-be32-b540ecc445de_101",
    "type": "security-rule"
}

@jpdjere
Copy link

jpdjere commented Jan 22, 2024

@terrancedejesus

Great, thanks a lot! 👍

Yes, that looks good. Just a nit - wanted to make sure that the date format is ISO 8601 with the UTC format; the example above is missing the miliseconds and the Z at the end: 2019-11-14T00:55:31.820Z. That's how the date are currently formatted for the created_at and updated_at properties in the security-rule assets:

image

@terrancedejesus
Copy link
Contributor Author

terrancedejesus commented Jan 25, 2024

@jpdjere - Thanks for responding.

the example above is missing the miliseconds and the Z at the end

The milliseconds and Z I can add these. Note that our updated_date in the rule metadata is not ISO-8601 formatted, so no time is captured and it will remain as 00 for these.

Since we are adding elastic_update_date to the attributes, is this now a required rule field that was added to the rule schema upstream? We ask because at the moment, our PoC for this loads the TOML file to JSON, then loads it as an object through our rule schema, which is how we validate the rule is valid. Only after this, do we add the elastic_update_date field when the rule is then converted to a rule asset to avoid version control, backports, breaking changes, etc. - Do we know if a rule is exported this field is exported as well in the rule?

From your image, it looks like the dates are separate from the actual security rule, therefore we are simply providing a way for you to retrieve this date when assets are shipped?

@brokensound77 - Am I missing the point here or questions we discussed?

@jpdjere
Copy link

jpdjere commented Jan 26, 2024

The milliseconds and Z I can add these. Note that our updated_date in the rule metadata is not ISO-8601 formatted, so no time is captured and it will remain as 00 for these.

That's OK, the information about year, month and day is enough for us. So dates that look like 2019-11-14T00:00:00.000Z are OK.

Since we are adding elastic_update_date to the attributes, is this now a required rule field that was added to the rule schema upstream?

We will be adding the elastic_update_date as an optional field within the optional prebuilt field for our rule schema. This will be part of the Prebuilt Rule Customization Epic - Milestone 3 we discussed in yesterday's meeting.

Do we know if a rule is exported this field is exported as well in the rule?

Yes, it will, as part of the prebuilt object field.

From your image, it looks like the dates are separate from the actual security rule, therefore we are simply providing a way for you to retrieve this date when assets are shipped?

Those dates you see in the image above not the update_at and created_at dates from the metadata in the detection-rules package. They are dates that are added to the Elasticsearch savedobjects when the Fleet API is called to install the security_detection_engine package with the prebuilt rules, and are always set to the current date when the API is called, which is not useful information for us.

That's why the elastic_update_date should be part of the rule attributes themselves.

@terrancedejesus
Copy link
Contributor Author

@jpdjere - Thank you for providing additional insights.

We will be adding the elastic_update_date as an optional field within the optional prebuilt field for our rule schema. This will be part of the Prebuilt Rule Customization Epic - Milestone 3 we discussed in yesterday's meeting.

With this being said, if it is part of the rule schema, required or not, it is a breaking change for us because of backporting. We will need to change our approach and add this to our rule schema, rather than dynamically populate and push into the rule asset.

@brokensound77 - With this field being optional, I think it would be best to be a build time field, determined from rule metadata that we can only build for the compatible semantic version of the stack the feature is being added to. Regarding backporting, this will cause ALL of our rules to receive version bumps, for each release package. We have done this before, so I can get started on our strategy to implement this. Before I do, any additional thoughts?

@banderror
Copy link

Hey @jpdjere @terrancedejesus @Mikaayenson 👋

So there were lots of comments in this thread, and I'd like to double-check that after all these comments we're on the same page. Let me try to reiterate on our agreements and please correct me or add anything.

We're going to add a new optional field elastic_update_date to security-rule assets we ship via the package. Here's an example of this field for the Linux Restricted Shell Breakout via Linux Binary(s) prebuilt rule:

The latest 111 version of this rule looks like this in the package:

{
  "type": "security-rule",
  "id": "52376a86-ee86-4967-97ae-1a05f55816f0",
  "attributes": {
    "rule_id": "52376a86-ee86-4967-97ae-1a05f55816f0",
    "name": "Linux Restricted Shell Breakout via Linux Binary(s)",
    "description": "Identifies the abuse of a Linux binary to break out of a restricted shell or environment by spawning an interactive system shell. The activity of spawning a shell from a binary is not common behavior for a user or system administrator, and may indicate an attempt to evade detection, increase capabilities or enhance the stability of an adversary.",
    "type": "eql",
    "language": "eql",
    "index": ["logs-endpoint.events.*"],
    // other rule fields...
    "version": 111
  }
}

The next 112 version should look like that:

{
  "type": "security-rule",
  "id": "52376a86-ee86-4967-97ae-1a05f55816f0",
  "attributes": {
    "rule_id": "52376a86-ee86-4967-97ae-1a05f55816f0",
    "name": "Linux Restricted Shell Breakout via Linux Binary(s)",
    "description": "Identifies the abuse of a Linux binary to break out of a restricted shell or environment by spawning an interactive system shell. The activity of spawning a shell from a binary is not common behavior for a user or system administrator, and may indicate an attempt to evade detection, increase capabilities or enhance the stability of an adversary.",
    "type": "eql",
    "language": "eql",
    "index": ["logs-endpoint.events.*"],
    // other rule fields...
    "version": 112,
    "elastic_update_date": "2024-01-29T00:00:00.000Z"
  }
}

This field will be optional in our rule asset schema in Kibana. The field should be specified for all latest versions of all rules in the next version of the package for Kibana 8.13. The field can be omitted for all existing historical (previous) versions of rules as of today, but should be specified for all historical rule versions created after today in the future. For example, for the Linux Restricted Shell Breakout via Linux Binary(s) rule above, all rule versions >= 112 should include the elastic_update_date field.

The field's value must be formatted in the standard ISO format. Time of the day is not required and can be set to T00:00:00.000Z. We don't have strong requirements for the accuracy of the date itself. It can be the date of file modification by a rule author, the date of PR merge, or the date of building the package. The only requirement is that the values must be monotonically increasing and give a rough understanding to the user when Elastic shipped an update to the rule. +/- a few days would be sufficient accuracy for us.

The new field must not be backported to any packages compatible with Kibana 8.11.x and below. It can be backported to packages that are only compatible with Kibana 8.12.0 and above because starting from 8.12.0 we have forward compatibility of the rule asset schema in Kibana: https:/elastic/security-team/issues/6888. This means that in 8.12.x Kibana versions the elastic_update_date, if specified in the package, will be ignored/omitted until we add support for it. In Kibana versions 8.11.x and below the elastic_update_date, if specified in the package, will lead to an error during prebuilt rule installation or upgrade.

@terrancedejesus terrancedejesus changed the title [FR] Add creation_date and updated_date to rule objects in release packages [FR] Add elastic_last_update to Rule Schema as a Build Time Field Feb 5, 2024
@banderror
Copy link

Hey @terrancedejesus @Mikaayenson, last ask from our side: let's please change the name of the field to source_updated_at to make it a little bit more future-proof.

After chatting with @jpdjere we figured we want the name to be resilient to hypothetical future capabilities in Kibana, such as user- or community-created packages with security-rule assets distributed via private/user EPRs or the centralized EPR of Elastic if we ever have support for community-created content.

@terrancedejesus
Copy link
Contributor Author

terrancedejesus commented Feb 21, 2024

Alright so I did a bit of digging.

  • As it stands, the updates here should add the field as required and how we typically add build time fields with required stack versions
  • Checking out various branches and applying the diff to rule.py and definitions.py, the rules build successfully where if >=8.12 branch, the field is added accordingly, but if any lower the field is not added
  • We have a unit test that will fail as it is checking if a build time field attribute is inherited for a rule and if it does but the min stack defined in the rule is not compatible, will error. While this makes sense, we solve this here and in the field definition for BaseRuleData. So dynamically the optional field will only generate on the respective compatible branches when packages are built.

This gets me to the real problem and that is how we backport and version lock. As I attempt to showcase in the image below whenever we have a new field that is applied to all rules, optional or not, our versioning strategy does not do a good job of supporting this because the version is checked per backport branch where the SHA256 hashes are calculated. If these are different, then the version bumps +1. The important part to understand is that, in this example, in 8.11 a rule will not have elastic_update_date dynamically generated with version X. The version lock workflow will then checkout 8.12 and do the same workflow, but now the rule will have elastic_update_date and the SHA256 will change, bumping the rule version. The next time we lock versions it will bump twice as the state of the rule will always be different within (8.3-8.11) vs (8.12+).

version_lock_image

The only option at this time would be to min-stack ALL rules to 8.12 so any updates, tunings, new rules would only go back to 8.12 stacks which is out of sync with our current supported stacks current-3, therefore this is a breaking change as @brokensound77 has stated. While we have introduced breaking changes before regarding this, it seems like a lot of breaking for a timestamp we can supply in metadata when shipping the rule asset to avoid breaking our backporting and versioning.

@terrancedejesus
Copy link
Contributor Author

@Mikaayenson DED has an epic or meta somewhere for refactoring Detection Rules. May be worth exploring the schema for version lock file(s) in Detection Rules. I believe there is some resilience that can be added with a couple of options:

  • version lock per rule to track rule version and SHA256 PER stack version
  • adjust version lock file schema where stack versions are the sub-root of the UUID and everything nested in captures the state of the rule per stack version
  • etc.

Remember that when we build a package per stack version, we build it from that branch specifically so we could align that with its own state of the rules for that branch somehow.

 "4d4c35f4-414e-4d0c-bb7e-6db7c80a6957": {
    "8.12" : {
      "min_stack_version": "8.3",
      "rule_name": "Kernel Load or Unload via Kexec Detected",
      "sha256": "53f533ffdd9d2d9f7c1a5cba374de00d7db74d814cde9706d3750390086f3c78",
      "type": "eql",
      "version": 5
    },
    "8.11" : {
      "min_stack_version": "8.3",
      "rule_name": "Kernel Load or Unload via Kexec Detected",
      "sha256": "53f533ffdd9d2d9f7c1a5cba374de00d7db74d814cde9706d3750390086f3c78",
      "type": "eql",
      "version": 5
    },
    "8.10" : {
      "min_stack_version": "8.3",
      "rule_name": "Kernel Load or Unload via Kexec Detected",
      "sha256": "53f533ffdd9d2d9f7c1a5cba374de00d7db74d814cde9706d3750390086f3c78",
      "type": "eql",
      "version": 5
    },
    "8.9" : {
      "min_stack_version": "8.3",
      "rule_name": "Kernel Load or Unload via Kexec Detected",
      "sha256": "53f533ffdd9d2d9f7c1a5cba374de00d7db74d814cde9706d3750390086f3c78",
      "type": "eql",
      "version": 5
    },
    "8.8" : {
      "min_stack_version": "8.3",
      "rule_name": "Kernel Load or Unload via Kexec Detected",
      "sha256": "53f533ffdd9d2d9f7c1a5cba374de00d7db74d814cde9706d3750390086f3c78",
      "type": "eql",
      "version": 5
    },
    "8.7" : {
      "min_stack_version": "8.3",
      "rule_name": "Kernel Load or Unload via Kexec Detected",
      "sha256": "53f533ffdd9d2d9f7c1a5cba374de00d7db74d814cde9706d3750390086f3c78",
      "type": "eql",
      "version": 5
    },
    "8.6" : {
      "min_stack_version": "8.3",
      "rule_name": "Kernel Load or Unload via Kexec Detected",
      "sha256": "53f533ffdd9d2d9f7c1a5cba374de00d7db74d814cde9706d3750390086f3c78",
      "type": "eql",
      "version": 5

@Mikaayenson
Copy link
Contributor

@terrancedejesus I'm thinking about how to simplify this for you. Can we do this:

  1. Add the field to our schema just to support being able to import Kibana-exported ndjson that contains the new field
  2. DON'T create the field as a build time field. Just generate the field and date dynamically ONLY for publishing. In short call self._convert_add_elastic_last_update_date(obj) only for releasing.

@terrancedejesus terrancedejesus changed the title [FR] Add elastic_last_update to Rule Schema as a Build Time Field [FR] Add source_update_at to Rule Schema as a Build Time Field Feb 21, 2024
@terrancedejesus terrancedejesus changed the title [FR] Add source_update_at to Rule Schema as a Build Time Field [FR] Add source_updated_at to Rule Schema as a Build Time Field Feb 21, 2024
@terrancedejesus
Copy link
Contributor Author

terrancedejesus commented Feb 21, 2024

After discussion with @Mikaayenson...there were a couple options we wanted to explore to hopefully get this in on our end to not be a blocker for @banderror 's team.

The final proposal, as shown in the pull request, is to do the following and address each concern:

We need to align our schemas with upstream

  • Add the source_updated_at field to BaseRuleData to match the schema upstream but maintain typical rigor with schemas and our dataclasses
  • Whether we import or export a rule, source_updated_at is a valid optional rule field

We do not want rule authors duplicating the update date that already exists in rule metadata

  • Add it as a "build time field", meaning it will be generated into the final output of the rule to align with our typical strategy for build time fields that are deterministic
  • The source_updated_at field is pulled from the rule metadata and converted to the recommended ISO 8601 format

We need to consider versioning as this is likely a breaking change requiring min-stack updates to 8.12 across all rules

  • Build time fields all have a minimum stack version set that can be programmatically referenced throughout the code
  • When we calculate the SHA256 of rule contents, metadata is excluded and the rule contents are in JSON format. Before calculation, we remove source_updated_at from the JSON
  • In turn, when we run the version lock and it builds each rule, checks version and compares the SHA256 will never take source_updated_at into consideration

We need to remain consistent across our code and not introduce anything new that has to be managed

  • We added a regex pattern in definitions.py that ensures the source_updated_at string is the correct ISO 8601 format
  • We added a SKIP_FIELDS_FOR_SHA256 variable to definitions.py that equates to an array of strings. These strings are then used to remove their respective key:value pairs from the hashed rule content before calculating - moving forward we can use this for other fields

NOTE I want to emphasize that we should not always revert to adding new build time fields here. For instance, related_integrations and required_fields have implications upstream that we cannot control and need to include these in versioning for potential breaking changes. Thus while it is an option, it does not suggest it is a go to solution moving forward.

We need to ensure unit tests exist or are adjusted to accommodate our changes

  • We actually removed TestBuildTimeFields unit test class
  • @Mikaayenson and I walked through the logic here and noticed a few things
    • Minimum stack of the rule is used to logically determine if a build time field is compatible with a rule - this logic is incorrect as we should only reference the current branch stack version
    • When _post_dict_conversion() to called while loading and building a rule, we have steps in here to call each respective method for the build time fields to add them into the rule asset (dictionary). In these methods we call check_restricted_field_versions() which checks if the build time field compatible stack version is compatible with the current stack version referenced in packages.yml. If compatible, proceed to add the build time field, if not don't add.
    • Reminder that source_updated_at is not taken into consideration for SHA256 calculations with suggested changes, thus versions will not change, added or not added, across branches

@banderror
Copy link

@terrancedejesus @Mikaayenson Copying this from slack:

We haven’t worked on adding support for source_updated_at on our side yet. Moreover, I guess it’s still unclear if we want to have a top-level field source_updated_at or an object with a field source.updated_at to make it future-proof (potentially, for DaC). @jpdjere should include a proposal for this field’s schema into the RFC, the goal is to complete it by the end of this week.

I guess it's not a big difference so this shouldn't block you from working on some implementation of this field, but please hold off merging anything until we approve the proposal on our side and get an approval from your side.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backlog enhancement New feature or request v8.10
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants