Skip to content

Conversation

@dsherret
Copy link
Member

@dsherret dsherret commented Nov 7, 2025

Allow flags that are more specific than deny flags should take precedence.

Some examples:

  • --allow-read=./data/sub --deny-read=./data
  • --allow-env=NODE_ENV --deny-read=NODE_*

Extracted out of #31187 -- This behaviour will be more important when the --ingore-env flag is added in order to make the feature more powerful.

Copilot AI review requested due to automatic review settings November 7, 2025 22:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the permission system to improve handling of granular allow and deny rules. The main change replaces separate HashSet-based storage for granted, flag-denied, and prompt-denied descriptors with a unified sorted vector-based approach, enabling more precise permission resolution with partial denials.

  • Changed data structure from HashSet-based to sorted Vec-based descriptor storage
  • Added DeniedPartial state to distinguish between full and partial denials
  • Implemented comparison traits for descriptor ordering to support binary search

Reviewed Changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
runtime/permissions/lib.rs Core refactoring: unified descriptor storage, added sorting/comparison logic, implemented partial denial handling
runtime/permissions/Cargo.toml Added test features for sys_traits dependency
runtime/ops/permissions.rs Updated PermissionStatus to use static string for state and handle DeniedPartial
libs/config/deno_json/permissions.rs Added helper method and simplified empty check logic
cli/args/mod.rs Renamed variable from no_op to identity for clarity
Cargo.toml Updated fqdn dependency from 0.3.4 to 0.4.6
Cargo.lock Updated fqdn package checksum and version

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

state.to_string()
state: match state {
PermissionState::Granted | PermissionState::GrantedPartial => "granted",
PermissionState::DeniedPartial | PermissionState::Denied => "denied",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This DeniedPartial isn't used at the moment, but it's useful granularity to have. I'll use it in a future PR that I extract out of #31187

dsherret and others added 2 commits November 7, 2025 23:57
Co-authored-by: Copilot <[email protected]>
Signed-off-by: David Sherret <[email protected]>

impl PermissionsObject {
/// Returns true if the permissions object is empty (no permissions are set).
pub fn is_empty(&self) -> bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably asked this in the past - but aren't we calling this in a hot path? Maybe this value should be memoized?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is only used in a few select places (I think one place). If it were in runtime/permissions then yeah it could be in a hot path.

PermissionState::GrantedPartial => f.pad("granted-partial"),
PermissionState::Prompt => f.pad("prompt"),
PermissionState::Denied => f.pad("denied"),
PermissionState::Denied | PermissionState::DeniedPartial => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not discriminate denied-partial?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems this is not used so I will remove it.

Comment on lines +804 to +806
UnaryPermissionDesc::FlagDenied(_) => 0,
UnaryPermissionDesc::PromptDenied(_) => 1,
UnaryPermissionDesc::Granted(_) => 2,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does "flag denied" have a higher precedence than "prompt denied"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One needed to be higher than the other and flag seems a little more higher precedence than prompt.

Co-authored-by: Bartek Iwańczuk <[email protected]>
Signed-off-by: David Sherret <[email protected]>
Comment on lines +5822 to +5834
{
let perms = Permissions::from_options(
&parser,
&PermissionsOptions {
allow_read: Some(svec!["/foo/specific"]),
deny_read: Some(svec!["/foo"]),
allow_write: Some(svec!["/foo/specific"]),
deny_write: Some(svec!["/foo"]),
allow_ffi: Some(svec!["/foo/specific"]),
deny_ffi: Some(svec!["/foo"]),
..Default::default()
},
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add another test case, that flips the values (ie. allow_read: Some(svec!["/foo"]), deny_read: Some(svec!["/foo/specific"])) - just to be extra sure that reading /foo/bar is allowed but /foo/specific is not

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 8185f89

Comment on lines +6399 to +6410
assert_eq!(
perms.env.query(Some("PREFIX_TEST")),
PermissionState::Denied
);
assert_eq!(
perms.env.query(Some("PREFIX_ALLOWED_TEST")),
PermissionState::Granted
);
assert_eq!(
perms.env.query(Some("PREFIX_EXPLICIT_ALLOWED")),
PermissionState::Granted
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@dsherret dsherret enabled auto-merge (squash) November 12, 2025 20:39
@dsherret dsherret merged commit a664ebe into denoland:main Nov 12, 2025
19 checks passed
@dsherret dsherret deleted the improved_handling_allow_deny branch November 12, 2025 22:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants